Merge "[aapt2] Support ConfigVarying in CTS resources"
diff --git a/Android.bp b/Android.bp
index 0c1095d..e1cb037 100644
--- a/Android.bp
+++ b/Android.bp
@@ -297,6 +297,7 @@
     srcs: [
         ":framework-non-updatable-sources",
         "core/java/**/*.logtags",
+        ":apex-info-list",
     ],
     aidl: {
         generate_get_transaction_name: true,
diff --git a/apct-tests/perftests/autofill/AndroidManifest.xml b/apct-tests/perftests/autofill/AndroidManifest.xml
index 57595a2..de2a3f2 100644
--- a/apct-tests/perftests/autofill/AndroidManifest.xml
+++ b/apct-tests/perftests/autofill/AndroidManifest.xml
@@ -16,6 +16,16 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.perftests.autofill">
 
+    <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+    <uses-permission android:name="android.permission.DEVICE_POWER" />
+    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
     <application>
         <uses-library android:name="android.test.runner" />
         <activity android:name="android.perftests.utils.PerfTestActivity"
diff --git a/core/api/current.txt b/core/api/current.txt
index 454567d..f989d06 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -100,7 +100,7 @@
     field public static final String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
     field public static final String INTERNET = "android.permission.INTERNET";
     field public static final String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
-    field public static final String LAUNCH_TWO_PANE_SETTINGS_DEEP_LINK = "android.permission.LAUNCH_TWO_PANE_SETTINGS_DEEP_LINK";
+    field public static final String LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK = "android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK";
     field public static final String LOADER_USAGE_STATS = "android.permission.LOADER_USAGE_STATS";
     field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
@@ -169,6 +169,7 @@
     field public static final String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
     field @Deprecated public static final String SMS_FINANCIAL_TRANSACTIONS = "android.permission.SMS_FINANCIAL_TRANSACTIONS";
     field public static final String START_FOREGROUND_SERVICES_FROM_BACKGROUND = "android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND";
+    field public static final String START_VIEW_APP_FEATURES = "android.permission.START_VIEW_APP_FEATURES";
     field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE";
     field public static final String STATUS_BAR = "android.permission.STATUS_BAR";
     field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
@@ -2040,6 +2041,10 @@
     field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
     field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
     field public static final int accessibilityActionShowTooltip = 16908356; // 0x1020044
+    field public static final int accessibilityActionSwipeDown;
+    field public static final int accessibilityActionSwipeLeft;
+    field public static final int accessibilityActionSwipeRight;
+    field public static final int accessibilityActionSwipeUp;
     field public static final int accessibilitySystemActionBack = 16908363; // 0x102004b
     field public static final int accessibilitySystemActionHome = 16908364; // 0x102004c
     field public static final int accessibilitySystemActionLockScreen = 16908370; // 0x1020052
@@ -6774,7 +6779,7 @@
   public final class UiAutomation {
     method public void adoptShellPermissionIdentity();
     method public void adoptShellPermissionIdentity(@Nullable java.lang.String...);
-    method public void clearWindowAnimationFrameStats();
+    method @Deprecated public void clearWindowAnimationFrameStats();
     method public boolean clearWindowContentFrameStats(int);
     method public void dropShellPermissionIdentity();
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
@@ -6783,7 +6788,7 @@
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
-    method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
+    method @Deprecated public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
     method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
     method @NonNull public android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
@@ -13459,7 +13464,7 @@
     method public float getFloat(@DimenRes int);
     method @NonNull public android.graphics.Typeface getFont(@FontRes int) throws android.content.res.Resources.NotFoundException;
     method public float getFraction(@FractionRes int, int, int);
-    method public int getIdentifier(String, String, String);
+    method @Discouraged(message="Use of this function is discouraged because resource reflection makes it harder to perform build optimizations and compile-time verification of code. It is much more efficient to retrieve resources by identifier (e.g. `R.foo.bar`) than by name (e.g. `getIdentifier(\"bar\", \"foo\", null)`).") public int getIdentifier(String, String, String);
     method @NonNull public int[] getIntArray(@ArrayRes int) throws android.content.res.Resources.NotFoundException;
     method public int getInteger(@IntegerRes int) throws android.content.res.Resources.NotFoundException;
     method @NonNull public android.content.res.XmlResourceParser getLayout(@LayoutRes int) throws android.content.res.Resources.NotFoundException;
@@ -13479,7 +13484,7 @@
     method public CharSequence getText(@StringRes int, CharSequence);
     method @NonNull public CharSequence[] getTextArray(@ArrayRes int) throws android.content.res.Resources.NotFoundException;
     method public void getValue(@AnyRes int, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
-    method public void getValue(String, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
+    method @Discouraged(message="Use of this function is discouraged because it makes internal calls to `getIdentifier()`, which uses resource reflection. Reflection makes it harder to perform build optimizations and compile-time verification of code. It is much more efficient to retrieve resource values by identifier (e.g. `getValue(R.foo.bar, outValue, true)`) than by name (e.g. `getValue(\"foo\", outvalue, true)`).") public void getValue(String, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
     method public void getValueForDensity(@AnyRes int, int, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
     method @NonNull public android.content.res.XmlResourceParser getXml(@XmlRes int) throws android.content.res.Resources.NotFoundException;
     method public final android.content.res.Resources.Theme newTheme();
@@ -18016,6 +18021,8 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> DISTORTION_CORRECTION_AVAILABLE_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> FLASH_INFO_STRENGTH_DEFAULT_LEVEL;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> FLASH_INFO_STRENGTH_MAXIMUM_LEVEL;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.DeviceStateSensorOrientationMap> INFO_DEVICE_STATE_SENSOR_ORIENTATION_MAP;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL;
@@ -35366,7 +35373,7 @@
     field public static final String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
     field public static final String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
     field public static final String ACTION_SETTINGS = "android.settings.SETTINGS";
-    field public static final String ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK = "android.settings.SETTINGS_LARGE_SCREEN_DEEP_LINK";
+    field public static final String ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY = "android.settings.SETTINGS_EMBED_DEEP_LINK_ACTIVITY";
     field public static final String ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO";
     field public static final String ACTION_SHOW_WORK_POLICY_INFO = "android.settings.SHOW_WORK_POLICY_INFO";
     field public static final String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS";
@@ -35407,8 +35414,8 @@
     field public static final String EXTRA_EASY_CONNECT_ERROR_CODE = "android.provider.extra.EASY_CONNECT_ERROR_CODE";
     field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
     field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
-    field public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI = "android.provider.extra.SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI";
-    field public static final String EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY = "android.provider.extra.SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY";
+    field public static final String EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY = "android.provider.extra.SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY";
+    field public static final String EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI = "android.provider.extra.SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI";
     field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
     field public static final String EXTRA_WIFI_NETWORK_LIST = "android.provider.extra.WIFI_NETWORK_LIST";
     field public static final String EXTRA_WIFI_NETWORK_RESULT_LIST = "android.provider.extra.WIFI_NETWORK_RESULT_LIST";
@@ -51185,6 +51192,10 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_ON_SCREEN;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_TOOLTIP;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SWIPE_DOWN;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SWIPE_LEFT;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SWIPE_RIGHT;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SWIPE_UP;
     field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> CREATOR;
   }
 
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index a480cd9..cc30db3 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -333,7 +333,7 @@
 package android.provider {
 
   public static final class ContactsContract.RawContactsEntity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns {
-    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static java.util.Map<java.lang.String,java.util.List<android.content.ContentValues>> queryRawContactEntity(@NonNull android.content.Context, long);
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static java.util.Map<java.lang.String,java.util.List<android.content.ContentValues>> queryRawContactEntity(@NonNull android.content.ContentResolver, long);
   }
 
   public final class DeviceConfig {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1bcbaa2..599f11d 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -26,7 +26,7 @@
     field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
     field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
     field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
-    field public static final String ALLOW_PLACE_IN_TWO_PANE_SETTINGS = "android.permission.ALLOW_PLACE_IN_TWO_PANE_SETTINGS";
+    field public static final String ALLOW_PLACE_IN_MULTI_PANE_SETTINGS = "android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS";
     field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
     field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES";
@@ -242,6 +242,7 @@
     field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
+    field public static final String REQUEST_COMPANION_PROFILE_APP_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING";
     field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
     field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -439,6 +440,11 @@
     method public void onUidImportance(int, int);
   }
 
+  public class ActivityOptions {
+    method public int getLaunchTaskId();
+    method @RequiresPermission("android.permission.START_TASKS_FROM_RECENTS") public void setLaunchTaskId(int);
+  }
+
   public class AlarmManager {
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource);
@@ -2292,6 +2298,10 @@
 
 package android.companion {
 
+  public final class AssociationRequest implements android.os.Parcelable {
+    field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING) public static final String DEVICE_PROFILE_APP_STREAMING = "android.app.role.COMPANION_DEVICE_APP_STREAMING";
+  }
+
   public final class CompanionDeviceManager {
     method @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public void associate(@NonNull String, @NonNull android.net.MacAddress, @NonNull byte[]);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
@@ -2450,6 +2460,7 @@
     field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
     field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
+    field @RequiresPermission(android.Manifest.permission.START_VIEW_APP_FEATURES) public static final String ACTION_VIEW_APP_FEATURES = "android.intent.action.VIEW_APP_FEATURES";
     field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
     field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
     field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -3357,6 +3368,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerControlMode();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerStateChangeOnActiveSourceLost();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getRoutingControl();
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSadPresenceInQuery(@NonNull String);
     method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioControl();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioModeMuting();
@@ -3375,6 +3387,8 @@
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerControlMode(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerStateChangeOnActiveSourceLost(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setRoutingControl(@NonNull int);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSadPresenceInQuery(@NonNull String, int);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSadsPresenceInQuery(@NonNull java.util.List<java.lang.String>, int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioControl(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioModeMuting(@NonNull int);
@@ -3386,6 +3400,21 @@
     field public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
     field public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "power_control_mode";
     field public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST = "power_state_change_on_active_source_lost";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_AAC = "query_sad_aac";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_ATRAC = "query_sad_atrac";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_DD = "query_sad_dd";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_DDP = "query_sad_ddp";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_DST = "query_sad_dst";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_DTS = "query_sad_dts";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_DTSHD = "query_sad_dtshd";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_LPCM = "query_sad_lpcm";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_MAX = "query_sad_max";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_MP3 = "query_sad_mp3";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_MPEG1 = "query_sad_mpeg1";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_MPEG2 = "query_sad_mpeg2";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO = "query_sad_onebitaudio";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_TRUEHD = "query_sad_truehd";
+    field public static final String CEC_SETTING_NAME_QUERY_SAD_WMAPRO = "query_sad_wmapro";
     field public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
     field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL = "system_audio_control";
     field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting";
@@ -3453,6 +3482,8 @@
     field public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; // 0x2
     field public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; // 0x3
     field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff
+    field public static final int QUERY_SAD_DISABLED = 0; // 0x0
+    field public static final int QUERY_SAD_ENABLED = 1; // 0x1
     field @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4; // 0x4
     field public static final int RESULT_COMMUNICATION_FAILED = 7; // 0x7
     field public static final int RESULT_EXCEPTION = 5; // 0x5
@@ -9026,6 +9057,7 @@
   public final class PermissionManager {
     method public int checkDeviceIdentifierAccess(@Nullable String, @Nullable String, @Nullable String, int, int);
     method public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
+    method public int checkPermissionForDataDeliveryFromDataSource(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
     method public int checkPermissionForPreflight(@NonNull String, @NonNull android.content.AttributionSource);
     method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionGrantedPackages();
     method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionRequestedPackages();
@@ -9243,6 +9275,7 @@
     field public static final String NAMESPACE_MEDIA = "media";
     field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
     field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
+    field public static final String NAMESPACE_NNAPI_NATIVE = "nnapi_native";
     field public static final String NAMESPACE_OTA = "ota";
     field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
     field public static final String NAMESPACE_PERMISSIONS = "permissions";
@@ -9266,6 +9299,7 @@
     field public static final String NAMESPACE_SYSTEMUI = "systemui";
     field public static final String NAMESPACE_SYSTEM_TIME = "system_time";
     field public static final String NAMESPACE_TELEPHONY = "telephony";
+    field public static final String NAMESPACE_TETHERING = "tethering";
     field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
     field public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 372ad9d..e707b03 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -146,7 +146,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
     method public static void setExitTransitionTimeout(long);
     method public void setLaunchActivityType(int);
-    method public void setLaunchTaskId(int);
     method public void setLaunchWindowingMode(int);
     method public void setLaunchedFromBubble(boolean);
     method public void setTaskAlwaysOnTop(boolean);
diff --git a/core/java/android/accounts/ChooseAccountTypeActivity.java b/core/java/android/accounts/ChooseAccountTypeActivity.java
index 63e005f..983dcd8 100644
--- a/core/java/android/accounts/ChooseAccountTypeActivity.java
+++ b/core/java/android/accounts/ChooseAccountTypeActivity.java
@@ -138,7 +138,6 @@
                 if (sequence != null) {
                     name = sequence.toString();
                 }
-                name = sequence.toString();
             } catch (PackageManager.NameNotFoundException e) {
                 // Nothing we can do much here, just log
                 if (Log.isLoggable(TAG, Log.WARN)) {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index c1b94787..a5facd9 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1529,7 +1529,8 @@
      * Sets the task the activity will be launched in.
      * @hide
      */
-    @TestApi
+    @RequiresPermission(START_TASKS_FROM_RECENTS)
+    @SystemApi
     public void setLaunchTaskId(int taskId) {
         mLaunchTaskId = taskId;
     }
@@ -1537,6 +1538,7 @@
     /**
      * @hide
      */
+    @SystemApi
     public int getLaunchTaskId() {
         return mLaunchTaskId;
     }
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 4ca3beb..5b8cc70 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -193,6 +193,26 @@
     }
 
     /**
+     * Set PendingIntent activity is allowed to be started in the background if the caller
+     * can start background activities.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
+        super.setPendingIntentBackgroundActivityLaunchAllowed(allowed);
+    }
+
+    /**
+     * Get PendingIntent activity is allowed to be started in the background if the caller
+     * can start background activities.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
+        return super.isPendingIntentBackgroundActivityLaunchAllowed();
+    }
+
+    /**
      * Return {@link #setTemporaryAppAllowlist}.
      * @hide
      */
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 95ba523..1312454 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -115,7 +115,6 @@
     ParceledListSlice getNotificationChannelGroups(String pkg);
     boolean onlyHasDefaultChannel(String pkg, int uid);
     boolean areChannelsBypassingDnd();
-    int getAppsBypassingDndCount(int uid);
     ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
     boolean isPackagePaused(String pkg);
     void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index a2c9795..74208c3a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -2134,4 +2134,38 @@
             final IBinder mService;
         }
     }
+
+    /**
+     * Check if the Apk paths in the cache are correct, and update them if they are not.
+     * @hide
+     */
+    public static void checkAndUpdateApkPaths(ApplicationInfo expectedAppInfo) {
+        // Get the LoadedApk from the cache
+        ActivityThread activityThread = ActivityThread.currentActivityThread();
+        if (activityThread == null) {
+            Log.e(TAG, "Cannot find activity thread");
+            return;
+        }
+        checkAndUpdateApkPaths(activityThread, expectedAppInfo, /* cacheWithCode */ true);
+        checkAndUpdateApkPaths(activityThread, expectedAppInfo, /* cacheWithCode */ false);
+    }
+
+    private static void checkAndUpdateApkPaths(ActivityThread activityThread,
+            ApplicationInfo expectedAppInfo, boolean cacheWithCode) {
+        String expectedCodePath = expectedAppInfo.getCodePath();
+        LoadedApk loadedApk = activityThread.peekPackageInfo(
+                expectedAppInfo.packageName, /* includeCode= */ cacheWithCode);
+        // If there is load apk cached, or if the cache is valid, don't do anything.
+        if (loadedApk == null || loadedApk.getApplicationInfo() == null
+                || loadedApk.getApplicationInfo().getCodePath().equals(expectedCodePath)) {
+            return;
+        }
+        // Duplicate framework logic
+        List<String> oldPaths = new ArrayList<>();
+        LoadedApk.makePaths(activityThread, expectedAppInfo, oldPaths);
+
+        // Force update the LoadedApk instance, which should update the reference in the cache
+        loadedApk.updateApplicationInfo(expectedAppInfo, oldPaths);
+    }
+
 }
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e0e9b62..4da51c1 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -28,6 +28,7 @@
 per-file Service* = file:/services/core/java/com/android/server/am/OWNERS
 per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS
 per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS
+per-file UiAutomation.java = file:/services/accessibility/OWNERS
 
 # ActivityThread
 per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index e0b484c..65f71d0 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1174,7 +1174,9 @@
      * @see android.view.WindowAnimationFrameStats
      * @see #getWindowAnimationFrameStats()
      * @see android.R.styleable#WindowAnimation
+     * @deprecated animation-frames are no-longer used.
      */
+    @Deprecated
     public void clearWindowAnimationFrameStats() {
         try {
             if (DEBUG) {
@@ -1213,7 +1215,9 @@
      * @see android.view.WindowAnimationFrameStats
      * @see #clearWindowAnimationFrameStats()
      * @see android.R.styleable#WindowAnimation
+     * @deprecated animation-frames are no-longer used.
      */
+    @Deprecated
     public WindowAnimationFrameStats getWindowAnimationFrameStats() {
         try {
             if (DEBUG) {
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index dd147cc..eb10f09 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityOptions;
+import android.app.LoadedApk;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -554,7 +555,7 @@
             }
             // Prepare a local reference to the remote Context so we're ready to
             // inflate any requested LayoutParams.
-            mRemoteContext = getRemoteContext();
+            mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath();
 
             int layoutId = rvToApply.getLayoutId();
             if (rvToApply.canRecycleView(mView)) {
@@ -616,7 +617,7 @@
     private void inflateAsync(@NonNull RemoteViews remoteViews) {
         // Prepare a local reference to the remote Context so we're ready to
         // inflate any requested LayoutParams.
-        mRemoteContext = getRemoteContext();
+        mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath();
         int layoutId = remoteViews.getLayoutId();
 
         if (mLastExecutionSignal != null) {
@@ -718,8 +719,10 @@
      * purposes of reading remote resources.
      * @hide
      */
-    protected Context getRemoteContext() {
+    protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
         try {
+            ApplicationInfo expectedAppInfo = mInfo.providerInfo.applicationInfo;
+            LoadedApk.checkAndUpdateApkPaths(expectedAppInfo);
             // Return if cloned successfully, otherwise default
             Context newContext = mContext.createApplicationContext(
                     mInfo.providerInfo.applicationInfo,
@@ -765,7 +768,7 @@
 
         try {
             if (mInfo != null) {
-                Context theirContext = getRemoteContext();
+                Context theirContext = getRemoteContextEnsuringCorrectCachedApkPath();
                 mRemoteContext = theirContext;
                 LayoutInflater inflater = (LayoutInflater)
                         theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index b060ce2..7cea33d 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -15,219 +15,223 @@
  */
 package android.companion;
 
+import static android.companion.DeviceId.TYPE_MAC_ADDRESS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.DataClass;
-
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * A record indicating that a device with a given address was confirmed by the user to be
  * associated to a given companion app
  *
  * @hide
+ * TODO(b/1979395): un-hide and rename to AssociationInfo when implementing public APIs that use
+ *                  this class.
  */
-@DataClass(genEqualsHashCode = true, genToString = true, genHiddenConstructor = true)
 public final class Association implements Parcelable {
+    /**
+     * A unique ID of this Association record.
+     * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
+     * {@code disassociate()} API call).
+     */
+    private final int mAssociationId;
 
     private final @UserIdInt int mUserId;
-    private final @NonNull String mDeviceMacAddress;
     private final @NonNull String mPackageName;
+
+    private final @NonNull List<DeviceId> mDeviceIds;
     private final @Nullable String mDeviceProfile;
-    private final boolean mNotifyOnDeviceNearby;
+
+    private final boolean mManagedByCompanionApp;
+    private boolean mNotifyOnDeviceNearby;
     private final long mTimeApprovedMs;
 
+    /**
+     * Creates a new Association.
+     * Only to be used by the CompanionDeviceManagerService.
+     *
+     * @hide
+     */
+    public Association(int associationId, @UserIdInt int userId, @NonNull String packageName,
+            @NonNull List<DeviceId> deviceIds, @Nullable String deviceProfile,
+            boolean managedByCompanionApp, boolean notifyOnDeviceNearby, long timeApprovedMs) {
+        if (associationId <= 0) {
+            throw new IllegalArgumentException("Association ID should be greater than 0");
+        }
+        validateDeviceIds(deviceIds);
+
+        mAssociationId = associationId;
+
+        mUserId = userId;
+        mPackageName = packageName;
+
+        mDeviceProfile = deviceProfile;
+        mDeviceIds = new ArrayList<>(deviceIds);
+
+        mManagedByCompanionApp = managedByCompanionApp;
+        mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+        mTimeApprovedMs = timeApprovedMs;
+    }
+
+    /**
+     * @return the unique ID of this association record.
+     */
+    public int getAssociationId() {
+        return mAssociationId;
+    }
+
     /** @hide */
     public int getUserId() {
         return mUserId;
     }
 
-    private String timeApprovedMsToString() {
-        return new Date(mTimeApprovedMs).toString();
-    }
-
-
-
-    // Code below generated by codegen v1.0.22.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/companion/Association.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    /**
-     * Creates a new Association.
-     *
-     * @hide
-     */
-    @DataClass.Generated.Member
-    public Association(
-            @UserIdInt int userId,
-            @NonNull String deviceMacAddress,
-            @NonNull String packageName,
-            @Nullable String deviceProfile,
-            boolean notifyOnDeviceNearby,
-            long timeApprovedMs) {
-        this.mUserId = userId;
-        com.android.internal.util.AnnotationValidations.validate(
-                UserIdInt.class, null, mUserId);
-        this.mDeviceMacAddress = deviceMacAddress;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mDeviceMacAddress);
-        this.mPackageName = packageName;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mPackageName);
-        this.mDeviceProfile = deviceProfile;
-        this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
-        this.mTimeApprovedMs = timeApprovedMs;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull String getDeviceMacAddress() {
-        return mDeviceMacAddress;
-    }
-
-    @DataClass.Generated.Member
+    /** @hide */
     public @NonNull String getPackageName() {
         return mPackageName;
     }
 
-    @DataClass.Generated.Member
+    /**
+     * @return list of the device's IDs. At any time a device has at least 1 ID.
+     */
+    public @NonNull List<DeviceId> getDeviceIds() {
+        return Collections.unmodifiableList(mDeviceIds);
+    }
+
+    /**
+     * @param type type of the ID.
+     * @return ID of the type if the device has such ID, {@code null} otherwise.
+     */
+    public @Nullable String getIdOfType(@NonNull String type) {
+        for (int i = mDeviceIds.size() - 1; i >= 0; i--) {
+            final DeviceId id = mDeviceIds.get(i);
+            if (Objects.equals(mDeviceIds.get(i).getType(), type)) return id.getValue();
+        }
+        return null;
+    }
+
+    /** @hide */
+    public @NonNull String getDeviceMacAddress() {
+        return Objects.requireNonNull(getIdOfType(TYPE_MAC_ADDRESS),
+                "MAC address of this device is not specified.");
+    }
+
+    /**
+     * @return the profile of the device.
+     */
     public @Nullable String getDeviceProfile() {
         return mDeviceProfile;
     }
 
-    @DataClass.Generated.Member
+    /** @hide */
+    public boolean isManagedByCompanionApp() {
+        return mManagedByCompanionApp;
+    }
+
+    /**
+     * Should only be used by the CdmService.
+     * @hide
+     */
+    public void setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
+        mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+    }
+
+    /** @hide */
     public boolean isNotifyOnDeviceNearby() {
         return mNotifyOnDeviceNearby;
     }
 
-    @DataClass.Generated.Member
+    /** @hide */
     public long getTimeApprovedMs() {
         return mTimeApprovedMs;
     }
 
-    @Override
-    @DataClass.Generated.Member
-    public String toString() {
-        // You can override field toString logic by defining methods like:
-        // String fieldNameToString() { ... }
-
-        return "Association { " +
-                "userId = " + mUserId + ", " +
-                "deviceMacAddress = " + mDeviceMacAddress + ", " +
-                "packageName = " + mPackageName + ", " +
-                "deviceProfile = " + mDeviceProfile + ", " +
-                "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + ", " +
-                "timeApprovedMs = " + timeApprovedMsToString() +
-        " }";
+    /** @hide */
+    public boolean belongsToPackage(@UserIdInt int userId, String packageName) {
+        return mUserId == userId && Objects.equals(mPackageName, packageName);
     }
 
     @Override
-    @DataClass.Generated.Member
-    public boolean equals(@Nullable Object o) {
-        // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(Association other) { ... }
-        // boolean fieldNameEquals(FieldType otherValue) { ... }
+    public String toString() {
+        return "Association{"
+                + "mAssociationId=" + mAssociationId
+                + ", mUserId=" + mUserId
+                + ", mPackageName='" + mPackageName + '\''
+                + ", mDeviceIds=" + mDeviceIds
+                + ", mDeviceProfile='" + mDeviceProfile + '\''
+                + ", mManagedByCompanionApp=" + mManagedByCompanionApp
+                + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
+                + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
+                + '}';
+    }
 
+    @Override
+    public boolean equals(Object o) {
         if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        @SuppressWarnings("unchecked")
-        Association that = (Association) o;
-        //noinspection PointlessBooleanExpression
-        return true
+        if (!(o instanceof Association)) return false;
+        final Association that = (Association) o;
+        return mAssociationId == that.mAssociationId
                 && mUserId == that.mUserId
-                && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
+                && mManagedByCompanionApp == that.mManagedByCompanionApp
+                && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
+                && mTimeApprovedMs == that.mTimeApprovedMs
                 && Objects.equals(mPackageName, that.mPackageName)
                 && Objects.equals(mDeviceProfile, that.mDeviceProfile)
-                && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
-                && mTimeApprovedMs == that.mTimeApprovedMs;
+                && Objects.equals(mDeviceIds, that.mDeviceIds);
     }
 
     @Override
-    @DataClass.Generated.Member
     public int hashCode() {
-        // You can override field hashCode logic by defining methods like:
-        // int fieldNameHashCode() { ... }
-
-        int _hash = 1;
-        _hash = 31 * _hash + mUserId;
-        _hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
-        _hash = 31 * _hash + Objects.hashCode(mPackageName);
-        _hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
-        _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby);
-        _hash = 31 * _hash + Long.hashCode(mTimeApprovedMs);
-        return _hash;
+        return Objects.hash(mAssociationId, mUserId, mPackageName, mDeviceIds, mDeviceProfile,
+                mManagedByCompanionApp, mNotifyOnDeviceNearby, mTimeApprovedMs);
     }
 
     @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
+    public int describeContents() {
+        return 0;
+    }
 
-        byte flg = 0;
-        if (mNotifyOnDeviceNearby) flg |= 0x10;
-        if (mDeviceProfile != null) flg |= 0x8;
-        dest.writeByte(flg);
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mAssociationId);
+
         dest.writeInt(mUserId);
-        dest.writeString(mDeviceMacAddress);
         dest.writeString(mPackageName);
-        if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
+
+        dest.writeParcelableList(mDeviceIds, 0);
+        dest.writeString(mDeviceProfile);
+
+        dest.writeBoolean(mManagedByCompanionApp);
+        dest.writeBoolean(mNotifyOnDeviceNearby);
         dest.writeLong(mTimeApprovedMs);
     }
 
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
+    private Association(@NonNull Parcel in) {
+        mAssociationId = in.readInt();
 
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    /* package-private */ Association(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
+        mUserId = in.readInt();
+        mPackageName = in.readString();
 
-        byte flg = in.readByte();
-        boolean notifyOnDeviceNearby = (flg & 0x10) != 0;
-        int userId = in.readInt();
-        String deviceMacAddress = in.readString();
-        String packageName = in.readString();
-        String deviceProfile = (flg & 0x8) == 0 ? null : in.readString();
-        long timeApprovedMs = in.readLong();
+        mDeviceIds = in.readParcelableList(new ArrayList<>(), DeviceId.class.getClassLoader());
+        mDeviceProfile = in.readString();
 
-        this.mUserId = userId;
-        com.android.internal.util.AnnotationValidations.validate(
-                UserIdInt.class, null, mUserId);
-        this.mDeviceMacAddress = deviceMacAddress;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mDeviceMacAddress);
-        this.mPackageName = packageName;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mPackageName);
-        this.mDeviceProfile = deviceProfile;
-        this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
-        this.mTimeApprovedMs = timeApprovedMs;
-
-        // onConstructed(); // You can define this method to get a callback
+        mManagedByCompanionApp = in.readBoolean();
+        mNotifyOnDeviceNearby = in.readBoolean();
+        mTimeApprovedMs = in.readLong();
     }
 
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<Association> CREATOR
-            = new Parcelable.Creator<Association>() {
+    public static final Parcelable.Creator<Association> CREATOR =
+            new Parcelable.Creator<Association>() {
         @Override
         public Association[] newArray(int size) {
             return new Association[size];
@@ -239,16 +243,18 @@
         }
     };
 
-    @DataClass.Generated(
-            time = 1611795283642L,
-            codegenVersion = "1.0.22",
-            sourceFile = "frameworks/base/core/java/android/companion/Association.java",
-            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final  boolean mNotifyOnDeviceNearby\nprivate final  long mTimeApprovedMs\npublic  int getUserId()\nprivate  java.lang.String timeApprovedMsToString()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
-    @Deprecated
-    private void __metadata() {}
+    private static void validateDeviceIds(@NonNull List<DeviceId> ids) {
+        if (ids.isEmpty()) throw new IllegalArgumentException("Device must have at least 1 id.");
 
-
-    //@formatter:on
-    // End of generated code
-
+        // Make sure none of the IDs are null, and they all have different types.
+        final Set<String> types = new HashSet<>(ids.size());
+        for (int i = ids.size() - 1; i >= 0; i--) {
+            final DeviceId deviceId = ids.get(i);
+            if (deviceId == null) throw new IllegalArgumentException("DeviceId must not be null");
+            if (!types.add(deviceId.getType())) {
+                throw new IllegalArgumentException(
+                        "DeviceId cannot have multiple IDs of the same type");
+            }
+        }
+    }
 }
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index bb8fa9e..24d1248 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -18,9 +18,12 @@
 
 import static com.android.internal.util.CollectionUtils.emptyIfNull;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -52,7 +55,8 @@
         genHiddenGetters = true,
         genParcelable = true,
         genHiddenConstructor = true,
-        genBuilder = false)
+        genBuilder = false,
+        genConstDefs = false)
 public final class AssociationRequest implements Parcelable {
 
     private static final String LOG_TAG = AssociationRequest.class.getSimpleName();
@@ -74,6 +78,22 @@
      */
     public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH";
 
+    /**
+     * Device profile: a virtual display capable of rendering Android applications, and sending back
+     * input events.
+     *
+     * Only applications that have been granted
+     * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_APP_STREAMING} are allowed to
+     * request to be associated with such devices.
+     *
+     * @see AssociationRequest.Builder#setDeviceProfile
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING)
+    @SystemApi
+    public static final String DEVICE_PROFILE_APP_STREAMING =
+            "android.app.role.COMPANION_DEVICE_APP_STREAMING";
+
     /** @hide */
     @StringDef(value = { DEVICE_PROFILE_WATCH })
     public @interface DeviceProfile {}
@@ -229,7 +249,7 @@
 
 
 
-    // Code below generated by codegen v1.0.22.
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -478,10 +498,10 @@
     };
 
     @DataClass.Generated(
-            time = 1615252862756L,
-            codegenVersion = "1.0.22",
+            time = 1634716126923L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
-            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate  long mCreationTime\nprivate  boolean mSkipPrompt\nprivate  void onConstructed()\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic  void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
+            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate  long mCreationTime\nprivate  boolean mSkipPrompt\nprivate  void onConstructed()\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic  void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index 5e2340c..ef5f84c 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.le.ScanFilter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.wifi.ScanResult;
 import android.os.Build;
@@ -30,9 +29,13 @@
 import android.os.Parcelable;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
 import java.util.regex.Pattern;
 
 /** @hide */
@@ -73,12 +76,19 @@
 
     static boolean matchesServiceUuid(ParcelUuid serviceUuid, ParcelUuid serviceUuidMask,
             BluetoothDevice device) {
-        ParcelUuid[] uuids = device.getUuids();
-        final boolean result = serviceUuid == null ||
-                ScanFilter.matchesServiceUuids(
-                        serviceUuid,
-                        serviceUuidMask,
-                        uuids == null ? Collections.emptyList() : Arrays.asList(uuids));
+        boolean result = false;
+        List<ParcelUuid> deviceUuids = device.getUuids() == null
+                ? Collections.emptyList() : Arrays.asList(device.getUuids());
+        if (serviceUuid == null) {
+            result = true;
+        } else {
+            for (ParcelUuid parcelUuid : deviceUuids) {
+                UUID uuidMask = serviceUuidMask == null ? null : serviceUuidMask.getUuid();
+                if (uuidsMaskedEquals(parcelUuid.getUuid(), serviceUuid.getUuid(), uuidMask)) {
+                    result = true;
+                }
+            }
+        }
         if (DEBUG) debugLogMatchResult(result, device, serviceUuid);
         return result;
     }
@@ -143,4 +153,23 @@
             throw new IllegalArgumentException("Unknown device type: " + device);
         }
     }
+
+    /**
+     * Compares two {@link #UUID} with a {@link #UUID} mask.
+     *
+     * @param data first {@link #UUID}.
+     * @param uuid second {@link #UUID}.
+     * @param mask mask {@link #UUID}.
+     * @return true if both UUIDs are equals when masked, false otherwise.
+     */
+    @VisibleForTesting
+    public static boolean uuidsMaskedEquals(UUID data, UUID uuid, UUID mask) {
+        if (mask == null) {
+            return Objects.equals(data, uuid);
+        }
+        return (data.getLeastSignificantBits() & mask.getLeastSignificantBits())
+                == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits())
+                && (data.getMostSignificantBits() & mask.getMostSignificantBits())
+                == (uuid.getMostSignificantBits() & mask.getMostSignificantBits());
+    }
 }
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index 828d482..58898cc 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -75,7 +75,7 @@
             String renameSuffix, int renameBytesFrom, int renameBytesLength,
             int renameNameFrom, int renameNameLength, boolean renameBytesReverseOrder) {
         mNamePattern = namePattern;
-        mScanFilter = ObjectUtils.firstNotNull(scanFilter, ScanFilter.EMPTY);
+        mScanFilter = ObjectUtils.firstNotNull(scanFilter, new ScanFilter.Builder().build());
         mRawDataFilter = rawDataFilter;
         mRawDataFilterMask = rawDataFilterMask;
         mRenamePrefix = renamePrefix;
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 15de079..d42d6b4 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -281,7 +281,7 @@
      * Wi-Fi MAC address for a given user.
      *
      * <p>This is a system API protected by the
-     * {@link andrioid.Manifest.permission#MANAGE_COMPANION_DEVICES} permission, that’s currently
+     * {@link android.Manifest.permission#MANAGE_COMPANION_DEVICES} permission, that’s currently
      * called by the Android Wi-Fi stack to determine whether user consent is required to connect
      * to a Wi-Fi network. Devices that have been pre-registered as companion devices will not
      * require user consent to connect.</p>
diff --git a/core/java/android/companion/DeviceId.java b/core/java/android/companion/DeviceId.java
new file mode 100644
index 0000000..5deed1a
--- /dev/null
+++ b/core/java/android/companion/DeviceId.java
@@ -0,0 +1,127 @@
+/*
+ * 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 android.companion;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The class represents free-form ID of a companion device.
+ *
+ * Since companion devices may have multiple IDs of different type at the same time
+ * (eg. a MAC address and a Serial Number), this class not only stores the ID itself, it also stores
+ * the type of the ID.
+ * Both the type of the ID and its actual value are represented as {@link String}-s.
+ *
+ * Examples of device IDs:
+ *  - "mac_address: f0:18:98:b3:fd:2e"
+ *  - "ip_address: 128.121.35.200"
+ *  - "imei: 352932100034923 / 44"
+ *  - "serial_number: 96141FFAZ000B7"
+ *  - "meid_hex: 35293210003492"
+ *  - "meid_dic: 08918 92240 0001 3548"
+ *
+ * @hide
+ * TODO(b/1979395): un-hide when implementing public APIs that use this class.
+ */
+public final class DeviceId implements Parcelable {
+    public static final String TYPE_MAC_ADDRESS = "mac_address";
+
+    private final @NonNull String mType;
+    private final @NonNull String mValue;
+
+    /**
+     * @param type type of the ID. Non-empty. Max length - 16 characters.
+     * @param value the ID. Non-empty. Max length - 48 characters.
+     * @throws IllegalArgumentException if either {@param type} or {@param value} is empty or
+     *         exceeds its max allowed length.
+     */
+    public DeviceId(@NonNull String type, @NonNull String value) {
+        if (type.isEmpty() || value.isEmpty()) {
+            throw new IllegalArgumentException("'type' and 'value' should not be empty");
+        }
+        this.mType = type;
+        this.mValue = value;
+    }
+
+    /**
+     * @return the type of the ID.
+     */
+    public @NonNull String getType() {
+        return mType;
+    }
+
+    /**
+     * @return the ID.
+     */
+    public @NonNull String getValue() {
+        return mValue;
+    }
+
+    @Override
+    public String toString() {
+        return "DeviceId{"
+                + "type='" + mType + '\''
+                + ", value='" + mValue + '\''
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DeviceId)) return false;
+        DeviceId deviceId = (DeviceId) o;
+        return Objects.equals(mType, deviceId.mType) && Objects.equals(mValue,
+                deviceId.mValue);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mValue);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mType);
+        dest.writeString(mValue);
+    }
+
+    private DeviceId(@NonNull Parcel in) {
+        mType = in.readString();
+        mValue = in.readString();
+    }
+
+    public static final @NonNull Creator<DeviceId> CREATOR = new Creator<DeviceId>() {
+        @Override
+        public DeviceId createFromParcel(@NonNull Parcel in) {
+            return new DeviceId(in);
+        }
+
+        @Override
+        public DeviceId[] newArray(int size) {
+            return new DeviceId[size];
+        }
+    };
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index dc37882..cfd3417 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3336,7 +3336,11 @@
      * Service will call {@link android.app.Service#startForeground(int, android.app.Notification)
      * startForeground(int, android.app.Notification)} once it begins running.  The service is given
      * an amount of time comparable to the ANR interval to do this, otherwise the system
-     * will automatically stop the service and declare the app ANR.
+     * will automatically crash the process, in which case an internal exception
+     * {@code ForegroundServiceDidNotStartInTimeException} is logged on logcat on devices
+     * running SDK Version {@link android.os.Build.VERSION_CODES#S} or later. On older Android
+     * versions, an internal exception {@code RemoteServiceException} is logged instead, with
+     * a corresponding message.
      *
      * <p>Unlike the ordinary {@link #startService(Intent)}, this method can be used
      * at any time, regardless of whether the app hosting the service is in a foreground
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b214255..6446b44 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2346,6 +2346,26 @@
     public static final String ACTION_MANAGE_PERMISSION_USAGE =
             "android.intent.action.MANAGE_PERMISSION_USAGE";
 
+    /**
+     * Activity action: Launch UI to view the app's feature's information.
+     *
+     * <p>
+     * Output: Nothing.
+     * </p>
+     * <p class="note">
+     * You must protect the activity that handles this action with the
+     * {@link android.Manifest.permission#START_VIEW_APP_FEATURES} permission to ensure that
+     * only the system can launch this activity. The system will not launch activities
+     * that are not properly protected.
+     * </p>
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.START_VIEW_APP_FEATURES)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VIEW_APP_FEATURES =
+            "android.intent.action.VIEW_APP_FEATURES";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 172a51a..cbb5c2b 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -995,9 +995,10 @@
      * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
      * OVERRIDE_MIN_ASPECT_RATIO_LARGE
      *
-     * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's
-     * manifest will be overridden to the largest enabled aspect ratio treatment unless the app's
-     * manifest value is higher.
+     * If OVERRIDE_MIN_ASPECT_RATIO is applied, and the activity's orientation is fixed to
+     * portrait, the min aspect ratio given in the app's manifest will be overridden to the
+     * largest enabled aspect ratio treatment unless the app's manifest value is higher.
+     * TODO(b/203647190): add OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY instead of portrait by default
      * @hide
      */
     @ChangeId
@@ -1233,8 +1234,8 @@
      * Returns true if the activity has maximum or minimum aspect ratio.
      * @hide
      */
-    public boolean hasFixedAspectRatio() {
-        return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
+    public boolean hasFixedAspectRatio(@ScreenOrientation int orientation) {
+        return getMaxAspectRatio() != 0 || getMinAspectRatio(orientation) != 0;
     }
 
     /**
@@ -1393,10 +1394,14 @@
      * {@code getManifestMinAspectRatio}.
      * @hide
      */
-    public float getMinAspectRatio() {
+    public float getMinAspectRatio(@ScreenOrientation int orientation) {
+        // TODO(b/203647190): check orientation only if OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY
+        // In case the activity's orientation isn't fixed to portrait, OVERRIDE_MIN_ASPECT_RATIO
+        // shouldn't be applied.
         if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO,
                 applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+                UserHandle.getUserHandleForUid(applicationInfo.uid))
+                || !isFixedOrientationPortrait(orientation)) {
             return mMinAspectRatio;
         }
 
@@ -1522,9 +1527,10 @@
         if (getMaxAspectRatio() != 0) {
             pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio());
         }
-        if (getMinAspectRatio() != 0) {
-            pw.println(prefix + "minAspectRatio=" + getMinAspectRatio());
-            if (getManifestMinAspectRatio() !=  getMinAspectRatio()) {
+        final float minAspectRatio = getMinAspectRatio(screenOrientation);
+        if (minAspectRatio != 0) {
+            pw.println(prefix + "minAspectRatio=" + minAspectRatio);
+            if (getManifestMinAspectRatio() !=  minAspectRatio) {
                 pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio());
             }
         }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 5a5f6db..edddf40 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -34,17 +34,25 @@
 import android.content.pm.PackageManager.Property;
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedActivityImpl;
 import android.content.pm.parsing.component.ParsedAttribution;
+import android.content.pm.parsing.component.ParsedAttributionImpl;
 import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedInstrumentationImpl;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.component.ParsedPermission;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedPermissionGroupImpl;
+import android.content.pm.parsing.component.ParsedPermissionImpl;
 import android.content.pm.parsing.component.ParsedProcess;
 import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedProviderImpl;
 import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedServiceImpl;
 import android.content.pm.parsing.component.ParsedUsesPermission;
+import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
 import android.content.res.TypedArray;
 import android.os.Build;
 import android.os.Bundle;
@@ -103,8 +111,8 @@
     public static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
     public static ForInternedStringSet sForInternedStringSet =
             Parcelling.Cache.getOrCreate(ForInternedStringSet.class);
-    protected static ParsedIntentInfo.StringPairListParceler sForIntentInfoPairs =
-            Parcelling.Cache.getOrCreate(ParsedIntentInfo.StringPairListParceler.class);
+    protected static ParsingUtils.StringPairListParceler sForIntentInfoPairs =
+            new ParsingUtils.StringPairListParceler();
 
     private static final Comparator<ParsedMainComponent> ORDER_COMPARATOR =
             (first, second) -> Integer.compare(second.getOrder(), first.getOrder());
@@ -270,7 +278,7 @@
     protected List<ParsedInstrumentation> instrumentations = emptyList();
 
     @NonNull
-    @DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
+//    @DataClass.ParcelWith(ParsingUtils.StringPairListParceler.class)
     private List<Pair<String, ParsedIntentInfo>> preferredActivityFilters = emptyList();
 
     @NonNull
@@ -718,7 +726,7 @@
 
     @Override
     public ParsingPackageImpl addImplicitPermission(String permission) {
-        addUsesPermission(new ParsedUsesPermission(permission, 0 /*usesPermissionFlags*/));
+        addUsesPermission(new ParsedUsesPermissionImpl(permission, 0 /*usesPermissionFlags*/));
         this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
                 TextUtils.safeIntern(permission));
         return this;
@@ -1277,20 +1285,24 @@
         this.originalPackages = in.createStringArrayList();
         this.adoptPermissions = sForInternedStringList.unparcel(in);
         this.requestedPermissions = sForInternedStringList.unparcel(in);
-        this.usesPermissions = in.createTypedArrayList(ParsedUsesPermission.CREATOR);
+        this.usesPermissions = ParsingUtils.createTypedInterfaceList(in,
+                ParsedUsesPermissionImpl.CREATOR);
         this.implicitPermissions = sForInternedStringList.unparcel(in);
         this.upgradeKeySets = sForStringSet.unparcel(in);
         this.keySetMapping = ParsingPackageUtils.readKeySetMapping(in);
         this.protectedBroadcasts = sForInternedStringList.unparcel(in);
 
-        this.activities = in.createTypedArrayList(ParsedActivity.CREATOR);
-        this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR);
-        this.services = in.createTypedArrayList(ParsedService.CREATOR);
-        this.providers = in.createTypedArrayList(ParsedProvider.CREATOR);
-        this.attributions = in.createTypedArrayList(ParsedAttribution.CREATOR);
-        this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR);
-        this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR);
-        this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR);
+        this.activities = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
+        this.receivers = ParsingUtils.createTypedInterfaceList(in, ParsedActivityImpl.CREATOR);
+        this.services = ParsingUtils.createTypedInterfaceList(in, ParsedServiceImpl.CREATOR);
+        this.providers = ParsingUtils.createTypedInterfaceList(in, ParsedProviderImpl.CREATOR);
+        this.attributions = ParsingUtils.createTypedInterfaceList(in,
+                ParsedAttributionImpl.CREATOR);
+        this.permissions = ParsingUtils.createTypedInterfaceList(in, ParsedPermissionImpl.CREATOR);
+        this.permissionGroups = ParsingUtils.createTypedInterfaceList(in,
+                ParsedPermissionGroupImpl.CREATOR);
+        this.instrumentations = ParsingUtils.createTypedInterfaceList(in,
+                ParsedInstrumentationImpl.CREATOR);
         this.preferredActivityFilters = sForIntentInfoPairs.unparcel(in);
         this.processes = in.readHashMap(boot);
         this.metaData = in.readBundle(boot);
@@ -1685,7 +1697,7 @@
 
     private void addMimeGroupsFromComponent(ParsedComponent component) {
         for (int i = component.getIntents().size() - 1; i >= 0; i--) {
-            IntentFilter filter = component.getIntents().get(i);
+            IntentFilter filter = component.getIntents().get(i).getIntentFilter();
             for (int groupIndex = filter.countMimeGroups() - 1; groupIndex >= 0; groupIndex--) {
                 mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
             }
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 97af1d24..d19186e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -49,8 +49,10 @@
 import android.content.pm.PackageManager.Property;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
+import android.content.pm.parsing.component.ComponentMutateUtils;
 import android.content.pm.parsing.component.ComponentParseUtils;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedActivityImpl;
 import android.content.pm.parsing.component.ParsedActivityUtils;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedAttributionUtils;
@@ -70,6 +72,7 @@
 import android.content.pm.parsing.component.ParsedService;
 import android.content.pm.parsing.component.ParsedServiceUtils;
 import android.content.pm.parsing.component.ParsedUsesPermission;
+import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseInput.DeferredError;
 import android.content.pm.parsing.result.ParseResult;
@@ -267,27 +270,27 @@
             boolean collectCertificates) {
         ParseResult<ParsingPackage> result;
 
-        ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, splitPermissions,
-                new Callback() {
-                    @Override
-                    public boolean hasFeature(String feature) {
-                        // Assume the device doesn't support anything. This will affect permission
-                        // parsing and will force <uses-permission/> declarations to include all
-                        // requiredNotFeature permissions and exclude all requiredFeature
-                        // permissions. This mirrors the old behavior.
-                        return false;
-                    }
+        ParsingPackageUtils parser = new ParsingPackageUtils(false, null /*separateProcesses*/,
+                null /*displayMetrics*/, splitPermissions, new Callback() {
+            @Override
+            public boolean hasFeature(String feature) {
+                // Assume the device doesn't support anything. This will affect permission
+                // parsing and will force <uses-permission/> declarations to include all
+                // requiredNotFeature permissions and exclude all requiredFeature
+                // permissions. This mirrors the old behavior.
+                return false;
+            }
 
-                    @Override
-                    public ParsingPackage startParsingPackage(
-                            @NonNull String packageName,
-                            @NonNull String baseApkPath,
-                            @NonNull String path,
-                            @NonNull TypedArray manifestArray, boolean isCoreApp) {
-                        return new ParsingPackageImpl(packageName, baseApkPath, path,
-                                manifestArray);
-                    }
-                });
+            @Override
+            public ParsingPackage startParsingPackage(
+                    @NonNull String packageName,
+                    @NonNull String baseApkPath,
+                    @NonNull String path,
+                    @NonNull TypedArray manifestArray, boolean isCoreApp) {
+                return new ParsingPackageImpl(packageName, baseApkPath, path,
+                        manifestArray);
+            }
+        });
         result = parser.parsePackage(input, file, parseFlags);
         if (result.isError()) {
             return input.error(result);
@@ -625,8 +628,8 @@
 
         final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
         try {
-            final boolean isCoreApp =
-                    parser.getAttributeBooleanValue(null, "coreApp", false);
+            final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
+                    "coreApp",false);
             final ParsingPackage pkg = mCallback.startParsingPackage(
                     pkgName, apkPath, codePath, manifestArray, isCoreApp);
             final ParseResult<ParsingPackage> result =
@@ -732,6 +735,13 @@
         } finally {
             sa.recycle();
         }
+
+        // If the loaded component did not specify a split, inherit the split name
+        // based on the split it is defined in.
+        // This is used to later load the correct split when starting this
+        // component.
+        String defaultSplitName = pkg.getSplitNames()[splitIndex];
+
         final int depth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -753,8 +763,7 @@
                 case "receiver":
                     ParseResult<ParsedActivity> activityResult =
                             ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
-                                    res,
-                                    parser, flags, sUseRoundIcon, input);
+                                    res, parser, flags, sUseRoundIcon, defaultSplitName, input);
                     if (activityResult.isSuccess()) {
                         ParsedActivity activity = activityResult.getResult();
                         if (isActivity) {
@@ -768,8 +777,8 @@
                     break;
                 case "service":
                     ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
-                            mSeparateProcesses, pkg, res, parser, flags,
-                            sUseRoundIcon, input);
+                            mSeparateProcesses, pkg, res, parser, flags, sUseRoundIcon,
+                            defaultSplitName, input);
                     if (serviceResult.isSuccess()) {
                         ParsedService service = serviceResult.getResult();
                         pkg.addService(service);
@@ -780,7 +789,7 @@
                 case "provider":
                     ParseResult<ParsedProvider> providerResult =
                             ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
-                                    flags, sUseRoundIcon, input);
+                                    flags, sUseRoundIcon, defaultSplitName, input);
                     if (providerResult.isSuccess()) {
                         ParsedProvider provider = providerResult.getResult();
                         pkg.addProvider(provider);
@@ -790,7 +799,7 @@
                     break;
                 case "activity-alias":
                     activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
-                            sUseRoundIcon, input);
+                            sUseRoundIcon, defaultSplitName, input);
                     if (activityResult.isSuccess()) {
                         ParsedActivity activity = activityResult.getResult();
                         pkg.addActivity(activity);
@@ -807,14 +816,6 @@
             if (result.isError()) {
                 return input.error(result);
             }
-
-            if (mainComponent != null && mainComponent.getSplitName() == null) {
-                // If the loaded component did not specify a split, inherit the split name
-                // based on the split it is defined in.
-                // This is used to later load the correct split when starting this
-                // component.
-                mainComponent.setSplitName(pkg.getSplitNames()[splitIndex]);
-            }
         }
 
         return input.success(pkg);
@@ -832,15 +833,15 @@
                 // note: application meta-data is stored off to the side, so it can
                 // remain null in the primary copy (we like to avoid extra copies because
                 // it can be large)
-                ParseResult<Property> metaDataResult = parseMetaData(pkg, null, res,
-                        parser, "<meta-data>", input);
+                ParseResult<Property> metaDataResult = parseMetaData(pkg, null /*component*/,
+                        res, parser, "<meta-data>", input);
                 if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
                     pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
                 }
                 return metaDataResult;
             case "property":
-                ParseResult<Property> propertyResult = parseMetaData(pkg, null, res,
-                        parser, "<property>", input);
+                ParseResult<Property> propertyResult = parseMetaData(pkg, null /*component*/,
+                        res, parser, "<property>", input);
                 if (propertyResult.isSuccess()) {
                     pkg.addProperty(propertyResult.getResult());
                 }
@@ -919,7 +920,7 @@
             }
         }
 
-        if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) {
+        if (!ParsedAttributionUtils.isCombinationValid(pkg.getAttributions())) {
             return input.error(
                     INSTALL_PARSE_FAILED_BAD_MANIFEST,
                     "Combination <attribution> tags are not valid"
@@ -1339,7 +1340,7 @@
             }
 
             if (!found) {
-                pkg.addUsesPermission(new ParsedUsesPermission(name, usesPermissionFlags));
+                pkg.addUsesPermission(new ParsedUsesPermissionImpl(name, usesPermissionFlags));
             }
             return success;
         } finally {
@@ -1807,13 +1808,14 @@
                 continue;
             }
             if (parser.getName().equals("intent")) {
-                ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(null,
-                        pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input);
+                ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(
+                        null /*className*/, pkg, res, parser, true /*allowGlobs*/,
+                        true /*allowAutoVerify*/, input);
                 if (result.isError()) {
                     return input.error(result);
                 }
 
-                ParsedIntentInfo intentInfo = result.getResult();
+                IntentFilter intentInfo = result.getResult().getIntentFilter();
 
                 Uri data = null;
                 String dataType = null;
@@ -2077,7 +2079,7 @@
                         R.styleable.AndroidManifestApplication_process);
             }
             ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
-                    pkgName, null, pname, flags, mSeparateProcesses, input);
+                    pkgName, null /*defProc*/, pname, flags, mSeparateProcesses, input);
             if (processNameResult.isError()) {
                 return input.error(processNameResult);
             }
@@ -2146,7 +2148,8 @@
                 case "receiver":
                     ParseResult<ParsedActivity> activityResult =
                             ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
-                                    res, parser, flags, sUseRoundIcon, input);
+                                    res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
+                                    input);
 
                     if (activityResult.isSuccess()) {
                         ParsedActivity activity = activityResult.getResult();
@@ -2164,7 +2167,8 @@
                 case "service":
                     ParseResult<ParsedService> serviceResult =
                             ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
-                                    flags, sUseRoundIcon, input);
+                                    flags, sUseRoundIcon, null /*defaultSplitName*/,
+                                    input);
                     if (serviceResult.isSuccess()) {
                         ParsedService service = serviceResult.getResult();
                         hasServiceOrder |= (service.getOrder() != 0);
@@ -2176,7 +2180,8 @@
                 case "provider":
                     ParseResult<ParsedProvider> providerResult =
                             ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
-                                    flags, sUseRoundIcon, input);
+                                    flags, sUseRoundIcon, null /*defaultSplitName*/,
+                                    input);
                     if (providerResult.isSuccess()) {
                         pkg.addProvider(providerResult.getResult());
                     }
@@ -2185,7 +2190,8 @@
                     break;
                 case "activity-alias":
                     activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
-                            parser, sUseRoundIcon, input);
+                            parser, sUseRoundIcon, null /*defaultSplitName*/,
+                            input);
                     if (activityResult.isSuccess()) {
                         ParsedActivity activity = activityResult.getResult();
                         hasActivityOrder |= (activity.getOrder() != 0);
@@ -2327,15 +2333,15 @@
                 // note: application meta-data is stored off to the side, so it can
                 // remain null in the primary copy (we like to avoid extra copies because
                 // it can be large)
-                final ParseResult<Property> metaDataResult = parseMetaData(pkg, null, res,
-                        parser, "<meta-data>", input);
+                final ParseResult<Property> metaDataResult = parseMetaData(pkg, null /*component*/,
+                        res, parser, "<meta-data>", input);
                 if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
                     pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
                 }
                 return metaDataResult;
             case "property":
-                final ParseResult<Property> propertyResult = parseMetaData(pkg, null, res,
-                        parser, "<property>", input);
+                final ParseResult<Property> propertyResult = parseMetaData(pkg, null /*component*/,
+                        res, parser, "<property>", input);
                 if (propertyResult.isSuccess()) {
                     pkg.addProperty(propertyResult.getResult());
                 }
@@ -2647,7 +2653,7 @@
             List<ParsedIntentInfo> filters = activity.getIntents();
             final int filtersSize = filters.size();
             for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) {
-                ParsedIntentInfo aii = filters.get(filtersIndex);
+                IntentFilter aii = filters.get(filtersIndex).getIntentFilter();
                 if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
                 if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
                 if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
@@ -2697,7 +2703,8 @@
                     ? activity.getMetaData().getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
                     : maxAspectRatio;
 
-            activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio);
+            ComponentMutateUtils.setMaxAspectRatio(activity, activity.getResizeMode(),
+                    activityAspectRatio);
         }
     }
 
@@ -2714,7 +2721,8 @@
         for (int index = 0; index < activitiesSize; index++) {
             ParsedActivity activity = activities.get(index);
             if (activity.getMinAspectRatio() == ASPECT_RATIO_NOT_SET) {
-                activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio);
+                ComponentMutateUtils.setMinAspectRatio(activity, activity.getResizeMode(),
+                        minAspectRatio);
             }
         }
     }
@@ -2731,7 +2739,7 @@
             if (supportsSizeChanges || (activity.getMetaData() != null
                     && activity.getMetaData().getBoolean(
                             METADATA_SUPPORTS_SIZE_CHANGES, false))) {
-                activity.setSupportsSizeChanges(true);
+                ComponentMutateUtils.setSupportsSizeChanges(activity, true);
             }
         }
     }
@@ -2871,7 +2879,7 @@
     private static void convertCompatPermissions(ParsingPackage pkg) {
         for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
             final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
-            if (pkg.getTargetSdkVersion() >= info.sdkVersion) {
+            if (pkg.getTargetSdkVersion() >= info.getSdkVersion()) {
                 break;
             }
             if (!pkg.getRequestedPermissions().contains(info.getName())) {
@@ -2910,8 +2918,9 @@
         int activitiesSize = activities.size();
         for (int index = 0; index < activitiesSize; index++) {
             ParsedActivity activity = activities.get(index);
-            activity.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
-                    .setFlags(activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
+            ComponentMutateUtils.setResizeMode(activity, RESIZE_MODE_UNRESIZEABLE);
+            ComponentMutateUtils.setExactFlags(activity,
+                    activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
         }
     }
 
diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java
index f3a1740..cce984e 100644
--- a/core/java/android/content/pm/parsing/ParsingUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingUtils.java
@@ -20,16 +20,24 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedIntentInfoImpl;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
 import android.util.Slog;
 
+import com.android.internal.util.Parcelling;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 /** @hide **/
 public class ParsingUtils {
@@ -74,4 +82,66 @@
         XmlUtils.skipCurrentTag(parser);
         return input.success(null); // Type doesn't matter
     }
+
+    /**
+     * Use with {@link Parcel#writeTypedList(List)}
+     *
+     * @see Parcel#createTypedArrayList(Parcelable.Creator)
+     */
+    @NonNull
+    public static <Interface, Impl extends Interface> List<Interface> createTypedInterfaceList(
+            @NonNull Parcel parcel, @NonNull Parcelable.Creator<Impl> creator) {
+        int size = parcel.readInt();
+        if (size < 0) {
+            return new ArrayList<>();
+        }
+        ArrayList<Interface> list = new ArrayList<Interface>(size);
+        while (size > 0) {
+            list.add(parcel.readTypedObject(creator));
+            size--;
+        }
+        return list;
+    }
+
+    public static class StringPairListParceler implements
+            Parcelling<List<Pair<String, ParsedIntentInfo>>> {
+
+        @Override
+        public void parcel(List<Pair<String, ParsedIntentInfo>> item, Parcel dest,
+                int parcelFlags) {
+            if (item == null) {
+                dest.writeInt(-1);
+                return;
+            }
+
+            final int size = item.size();
+            dest.writeInt(size);
+
+            for (int index = 0; index < size; index++) {
+                Pair<String, ParsedIntentInfo> pair = item.get(index);
+                dest.writeString(pair.first);
+                dest.writeParcelable(pair.second, parcelFlags);
+            }
+        }
+
+        @Override
+        public List<Pair<String, ParsedIntentInfo>> unparcel(Parcel source) {
+            int size = source.readInt();
+            if (size == -1) {
+                return null;
+            }
+
+            if (size == 0) {
+                return new ArrayList<>(0);
+            }
+
+            final List<Pair<String, ParsedIntentInfo>> list = new ArrayList<>(size);
+            for (int i = 0; i < size; ++i) {
+                list.add(Pair.create(source.readString(), source.readParcelable(
+                        ParsedIntentInfoImpl.class.getClassLoader())));
+            }
+
+            return list;
+        }
+    }
 }
diff --git a/core/java/android/content/pm/parsing/component/ComponentMutateUtils.java b/core/java/android/content/pm/parsing/component/ComponentMutateUtils.java
new file mode 100644
index 0000000..73d0b9f
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ComponentMutateUtils.java
@@ -0,0 +1,98 @@
+/*
+ * 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 android.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Contains mutation methods so that code doesn't have to cast to the Impl. Meant to eventually
+ * be removed once all post-parsing mutation is moved to parsing.
+ *
+ * @hide
+ */
+public class ComponentMutateUtils {
+
+    public static void setMaxAspectRatio(@NonNull ParsedActivity activity, int resizeMode,
+            float maxAspectRatio) {
+        ((ParsedActivityImpl) activity).setMaxAspectRatio(resizeMode, maxAspectRatio);
+    }
+
+    public static void setMinAspectRatio(@NonNull ParsedActivity activity, int resizeMode,
+            float minAspectRatio) {
+        ((ParsedActivityImpl) activity).setMinAspectRatio(resizeMode, minAspectRatio);
+    }
+
+    public static void setSupportsSizeChanges(@NonNull ParsedActivity activity,
+            boolean supportsSizeChanges) {
+        ((ParsedActivityImpl) activity).setSupportsSizeChanges(supportsSizeChanges);
+    }
+
+    public static void setResizeMode(@NonNull ParsedActivity activity, int resizeMode) {
+        ((ParsedActivityImpl) activity).setResizeMode(resizeMode);
+    }
+
+    public static void setExactFlags(ParsedComponent component, int exactFlags) {
+        ((ParsedComponentImpl) component).setFlags(exactFlags);
+    }
+
+    public static void setEnabled(@NonNull ParsedMainComponent component, boolean enabled) {
+        ((ParsedMainComponentImpl) component).setEnabled(enabled);
+    }
+
+    public static void setPackageName(@NonNull ParsedComponent component,
+            @NonNull String packageName) {
+        ((ParsedComponentImpl) component).setPackageName(packageName);
+    }
+
+    public static void setDirectBootAware(@NonNull ParsedMainComponent component,
+            boolean directBootAware) {
+        ((ParsedMainComponentImpl) component).setDirectBootAware(directBootAware);
+    }
+
+    public static void setExported(@NonNull ParsedMainComponent component, boolean exported) {
+        ((ParsedMainComponentImpl) component).setExported(exported);
+    }
+
+    public static void setAuthority(@NonNull ParsedProvider provider, @Nullable String authority) {
+        ((ParsedProviderImpl) provider).setAuthority(authority);
+    }
+
+    public static void setSyncable(@NonNull ParsedProvider provider, boolean syncable) {
+        ((ParsedProviderImpl) provider).setSyncable(syncable);
+    }
+
+    public static void setProtectionLevel(@NonNull ParsedPermission permission,
+            int protectionLevel) {
+        ((ParsedPermissionImpl) permission).setProtectionLevel(protectionLevel);
+    }
+
+    public static void setParsedPermissionGroup(@NonNull ParsedPermission permission,
+            @NonNull ParsedPermissionGroup permissionGroup) {
+        ((ParsedPermissionImpl) permission).setParsedPermissionGroup(permissionGroup);
+    }
+
+    public static void setPriority(@NonNull ParsedPermissionGroup parsedPermissionGroup,
+            int priority) {
+        ((ParsedPermissionGroupImpl) parsedPermissionGroup).setPriority(priority);
+    }
+
+    public static void addStateFrom(@NonNull ParsedProcess oldProcess,
+            @NonNull ParsedProcess newProcess) {
+        ((ParsedProcessImpl) oldProcess).addStateFrom(newProcess);
+    }
+}
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
index ae6181d..012a542 100644
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
@@ -45,13 +46,14 @@
 public class ComponentParseUtils {
 
     public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
-        return intentInfo.hasCategory(Intent.CATEGORY_BROWSABLE)
-                || intentInfo.hasAction(Intent.ACTION_SEND)
-                || intentInfo.hasAction(Intent.ACTION_SENDTO)
-                || intentInfo.hasAction(Intent.ACTION_SEND_MULTIPLE);
+        IntentFilter intentFilter = intentInfo.getIntentFilter();
+        return intentFilter.hasCategory(Intent.CATEGORY_BROWSABLE)
+                || intentFilter.hasAction(Intent.ACTION_SEND)
+                || intentFilter.hasAction(Intent.ACTION_SENDTO)
+                || intentFilter.hasAction(Intent.ACTION_SEND_MULTIPLE);
     }
 
-    static <Component extends ParsedComponent> ParseResult<Component> parseAllMetaData(
+    static <Component extends ParsedComponentImpl> ParseResult<Component> parseAllMetaData(
             ParsingPackage pkg, Resources res, XmlResourceParser parser, String tag,
             Component component, ParseInput input) throws XmlPullParserException, IOException {
         final int depth = parser.getDepth();
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 8ca86f1..a661b51 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -16,516 +16,74 @@
 
 package android.content.pm.parsing.component;
 
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-import static android.content.pm.parsing.ParsingPackageUtils.ASPECT_RATIO_NOT_SET;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
 /** @hide **/
-public class ParsedActivity extends ParsedMainComponent {
-
-    private int theme;
-    private int uiOptions;
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String targetActivity;
-
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String parentActivityName;
-    @Nullable
-    private String taskAffinity;
-    private int privateFlags;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String permission;
-
-    private int launchMode;
-    private int documentLaunchMode;
-    private int maxRecents;
-    private int configChanges;
-    private int softInputMode;
-    private int persistableMode;
-    private int lockTaskLaunchMode;
-
-    private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-    private int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
-
-    private float maxAspectRatio = ASPECT_RATIO_NOT_SET;
-    private float minAspectRatio = ASPECT_RATIO_NOT_SET;
-
-    private boolean supportsSizeChanges;
-
-    @Nullable
-    private String requestedVrComponent;
-    private int rotationAnimation = -1;
-    private int colorMode;
-
-    @Nullable
-    private ActivityInfo.WindowLayout windowLayout;
-
-    public ParsedActivity(ParsedActivity other) {
-        super(other);
-        this.theme = other.theme;
-        this.uiOptions = other.uiOptions;
-        this.targetActivity = other.targetActivity;
-        this.parentActivityName = other.parentActivityName;
-        this.taskAffinity = other.taskAffinity;
-        this.privateFlags = other.privateFlags;
-        this.permission = other.permission;
-        this.launchMode = other.launchMode;
-        this.documentLaunchMode = other.documentLaunchMode;
-        this.maxRecents = other.maxRecents;
-        this.configChanges = other.configChanges;
-        this.softInputMode = other.softInputMode;
-        this.persistableMode = other.persistableMode;
-        this.lockTaskLaunchMode = other.lockTaskLaunchMode;
-        this.screenOrientation = other.screenOrientation;
-        this.resizeMode = other.resizeMode;
-        this.maxAspectRatio = other.maxAspectRatio;
-        this.minAspectRatio = other.minAspectRatio;
-        this.supportsSizeChanges = other.supportsSizeChanges;
-        this.requestedVrComponent = other.requestedVrComponent;
-        this.rotationAnimation = other.rotationAnimation;
-        this.colorMode = other.colorMode;
-        this.windowLayout = other.windowLayout;
-    }
+public interface ParsedActivity extends ParsedMainComponent {
 
     /**
      * Generate activity object that forwards user to App Details page automatically.
      * This activity should be invisible to user and user should not know or see it.
      */
-    public static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
-            int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
-        ParsedActivity activity = new ParsedActivity();
-        activity.setPackageName(packageName);
-        activity.theme = android.R.style.Theme_NoDisplay;
-        activity.exported = true;
-        activity.setName(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
-        activity.setProcessName(processName);
-        activity.uiOptions = uiOptions;
-        activity.taskAffinity = taskAffinity;
-        activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-        activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
-        activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
-        activity.configChanges = ParsedActivityUtils.getActivityConfigChanges(0, 0);
-        activity.softInputMode = 0;
-        activity.persistableMode = ActivityInfo.PERSIST_NEVER;
-        activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-        activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
-        activity.lockTaskLaunchMode = 0;
-        activity.setDirectBootAware(false);
-        activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
-        activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
-        if (hardwareAccelerated) {
-            activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_HARDWARE_ACCELERATED);
-        }
-        return activity;
-    }
-
-    static ParsedActivity makeAlias(String targetActivityName, ParsedActivity target) {
-        ParsedActivity alias = new ParsedActivity();
-        alias.setPackageName(target.getPackageName());
-        alias.setTargetActivity(targetActivityName);
-        alias.configChanges = target.configChanges;
-        alias.flags = target.flags;
-        alias.privateFlags = target.privateFlags;
-        alias.icon = target.icon;
-        alias.logo = target.logo;
-        alias.banner = target.banner;
-        alias.labelRes = target.labelRes;
-        alias.nonLocalizedLabel = target.nonLocalizedLabel;
-        alias.launchMode = target.launchMode;
-        alias.lockTaskLaunchMode = target.lockTaskLaunchMode;
-        alias.documentLaunchMode = target.documentLaunchMode;
-        alias.descriptionRes = target.descriptionRes;
-        alias.screenOrientation = target.screenOrientation;
-        alias.taskAffinity = target.taskAffinity;
-        alias.theme = target.theme;
-        alias.softInputMode = target.softInputMode;
-        alias.uiOptions = target.uiOptions;
-        alias.parentActivityName = target.parentActivityName;
-        alias.maxRecents = target.maxRecents;
-        alias.windowLayout = target.windowLayout;
-        alias.resizeMode = target.resizeMode;
-        alias.maxAspectRatio = target.maxAspectRatio;
-        alias.minAspectRatio = target.minAspectRatio;
-        alias.supportsSizeChanges = target.supportsSizeChanges;
-        alias.requestedVrComponent = target.requestedVrComponent;
-        alias.directBootAware = target.directBootAware;
-        alias.setProcessName(target.getProcessName());
-        return alias;
-
-        // Not all attributes from the target ParsedActivity are copied to the alias.
-        // Careful when adding an attribute and determine whether or not it should be copied.
-//        alias.enabled = target.enabled;
-//        alias.exported = target.exported;
-//        alias.permission = target.permission;
-//        alias.splitName = target.splitName;
-//        alias.persistableMode = target.persistableMode;
-//        alias.rotationAnimation = target.rotationAnimation;
-//        alias.colorMode = target.colorMode;
-//        alias.intents.addAll(target.intents);
-//        alias.order = target.order;
-//        alias.metaData = target.metaData;
-    }
-
-    public boolean isSupportsSizeChanges() {
-        return supportsSizeChanges;
-    }
-
-    public ParsedActivity setColorMode(int colorMode) {
-        this.colorMode = colorMode;
-        return this;
-    }
-
-    public ParsedActivity setConfigChanges(int configChanges) {
-        this.configChanges = configChanges;
-        return this;
-    }
-
-    public ParsedActivity setDocumentLaunchMode(int documentLaunchMode) {
-        this.documentLaunchMode = documentLaunchMode;
-        return this;
-    }
-
-    public ParsedActivity setLaunchMode(int launchMode) {
-        this.launchMode = launchMode;
-        return this;
-    }
-
-    public ParsedActivity setLockTaskLaunchMode(int lockTaskLaunchMode) {
-        this.lockTaskLaunchMode = lockTaskLaunchMode;
-        return this;
-    }
-
-    public ParsedActivity setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
-        if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
-                || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
-            // Resizeable activities can be put in any aspect ratio.
-            return this;
-        }
-
-        if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
-            // Ignore any value lesser than 1.0.
-            return this;
-        }
-
-        this.maxAspectRatio = maxAspectRatio;
-        return this;
-    }
-
-    public ParsedActivity setMaxAspectRatio(float maxAspectRatio) {
-        this.maxAspectRatio = maxAspectRatio;
-        return this;
-    }
-
-    public ParsedActivity setMaxRecents(int maxRecents) {
-        this.maxRecents = maxRecents;
-        return this;
-    }
-
-    public ParsedActivity setMinAspectRatio(int resizeMode, float minAspectRatio) {
-        if (resizeMode == RESIZE_MODE_RESIZEABLE
-                || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
-            // Resizeable activities can be put in any aspect ratio.
-            return this;
-        }
-
-        if (minAspectRatio < 1.0f && minAspectRatio != 0) {
-            // Ignore any value lesser than 1.0.
-            return this;
-        }
-
-        this.minAspectRatio = minAspectRatio;
-        return this;
-    }
-
-    public ParsedActivity setMinAspectRatio(float minAspectRatio) {
-        this.minAspectRatio = minAspectRatio;
-        return this;
-    }
-
-    public ParsedActivity setParentActivityName(String parentActivityName) {
-        this.parentActivityName = parentActivityName;
-        return this;
-    }
-
-    public ParsedActivity setPersistableMode(int persistableMode) {
-        this.persistableMode = persistableMode;
-        return this;
-    }
-
-    public ParsedActivity setPrivateFlags(int privateFlags) {
-        this.privateFlags = privateFlags;
-        return this;
-    }
-
-    public ParsedActivity setRequestedVrComponent(String requestedVrComponent) {
-        this.requestedVrComponent = requestedVrComponent;
-        return this;
-    }
-
-    public ParsedActivity setRotationAnimation(int rotationAnimation) {
-        this.rotationAnimation = rotationAnimation;
-        return this;
-    }
-
-    public ParsedActivity setScreenOrientation(int screenOrientation) {
-        this.screenOrientation = screenOrientation;
-        return this;
-    }
-
-    public ParsedActivity setSoftInputMode(int softInputMode) {
-        this.softInputMode = softInputMode;
-        return this;
-    }
-
-    public ParsedActivity setSupportsSizeChanges(boolean supportsSizeChanges) {
-        this.supportsSizeChanges = supportsSizeChanges;
-        return this;
-    }
-
-    public ParsedActivity setResizeMode(int resizeMode) {
-        this.resizeMode = resizeMode;
-        return this;
-    }
-
-    public ParsedActivity setTargetActivity(String targetActivity) {
-        this.targetActivity = TextUtils.safeIntern(targetActivity);
-        return this;
-    }
-
-    public ParsedActivity setPermission(String permission) {
-        // Empty string must be converted to null
-        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
-        return this;
-    }
-
-    public ParsedActivity setTaskAffinity(String taskAffinity) {
-        this.taskAffinity = taskAffinity;
-        return this;
-    }
-
-    public ParsedActivity setTheme(int theme) {
-        this.theme = theme;
-        return this;
-    }
-
-    public ParsedActivity setUiOptions(int uiOptions) {
-        this.uiOptions = uiOptions;
-        return this;
-    }
-
-    public ParsedActivity setWindowLayout(ActivityInfo.WindowLayout windowLayout) {
-        this.windowLayout = windowLayout;
-        return this;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Activity{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        ComponentName.appendShortString(sb, getPackageName(), getName());
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeInt(this.theme);
-        dest.writeInt(this.uiOptions);
-        dest.writeString(this.targetActivity);
-        dest.writeString(this.parentActivityName);
-        dest.writeString(this.taskAffinity);
-        dest.writeInt(this.privateFlags);
-        sForInternedString.parcel(this.permission, dest, flags);
-        dest.writeInt(this.launchMode);
-        dest.writeInt(this.documentLaunchMode);
-        dest.writeInt(this.maxRecents);
-        dest.writeInt(this.configChanges);
-        dest.writeInt(this.softInputMode);
-        dest.writeInt(this.persistableMode);
-        dest.writeInt(this.lockTaskLaunchMode);
-        dest.writeInt(this.screenOrientation);
-        dest.writeInt(this.resizeMode);
-        dest.writeFloat(this.maxAspectRatio);
-        dest.writeFloat(this.minAspectRatio);
-        dest.writeBoolean(this.supportsSizeChanges);
-        dest.writeString(this.requestedVrComponent);
-        dest.writeInt(this.rotationAnimation);
-        dest.writeInt(this.colorMode);
-        dest.writeBundle(this.metaData);
-
-        if (windowLayout != null) {
-            dest.writeInt(1);
-            windowLayout.writeToParcel(dest);
-        } else {
-            dest.writeBoolean(false);
-        }
-    }
-
-    public ParsedActivity() {
-    }
-
-    protected ParsedActivity(Parcel in) {
-        super(in);
-        this.theme = in.readInt();
-        this.uiOptions = in.readInt();
-        this.targetActivity = in.readString();
-        this.parentActivityName = in.readString();
-        this.taskAffinity = in.readString();
-        this.privateFlags = in.readInt();
-        this.permission = sForInternedString.unparcel(in);
-        this.launchMode = in.readInt();
-        this.documentLaunchMode = in.readInt();
-        this.maxRecents = in.readInt();
-        this.configChanges = in.readInt();
-        this.softInputMode = in.readInt();
-        this.persistableMode = in.readInt();
-        this.lockTaskLaunchMode = in.readInt();
-        this.screenOrientation = in.readInt();
-        this.resizeMode = in.readInt();
-        this.maxAspectRatio = in.readFloat();
-        this.minAspectRatio = in.readFloat();
-        this.supportsSizeChanges = in.readBoolean();
-        this.requestedVrComponent = in.readString();
-        this.rotationAnimation = in.readInt();
-        this.colorMode = in.readInt();
-        this.metaData = in.readBundle();
-        if (in.readBoolean()) {
-            windowLayout = new ActivityInfo.WindowLayout(in);
-        }
-    }
-
     @NonNull
-    public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
-        @Override
-        public ParsedActivity createFromParcel(Parcel source) {
-            return new ParsedActivity(source);
-        }
-
-        @Override
-        public ParsedActivity[] newArray(int size) {
-            return new ParsedActivity[size];
-        }
-    };
-
-    public int getTheme() {
-        return theme;
+    static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
+            int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
+        // Proxy method since ParsedActivityImpl is supposed to be package visibility
+        return ParsedActivityImpl.makeAppDetailsActivity(packageName, processName, uiOptions,
+                taskAffinity, hardwareAccelerated);
     }
 
-    public int getUiOptions() {
-        return uiOptions;
-    }
+    int getColorMode();
+
+    int getConfigChanges();
+
+    int getDocumentLaunchMode();
+
+    int getLaunchMode();
+
+    int getLockTaskLaunchMode();
+
+    int getMaxRecents();
+
+    float getMaxAspectRatio();
+
+    float getMinAspectRatio();
 
     @Nullable
-    public String getTargetActivity() {
-        return targetActivity;
-    }
+    String getParentActivityName();
 
     @Nullable
-    public String getParentActivityName() {
-        return parentActivityName;
-    }
+    String getPermission();
+
+    int getPersistableMode();
+
+    int getPrivateFlags();
 
     @Nullable
-    public String getTaskAffinity() {
-        return taskAffinity;
-    }
+    String getRequestedVrComponent();
 
-    public int getPrivateFlags() {
-        return privateFlags;
-    }
+    int getRotationAnimation();
+
+    int getResizeMode();
+
+    int getScreenOrientation();
+
+    int getSoftInputMode();
 
     @Nullable
-    public String getPermission() {
-        return permission;
-    }
-
-    public int getLaunchMode() {
-        return launchMode;
-    }
-
-    public int getDocumentLaunchMode() {
-        return documentLaunchMode;
-    }
-
-    public int getMaxRecents() {
-        return maxRecents;
-    }
-
-    public int getConfigChanges() {
-        return configChanges;
-    }
-
-    public int getSoftInputMode() {
-        return softInputMode;
-    }
-
-    public int getPersistableMode() {
-        return persistableMode;
-    }
-
-    public int getLockTaskLaunchMode() {
-        return lockTaskLaunchMode;
-    }
-
-    public int getScreenOrientation() {
-        return screenOrientation;
-    }
-
-    public int getResizeMode() {
-        return resizeMode;
-    }
-
-    public float getMaxAspectRatio() {
-        return maxAspectRatio;
-    }
-
-    public float getMinAspectRatio() {
-        return minAspectRatio;
-    }
+    String getTargetActivity();
 
     @Nullable
-    public String getRequestedVrComponent() {
-        return requestedVrComponent;
-    }
+    String getTaskAffinity();
 
-    public int getRotationAnimation() {
-        return rotationAnimation;
-    }
+    int getTheme();
 
-    public int getColorMode() {
-        return colorMode;
-    }
+    int getUiOptions();
 
     @Nullable
-    public ActivityInfo.WindowLayout getWindowLayout() {
-        return windowLayout;
-    }
+    ActivityInfo.WindowLayout getWindowLayout();
+
+    boolean isSupportsSizeChanges();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java b/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
new file mode 100644
index 0000000..93dc5a4
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
@@ -0,0 +1,655 @@
+/*
+ * 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 android.content.pm.parsing.component;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.parsing.ParsingUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedActivityImpl extends ParsedMainComponentImpl implements ParsedActivity {
+
+    private int theme;
+    private int uiOptions;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetActivity;
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String parentActivityName;
+    @Nullable
+    private String taskAffinity;
+    private int privateFlags;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String permission;
+
+    private int launchMode;
+    private int documentLaunchMode;
+    private int maxRecents;
+    private int configChanges;
+    private int softInputMode;
+    private int persistableMode;
+    private int lockTaskLaunchMode;
+
+    private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    private int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+
+    private float maxAspectRatio = ParsingUtils.NOT_SET;
+    private float minAspectRatio = ParsingUtils.NOT_SET;
+
+    private boolean supportsSizeChanges;
+
+    @Nullable
+    private String requestedVrComponent;
+    private int rotationAnimation = -1;
+    private int colorMode;
+
+    @Nullable
+    private ActivityInfo.WindowLayout windowLayout;
+
+    public ParsedActivityImpl(ParsedActivityImpl other) {
+        super(other);
+        this.theme = other.theme;
+        this.uiOptions = other.uiOptions;
+        this.targetActivity = other.targetActivity;
+        this.parentActivityName = other.parentActivityName;
+        this.taskAffinity = other.taskAffinity;
+        this.privateFlags = other.privateFlags;
+        this.permission = other.permission;
+        this.launchMode = other.launchMode;
+        this.documentLaunchMode = other.documentLaunchMode;
+        this.maxRecents = other.maxRecents;
+        this.configChanges = other.configChanges;
+        this.softInputMode = other.softInputMode;
+        this.persistableMode = other.persistableMode;
+        this.lockTaskLaunchMode = other.lockTaskLaunchMode;
+        this.screenOrientation = other.screenOrientation;
+        this.resizeMode = other.resizeMode;
+        this.maxAspectRatio = other.maxAspectRatio;
+        this.minAspectRatio = other.minAspectRatio;
+        this.supportsSizeChanges = other.supportsSizeChanges;
+        this.requestedVrComponent = other.requestedVrComponent;
+        this.rotationAnimation = other.rotationAnimation;
+        this.colorMode = other.colorMode;
+        this.windowLayout = other.windowLayout;
+    }
+
+    /**
+     * Generate activity object that forwards user to App Details page automatically.
+     * This activity should be invisible to user and user should not know or see it.
+     */
+    @NonNull
+    static ParsedActivityImpl makeAppDetailsActivity(String packageName, String processName,
+            int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
+        ParsedActivityImpl activity = new ParsedActivityImpl();
+        activity.setPackageName(packageName);
+        activity.theme = android.R.style.Theme_NoDisplay;
+        activity.setExported(true);
+        activity.setName(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
+        activity.setProcessName(processName);
+        activity.uiOptions = uiOptions;
+        activity.taskAffinity = taskAffinity;
+        activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+        activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
+        activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
+        activity.configChanges = ParsedActivityUtils.getActivityConfigChanges(0, 0);
+        activity.softInputMode = 0;
+        activity.persistableMode = ActivityInfo.PERSIST_NEVER;
+        activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+        activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        activity.lockTaskLaunchMode = 0;
+        activity.setDirectBootAware(false);
+        activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
+        activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        if (hardwareAccelerated) {
+            activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_HARDWARE_ACCELERATED);
+        }
+        return activity;
+    }
+
+    @NonNull
+    static ParsedActivityImpl makeAlias(String targetActivityName, ParsedActivity target) {
+        ParsedActivityImpl alias = new ParsedActivityImpl();
+        alias.setPackageName(target.getPackageName());
+        alias.setTargetActivity(targetActivityName);
+        alias.configChanges = target.getConfigChanges();
+        alias.setFlags(target.getFlags());
+        alias.privateFlags = target.getPrivateFlags();
+        alias.setIcon(target.getIcon());
+        alias.setLogo(target.getLogo());
+        alias.setBanner(target.getBanner());
+        alias.setLabelRes(target.getLabelRes());
+        alias.setNonLocalizedLabel(target.getNonLocalizedLabel());
+        alias.launchMode = target.getLaunchMode();
+        alias.lockTaskLaunchMode = target.getLockTaskLaunchMode();
+        alias.documentLaunchMode = target.getDocumentLaunchMode();
+        alias.setDescriptionRes(target.getDescriptionRes());
+        alias.screenOrientation = target.getScreenOrientation();
+        alias.taskAffinity = target.getTaskAffinity();
+        alias.theme = target.getTheme();
+        alias.softInputMode = target.getSoftInputMode();
+        alias.uiOptions = target.getUiOptions();
+        alias.parentActivityName = target.getParentActivityName();
+        alias.maxRecents = target.getMaxRecents();
+        alias.windowLayout = target.getWindowLayout();
+        alias.resizeMode = target.getResizeMode();
+        alias.maxAspectRatio = target.getMaxAspectRatio();
+        alias.minAspectRatio = target.getMinAspectRatio();
+        alias.supportsSizeChanges = target.isSupportsSizeChanges();
+        alias.requestedVrComponent = target.getRequestedVrComponent();
+        alias.setDirectBootAware(target.isDirectBootAware());
+        alias.setProcessName(target.getProcessName());
+        return alias;
+
+        // Not all attributes from the target ParsedActivity are copied to the alias.
+        // Careful when adding an attribute and determine whether or not it should be copied.
+//        alias.enabled = target.enabled;
+//        alias.exported = target.exported;
+//        alias.permission = target.permission;
+//        alias.splitName = target.splitName;
+//        alias.persistableMode = target.persistableMode;
+//        alias.rotationAnimation = target.rotationAnimation;
+//        alias.colorMode = target.colorMode;
+//        alias.intents.addAll(target.intents);
+//        alias.order = target.order;
+//        alias.metaData = target.metaData;
+    }
+
+    public ParsedActivityImpl setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
+        if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
+                || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+            // Resizeable activities can be put in any aspect ratio.
+            return this;
+        }
+
+        if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
+            // Ignore any value lesser than 1.0.
+            return this;
+        }
+
+        this.maxAspectRatio = maxAspectRatio;
+        return this;
+    }
+
+    public ParsedActivityImpl setMinAspectRatio(int resizeMode, float minAspectRatio) {
+        if (resizeMode == RESIZE_MODE_RESIZEABLE
+                || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+            // Resizeable activities can be put in any aspect ratio.
+            return this;
+        }
+
+        if (minAspectRatio < 1.0f && minAspectRatio != 0) {
+            // Ignore any value lesser than 1.0.
+            return this;
+        }
+
+        this.minAspectRatio = minAspectRatio;
+        return this;
+    }
+
+    public ParsedActivityImpl setTargetActivity(String targetActivity) {
+        this.targetActivity = TextUtils.safeIntern(targetActivity);
+        return this;
+    }
+
+    public ParsedActivityImpl setPermission(String permission) {
+        // Empty string must be converted to null
+        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Activity{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(this.theme);
+        dest.writeInt(this.uiOptions);
+        dest.writeString(this.targetActivity);
+        dest.writeString(this.parentActivityName);
+        dest.writeString(this.taskAffinity);
+        dest.writeInt(this.privateFlags);
+        sForInternedString.parcel(this.permission, dest, flags);
+        dest.writeInt(this.launchMode);
+        dest.writeInt(this.documentLaunchMode);
+        dest.writeInt(this.maxRecents);
+        dest.writeInt(this.configChanges);
+        dest.writeInt(this.softInputMode);
+        dest.writeInt(this.persistableMode);
+        dest.writeInt(this.lockTaskLaunchMode);
+        dest.writeInt(this.screenOrientation);
+        dest.writeInt(this.resizeMode);
+        dest.writeValue(this.maxAspectRatio);
+        dest.writeValue(this.minAspectRatio);
+        dest.writeBoolean(this.supportsSizeChanges);
+        dest.writeString(this.requestedVrComponent);
+        dest.writeInt(this.rotationAnimation);
+        dest.writeInt(this.colorMode);
+        dest.writeBundle(this.getMetaData());
+
+        if (windowLayout != null) {
+            dest.writeInt(1);
+            windowLayout.writeToParcel(dest);
+        } else {
+            dest.writeBoolean(false);
+        }
+    }
+
+    public ParsedActivityImpl() {
+    }
+
+    protected ParsedActivityImpl(Parcel in) {
+        super(in);
+        this.theme = in.readInt();
+        this.uiOptions = in.readInt();
+        this.targetActivity = in.readString();
+        this.parentActivityName = in.readString();
+        this.taskAffinity = in.readString();
+        this.privateFlags = in.readInt();
+        this.permission = sForInternedString.unparcel(in);
+        this.launchMode = in.readInt();
+        this.documentLaunchMode = in.readInt();
+        this.maxRecents = in.readInt();
+        this.configChanges = in.readInt();
+        this.softInputMode = in.readInt();
+        this.persistableMode = in.readInt();
+        this.lockTaskLaunchMode = in.readInt();
+        this.screenOrientation = in.readInt();
+        this.resizeMode = in.readInt();
+        this.maxAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+        this.minAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+        this.supportsSizeChanges = in.readBoolean();
+        this.requestedVrComponent = in.readString();
+        this.rotationAnimation = in.readInt();
+        this.colorMode = in.readInt();
+        this.setMetaData(in.readBundle());
+        if (in.readBoolean()) {
+            windowLayout = new ActivityInfo.WindowLayout(in);
+        }
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedActivityImpl> CREATOR =
+            new Parcelable.Creator<ParsedActivityImpl>() {
+        @Override
+        public ParsedActivityImpl createFromParcel(Parcel source) {
+            return new ParsedActivityImpl(source);
+        }
+
+        @Override
+        public ParsedActivityImpl[] newArray(int size) {
+            return new ParsedActivityImpl[size];
+        }
+    };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedActivityImpl(
+            int theme,
+            int uiOptions,
+            @Nullable String targetActivity,
+            @Nullable String parentActivityName,
+            @Nullable String taskAffinity,
+            int privateFlags,
+            @Nullable String permission,
+            int launchMode,
+            int documentLaunchMode,
+            int maxRecents,
+            int configChanges,
+            int softInputMode,
+            int persistableMode,
+            int lockTaskLaunchMode,
+            int screenOrientation,
+            int resizeMode,
+            float maxAspectRatio,
+            float minAspectRatio,
+            boolean supportsSizeChanges,
+            @Nullable String requestedVrComponent,
+            int rotationAnimation,
+            int colorMode,
+            @Nullable ActivityInfo.WindowLayout windowLayout) {
+        this.theme = theme;
+        this.uiOptions = uiOptions;
+        this.targetActivity = targetActivity;
+        this.parentActivityName = parentActivityName;
+        this.taskAffinity = taskAffinity;
+        this.privateFlags = privateFlags;
+        this.permission = permission;
+        this.launchMode = launchMode;
+        this.documentLaunchMode = documentLaunchMode;
+        this.maxRecents = maxRecents;
+        this.configChanges = configChanges;
+        this.softInputMode = softInputMode;
+        this.persistableMode = persistableMode;
+        this.lockTaskLaunchMode = lockTaskLaunchMode;
+        this.screenOrientation = screenOrientation;
+        this.resizeMode = resizeMode;
+        this.maxAspectRatio = maxAspectRatio;
+        this.minAspectRatio = minAspectRatio;
+        this.supportsSizeChanges = supportsSizeChanges;
+        this.requestedVrComponent = requestedVrComponent;
+        this.rotationAnimation = rotationAnimation;
+        this.colorMode = colorMode;
+        this.windowLayout = windowLayout;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public int getTheme() {
+        return theme;
+    }
+
+    @DataClass.Generated.Member
+    public int getUiOptions() {
+        return uiOptions;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getTargetActivity() {
+        return targetActivity;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getParentActivityName() {
+        return parentActivityName;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getTaskAffinity() {
+        return taskAffinity;
+    }
+
+    @DataClass.Generated.Member
+    public int getPrivateFlags() {
+        return privateFlags;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getPermission() {
+        return permission;
+    }
+
+    @DataClass.Generated.Member
+    public int getLaunchMode() {
+        return launchMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getDocumentLaunchMode() {
+        return documentLaunchMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getMaxRecents() {
+        return maxRecents;
+    }
+
+    @DataClass.Generated.Member
+    public int getConfigChanges() {
+        return configChanges;
+    }
+
+    @DataClass.Generated.Member
+    public int getSoftInputMode() {
+        return softInputMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getPersistableMode() {
+        return persistableMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getLockTaskLaunchMode() {
+        return lockTaskLaunchMode;
+    }
+
+    @DataClass.Generated.Member
+    public int getScreenOrientation() {
+        return screenOrientation;
+    }
+
+    @DataClass.Generated.Member
+    public int getResizeMode() {
+        return resizeMode;
+    }
+
+    @DataClass.Generated.Member
+    public float getMaxAspectRatio() {
+        return maxAspectRatio;
+    }
+
+    @DataClass.Generated.Member
+    public float getMinAspectRatio() {
+        return minAspectRatio;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isSupportsSizeChanges() {
+        return supportsSizeChanges;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getRequestedVrComponent() {
+        return requestedVrComponent;
+    }
+
+    @DataClass.Generated.Member
+    public int getRotationAnimation() {
+        return rotationAnimation;
+    }
+
+    @DataClass.Generated.Member
+    public int getColorMode() {
+        return colorMode;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable ActivityInfo.WindowLayout getWindowLayout() {
+        return windowLayout;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setTheme( int value) {
+        theme = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setUiOptions( int value) {
+        uiOptions = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setParentActivityName(@NonNull String value) {
+        parentActivityName = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setTaskAffinity(@NonNull String value) {
+        taskAffinity = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setPrivateFlags( int value) {
+        privateFlags = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setLaunchMode( int value) {
+        launchMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setDocumentLaunchMode( int value) {
+        documentLaunchMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setMaxRecents( int value) {
+        maxRecents = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setConfigChanges( int value) {
+        configChanges = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setSoftInputMode( int value) {
+        softInputMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setPersistableMode( int value) {
+        persistableMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setLockTaskLaunchMode( int value) {
+        lockTaskLaunchMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setScreenOrientation( int value) {
+        screenOrientation = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setResizeMode( int value) {
+        resizeMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setMaxAspectRatio( float value) {
+        maxAspectRatio = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setMinAspectRatio( float value) {
+        minAspectRatio = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setSupportsSizeChanges( boolean value) {
+        supportsSizeChanges = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setRequestedVrComponent(@NonNull String value) {
+        requestedVrComponent = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setRotationAnimation( int value) {
+        rotationAnimation = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setColorMode( int value) {
+        colorMode = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setWindowLayout(@NonNull ActivityInfo.WindowLayout value) {
+        windowLayout = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1630600615936L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java",
+            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAlias(java.lang.String,android.content.pm.parsing.component.ParsedActivity)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedActivity]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index dc7eb80..45241b0 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -22,8 +22,10 @@
 import static android.content.pm.parsing.component.ComponentParseUtils.flag;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageUtils;
@@ -79,34 +81,32 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
-            boolean useRoundIcon, ParseInput input)
+            boolean useRoundIcon, @Nullable String defaultSplitName, ParseInput input)
             throws XmlPullParserException, IOException {
         final String packageName = pkg.getPackageName();
-        final ParsedActivity
-                activity = new ParsedActivity();
+        final ParsedActivityImpl activity = new ParsedActivityImpl();
 
         boolean receiver = "receiver".equals(parser.getName());
         String tag = "<" + parser.getName() + ">";
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
         try {
-            ParseResult<ParsedActivity> result =
-                    ParsedMainComponentUtils.parseMainComponent(
-                    activity, tag, separateProcesses,
-                    pkg, sa, flags, useRoundIcon, input,
-                    R.styleable.AndroidManifestActivity_banner,
-                    R.styleable.AndroidManifestActivity_description,
-                    R.styleable.AndroidManifestActivity_directBootAware,
-                    R.styleable.AndroidManifestActivity_enabled,
-                    R.styleable.AndroidManifestActivity_icon,
-                    R.styleable.AndroidManifestActivity_label,
-                    R.styleable.AndroidManifestActivity_logo,
-                    R.styleable.AndroidManifestActivity_name,
-                    R.styleable.AndroidManifestActivity_process,
-                    R.styleable.AndroidManifestActivity_roundIcon,
-                    R.styleable.AndroidManifestActivity_splitName,
-                    R.styleable.AndroidManifestActivity_attributionTags);
+            ParseResult<ParsedActivityImpl> result =
+                    ParsedMainComponentUtils.parseMainComponent(activity, tag, separateProcesses,
+                            pkg, sa, flags, useRoundIcon, defaultSplitName, input,
+                            R.styleable.AndroidManifestActivity_banner,
+                            R.styleable.AndroidManifestActivity_description,
+                            R.styleable.AndroidManifestActivity_directBootAware,
+                            R.styleable.AndroidManifestActivity_enabled,
+                            R.styleable.AndroidManifestActivity_icon,
+                            R.styleable.AndroidManifestActivity_label,
+                            R.styleable.AndroidManifestActivity_logo,
+                            R.styleable.AndroidManifestActivity_name,
+                            R.styleable.AndroidManifestActivity_process,
+                            R.styleable.AndroidManifestActivity_roundIcon,
+                            R.styleable.AndroidManifestActivity_splitName,
+                            R.styleable.AndroidManifestActivity_attributionTags);
             if (result.isError()) {
-                return result;
+                return input.error(result);
             }
 
             if (receiver && pkg.isCantSaveState()) {
@@ -227,8 +227,8 @@
 
     @NonNull
     public static ParseResult<ParsedActivity> parseActivityAlias(ParsingPackage pkg, Resources res,
-            XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
-            throws XmlPullParserException, IOException {
+            XmlResourceParser parser, boolean useRoundIcon, @Nullable String defaultSplitName,
+            @NonNull ParseInput input) throws XmlPullParserException, IOException {
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivityAlias);
         try {
             String targetActivity = sa.getNonConfigurationString(
@@ -263,11 +263,11 @@
                         + ", parsedActivities = " + activities);
             }
 
-            ParsedActivity activity = ParsedActivity.makeAlias(targetActivity, target);
+            ParsedActivityImpl activity = ParsedActivityImpl.makeAlias(targetActivity, target);
             String tag = "<" + parser.getName() + ">";
 
-            ParseResult<ParsedActivity> result = ParsedMainComponentUtils.parseMainComponent(
-                    activity, tag, null, pkg, sa, 0, useRoundIcon, input,
+            ParseResult<ParsedActivityImpl> result = ParsedMainComponentUtils.parseMainComponent(
+                    activity, tag, null, pkg, sa, 0, useRoundIcon, defaultSplitName, input,
                     R.styleable.AndroidManifestActivityAlias_banner,
                     R.styleable.AndroidManifestActivityAlias_description,
                     NOT_SET /*directBootAwareAttr*/,
@@ -281,7 +281,7 @@
                     NOT_SET /*splitNameAttr*/,
                     R.styleable.AndroidManifestActivityAlias_attributionTags);
             if (result.isError()) {
-                return result;
+                return input.error(result);
             }
 
             // TODO add visibleToInstantApps attribute to activity alias
@@ -308,7 +308,7 @@
      * type of logic.
      */
     @NonNull
-    private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivity activity,
+    private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivityImpl activity,
             ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
             TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
             ParseInput input, int parentActivityNameAttr, int permissionAttr,
@@ -354,15 +354,16 @@
                 ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
                         !isReceiver, visibleToEphemeral, resources, parser, input);
                 if (intentResult.isSuccess()) {
-                    ParsedIntentInfo intent = intentResult.getResult();
-                    if (intent != null) {
-                        activity.setOrder(Math.max(intent.getOrder(), activity.getOrder()));
-                        activity.addIntent(intent);
+                    ParsedIntentInfo intentInfo = intentResult.getResult();
+                    if (intentInfo != null) {
+                        IntentFilter intentFilter = intentInfo.getIntentFilter();
+                        activity.setOrder(Math.max(intentFilter.getOrder(), activity.getOrder()));
+                        activity.addIntent(intentInfo);
                         if (LOG_UNSAFE_BROADCASTS && isReceiver
                                 && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
-                            int actionCount = intent.countActions();
+                            int actionCount = intentFilter.countActions();
                             for (int i = 0; i < actionCount; i++) {
-                                final String action = intent.getAction(i);
+                                final String action = intentFilter.getAction(i);
                                 if (action == null || !action.startsWith("android.")) {
                                     continue;
                                 }
@@ -446,7 +447,7 @@
 
     @NonNull
     private static ParseResult<ParsedIntentInfo> parseIntentFilter(ParsingPackage pkg,
-            ParsedActivity activity, boolean allowImplicitEphemeralVisibility,
+            ParsedActivityImpl activity, boolean allowImplicitEphemeralVisibility,
             boolean visibleToEphemeral, Resources resources, XmlResourceParser parser,
             ParseInput input) throws IOException, XmlPullParserException {
         ParseResult<ParsedIntentInfo> result = ParsedMainComponentUtils.parseIntentFilter(activity,
@@ -459,10 +460,11 @@
 
         ParsedIntentInfo intent = result.getResult();
         if (intent != null) {
-            if (intent.isVisibleToInstantApp()) {
+            final IntentFilter intentFilter = intent.getIntentFilter();
+            if (intentFilter.isVisibleToInstantApp()) {
                 activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
             }
-            if (intent.isImplicitlyVisibleToInstantApp()) {
+            if (intentFilter.isImplicitlyVisibleToInstantApp()) {
                 activity.setFlags(
                         activity.getFlags() | ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP);
             }
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
index db3a1c4..ac7a928 100644
--- a/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
@@ -34,255 +34,26 @@
  *
  * @hide
  */
-@DataClass(genAidl = false, genSetters = true, genBuilder = false)
-public class ParsedAttribution implements Parcelable {
-    /** Maximum length of attribution tag */
-    public static final int MAX_ATTRIBUTION_TAG_LEN = 50;
-
-    /** Maximum amount of attributions per package */
-    private static final int MAX_NUM_ATTRIBUTIONS = 10000;
-
-    /** Tag of the attribution */
-    private @NonNull String tag;
-
-    /** User visible label fo the attribution */
-    private @StringRes int label;
-
-    /** Ids of previously declared attributions this attribution inherits from */
-    private @NonNull List<String> inheritFrom;
-
-    public ParsedAttribution() {}
+public interface ParsedAttribution extends Parcelable {
 
     /**
-     * @return Is this set of attributions a valid combination for a single package?
+     * Maximum length of attribution tag
+     * @hide
      */
-    public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) {
-        if (attributions == null) {
-            return true;
-        }
-
-        ArraySet<String> attributionTags = new ArraySet<>(attributions.size());
-        ArraySet<String> inheritFromAttributionTags = new ArraySet<>();
-
-        int numAttributions = attributions.size();
-        if (numAttributions > MAX_NUM_ATTRIBUTIONS) {
-            return false;
-        }
-
-        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
-            boolean wasAdded = attributionTags.add(attributions.get(attributionNum).tag);
-            if (!wasAdded) {
-                // feature id is not unique
-                return false;
-            }
-        }
-
-        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
-            ParsedAttribution feature = attributions.get(attributionNum);
-
-            int numInheritFrom = feature.inheritFrom.size();
-            for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
-                String inheritFrom = feature.inheritFrom.get(inheritFromNum);
-
-                if (attributionTags.contains(inheritFrom)) {
-                    // Cannot inherit from a attribution that is still defined
-                    return false;
-                }
-
-                boolean wasAdded = inheritFromAttributionTags.add(inheritFrom);
-                if (!wasAdded) {
-                    // inheritFrom is not unique
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @android.annotation.IntDef(prefix = "MAX_", value = {
-        MAX_ATTRIBUTION_TAG_LEN,
-        MAX_NUM_ATTRIBUTIONS
-    })
-    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
-    @DataClass.Generated.Member
-    public @interface Max {}
-
-    @DataClass.Generated.Member
-    public static String maxToString(@Max int value) {
-        switch (value) {
-            case MAX_ATTRIBUTION_TAG_LEN:
-                    return "MAX_ATTRIBUTION_TAG_LEN";
-            case MAX_NUM_ATTRIBUTIONS:
-                    return "MAX_NUM_ATTRIBUTIONS";
-            default: return Integer.toHexString(value);
-        }
-    }
-
-    /**
-     * Creates a new ParsedAttribution.
-     *
-     * @param tag
-     *   Tag of the attribution
-     * @param label
-     *   User visible label fo the attribution
-     * @param inheritFrom
-     *   Ids of previously declared attributions this attribution inherits from
-     */
-    @DataClass.Generated.Member
-    public ParsedAttribution(
-            @NonNull String tag,
-            @StringRes int label,
-            @NonNull List<String> inheritFrom) {
-        this.tag = tag;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, tag);
-        this.label = label;
-        com.android.internal.util.AnnotationValidations.validate(
-                StringRes.class, null, label);
-        this.inheritFrom = inheritFrom;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, inheritFrom);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    /**
-     * Tag of the attribution
-     */
-    @DataClass.Generated.Member
-    public @NonNull String getTag() {
-        return tag;
-    }
-
-    /**
-     * User visible label fo the attribution
-     */
-    @DataClass.Generated.Member
-    public @StringRes int getLabel() {
-        return label;
-    }
+    int MAX_ATTRIBUTION_TAG_LEN = 50;
 
     /**
      * Ids of previously declared attributions this attribution inherits from
      */
-    @DataClass.Generated.Member
-    public @NonNull List<String> getInheritFrom() {
-        return inheritFrom;
-    }
+    @NonNull List<String> getInheritFrom();
+
+    /**
+     * User visible label for the attribution
+     */
+    @StringRes int getLabel();
 
     /**
      * Tag of the attribution
      */
-    @DataClass.Generated.Member
-    public @NonNull ParsedAttribution setTag(@NonNull String value) {
-        tag = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, tag);
-        return this;
-    }
-
-    /**
-     * User visible label fo the attribution
-     */
-    @DataClass.Generated.Member
-    public @NonNull ParsedAttribution setLabel(@StringRes int value) {
-        label = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                StringRes.class, null, label);
-        return this;
-    }
-
-    /**
-     * Ids of previously declared attributions this attribution inherits from
-     */
-    @DataClass.Generated.Member
-    public @NonNull ParsedAttribution setInheritFrom(@NonNull List<String> value) {
-        inheritFrom = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, inheritFrom);
-        return this;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeString(tag);
-        dest.writeInt(label);
-        dest.writeStringList(inheritFrom);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedAttribution(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        String _tag = in.readString();
-        int _label = in.readInt();
-        List<String> _inheritFrom = new ArrayList<>();
-        in.readStringList(_inheritFrom);
-
-        this.tag = _tag;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, tag);
-        this.label = _label;
-        com.android.internal.util.AnnotationValidations.validate(
-                StringRes.class, null, label);
-        this.inheritFrom = _inheritFrom;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, inheritFrom);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ParsedAttribution> CREATOR
-            = new Parcelable.Creator<ParsedAttribution>() {
-        @Override
-        public ParsedAttribution[] newArray(int size) {
-            return new ParsedAttribution[size];
-        }
-
-        @Override
-        public ParsedAttribution createFromParcel(@NonNull Parcel in) {
-            return new ParsedAttribution(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1624050667337L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
-            inputSignatures = "public static final  int MAX_ATTRIBUTION_TAG_LEN\nprivate static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
+    @NonNull String getTag();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java b/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java
new file mode 100644
index 0000000..510425f
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestAttribution &lt;attribution&gt;} tag parsed from the
+ * manifest.
+ *
+ * @hide
+ */
+@DataClass(genAidl = false, genSetters = true, genBuilder = false, genParcelable = true)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedAttributionImpl implements ParsedAttribution {
+
+    /** Maximum amount of attributions per package */
+    static final int MAX_NUM_ATTRIBUTIONS = 10000;
+
+    /** Tag of the attribution */
+    private @NonNull String tag;
+
+    /** User visible label fo the attribution */
+    private @StringRes int label;
+
+    /** Ids of previously declared attributions this attribution inherits from */
+    private @NonNull List<String> inheritFrom;
+
+    public ParsedAttributionImpl() {}
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new ParsedAttributionImpl.
+     *
+     * @param tag
+     *   Tag of the attribution
+     * @param label
+     *   User visible label fo the attribution
+     * @param inheritFrom
+     *   Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public ParsedAttributionImpl(
+            @NonNull String tag,
+            @StringRes int label,
+            @NonNull List<String> inheritFrom) {
+        this.tag = tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        this.label = label;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        this.inheritFrom = inheritFrom;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Tag of the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getTag() {
+        return tag;
+    }
+
+    /**
+     * User visible label fo the attribution
+     */
+    @DataClass.Generated.Member
+    public @StringRes int getLabel() {
+        return label;
+    }
+
+    /**
+     * Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getInheritFrom() {
+        return inheritFrom;
+    }
+
+    /**
+     * Tag of the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttributionImpl setTag(@NonNull String value) {
+        tag = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        return this;
+    }
+
+    /**
+     * User visible label fo the attribution
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttributionImpl setLabel(@StringRes int value) {
+        label = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        return this;
+    }
+
+    /**
+     * Ids of previously declared attributions this attribution inherits from
+     */
+    @DataClass.Generated.Member
+    public @NonNull ParsedAttributionImpl setInheritFrom(@NonNull List<String> value) {
+        inheritFrom = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(tag);
+        dest.writeInt(label);
+        dest.writeStringList(inheritFrom);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedAttributionImpl(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _tag = in.readString();
+        int _label = in.readInt();
+        List<String> _inheritFrom = new ArrayList<>();
+        in.readStringList(_inheritFrom);
+
+        this.tag = _tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, tag);
+        this.label = _label;
+        com.android.internal.util.AnnotationValidations.validate(
+                StringRes.class, null, label);
+        this.inheritFrom = _inheritFrom;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, inheritFrom);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedAttributionImpl> CREATOR
+            = new Parcelable.Creator<ParsedAttributionImpl>() {
+        @Override
+        public ParsedAttributionImpl[] newArray(int size) {
+            return new ParsedAttributionImpl[size];
+        }
+
+        @Override
+        public ParsedAttributionImpl createFromParcel(@NonNull Parcel in) {
+            return new ParsedAttributionImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627594502974L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java",
+            inputSignatures = "static final  int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedAttribution]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
index dccc49a..84f1d44 100644
--- a/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
@@ -17,11 +17,13 @@
 package android.content.pm.parsing.component;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.util.ArraySet;
 
 import com.android.internal.R;
 
@@ -106,6 +108,54 @@
             ((ArrayList) inheritFrom).trimToSize();
         }
 
-        return input.success(new ParsedAttribution(attributionTag, label, inheritFrom));
+        return input.success(new ParsedAttributionImpl(attributionTag, label, inheritFrom));
+    }
+
+    /**
+     * @return Is this set of attributions a valid combination for a single package?
+     */
+    public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) {
+        if (attributions == null) {
+            return true;
+        }
+
+        ArraySet<String> attributionTags = new ArraySet<>(attributions.size());
+        ArraySet<String> inheritFromAttributionTags = new ArraySet<>();
+
+        int numAttributions = attributions.size();
+        if (numAttributions > ParsedAttributionImpl.MAX_NUM_ATTRIBUTIONS) {
+            return false;
+        }
+
+        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+            boolean wasAdded = attributionTags.add(attributions.get(attributionNum).getTag());
+            if (!wasAdded) {
+                // feature id is not unique
+                return false;
+            }
+        }
+
+        for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+            ParsedAttribution feature = attributions.get(attributionNum);
+
+            final List<String> inheritFromList = feature.getInheritFrom();
+            int numInheritFrom = inheritFromList.size();
+            for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
+                String inheritFrom = inheritFromList.get(inheritFromNum);
+
+                if (attributionTags.contains(inheritFrom)) {
+                    // Cannot inherit from a attribution that is still defined
+                    return false;
+                }
+
+                boolean wasAdded = inheritFromAttributionTags.add(inheritFrom);
+                if (!wasAdded) {
+                    // inheritFrom is not unique
+                    return false;
+                }
+            }
+        }
+
+        return true;
     }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
index 838adfd..c1372f6 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java
@@ -16,251 +16,49 @@
 
 package android.content.pm.parsing.component;
 
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import static java.util.Collections.emptyMap;
-
-import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageManager.Property;
 import android.os.Bundle;
-import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
 
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 /** @hide */
-public abstract class ParsedComponent implements Parcelable {
+public interface ParsedComponent extends Parcelable {
 
-    private static final ParsedIntentInfo.ListParceler sForIntentInfos =
-            Parcelling.Cache.getOrCreate(ParsedIntentInfo.ListParceler.class);
+    int getBanner();
 
     @NonNull
-    @DataClass.ParcelWith(ForInternedString.class)
-    protected String name;
-    protected int icon;
-    protected int labelRes;
-    @Nullable
-    protected CharSequence nonLocalizedLabel;
-    protected int logo;
-    protected int banner;
-    protected int descriptionRes;
+    ComponentName getComponentName();
 
-    // TODO(b/135203078): Replace flags with individual booleans, scoped by subclass
-    protected int flags;
+    int getDescriptionRes();
+
+    int getFlags();
+
+    int getIcon();
 
     @NonNull
-    @DataClass.ParcelWith(ForInternedString.class)
-    protected String packageName;
+    List<ParsedIntentInfo> getIntents();
+
+    int getLabelRes();
+
+    int getLogo();
 
     @Nullable
-    @DataClass.PluralOf("intent")
-    @DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
-    protected List<ParsedIntentInfo> intents;
+    Bundle getMetaData();
 
-    protected ComponentName componentName;
+    @NonNull
+    String getName();
 
     @Nullable
-    protected Bundle metaData;
-
-    protected Map<String, Property> mProperties = emptyMap();
-
-    ParsedComponent() {
-
-    }
-
-    @SuppressWarnings("IncompleteCopyConstructor")
-    public ParsedComponent(ParsedComponent other) {
-        this.metaData = other.metaData;
-        this.name = other.name;
-        this.icon = other.getIcon();
-        this.labelRes = other.getLabelRes();
-        this.nonLocalizedLabel = other.getNonLocalizedLabel();
-        this.logo = other.getLogo();
-        this.banner = other.getBanner();
-
-        this.descriptionRes = other.getDescriptionRes();
-
-        this.flags = other.getFlags();
-
-        this.setPackageName(other.packageName);
-        this.intents = new ArrayList<>(other.getIntents());
-    }
-
-    public void addIntent(ParsedIntentInfo intent) {
-        this.intents = CollectionUtils.add(this.intents, intent);
-    }
-
-    /** Add a property to the component */
-    public void addProperty(@NonNull Property property) {
-        this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
-    }
+    CharSequence getNonLocalizedLabel();
 
     @NonNull
-    public List<ParsedIntentInfo> getIntents() {
-        return intents != null ? intents : Collections.emptyList();
-    }
-
-    public ParsedComponent setBanner(int banner) {
-        this.banner = banner;
-        return this;
-    }
-
-    public ParsedComponent setDescriptionRes(int descriptionRes) {
-        this.descriptionRes = descriptionRes;
-        return this;
-    }
-
-    public ParsedComponent setFlags(int flags) {
-        this.flags = flags;
-        return this;
-    }
-
-    public ParsedComponent setIcon(int icon) {
-        this.icon = icon;
-        return this;
-    }
-
-    public ParsedComponent setLabelRes(int labelRes) {
-        this.labelRes = labelRes;
-        return this;
-    }
-
-    public ParsedComponent setLogo(int logo) {
-        this.logo = logo;
-        return this;
-    }
-
-    public ParsedComponent setMetaData(Bundle metaData) {
-        this.metaData = metaData;
-        return this;
-    }
-
-    public ParsedComponent setName(String name) {
-        this.name = TextUtils.safeIntern(name);
-        return this;
-    }
-
-    public ParsedComponent setNonLocalizedLabel(CharSequence nonLocalizedLabel) {
-        this.nonLocalizedLabel = nonLocalizedLabel;
-        return this;
-    }
-
-    @CallSuper
-    public void setPackageName(@NonNull String packageName) {
-        this.packageName = TextUtils.safeIntern(packageName);
-        //noinspection ConstantConditions
-        this.componentName = null;
-
-        // Note: this method does not edit name (which can point to a class), because this package
-        // name change is not changing the package in code, but the identifier used by the system.
-    }
+    String getPackageName();
 
     @NonNull
-    public ComponentName getComponentName() {
-        if (componentName == null) {
-            componentName = new ComponentName(getPackageName(), getName());
-        }
-        return componentName;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(this.name);
-        dest.writeInt(this.getIcon());
-        dest.writeInt(this.getLabelRes());
-        dest.writeCharSequence(this.getNonLocalizedLabel());
-        dest.writeInt(this.getLogo());
-        dest.writeInt(this.getBanner());
-        dest.writeInt(this.getDescriptionRes());
-        dest.writeInt(this.getFlags());
-        sForInternedString.parcel(this.packageName, dest, flags);
-        sForIntentInfos.parcel(this.getIntents(), dest, flags);
-        dest.writeBundle(this.metaData);
-        dest.writeMap(this.mProperties);
-    }
-
-    protected ParsedComponent(Parcel in) {
-        // We use the boot classloader for all classes that we load.
-        final ClassLoader boot = Object.class.getClassLoader();
-        //noinspection ConstantConditions
-        this.name = in.readString();
-        this.icon = in.readInt();
-        this.labelRes = in.readInt();
-        this.nonLocalizedLabel = in.readCharSequence();
-        this.logo = in.readInt();
-        this.banner = in.readInt();
-        this.descriptionRes = in.readInt();
-        this.flags = in.readInt();
-        //noinspection ConstantConditions
-        this.packageName = sForInternedString.unparcel(in);
-        this.intents = sForIntentInfos.unparcel(in);
-        this.metaData = in.readBundle(boot);
-        this.mProperties = in.readHashMap(boot);
-    }
-
-    @NonNull
-    public String getName() {
-        return name;
-    }
-
-    public int getIcon() {
-        return icon;
-    }
-
-    public int getLabelRes() {
-        return labelRes;
-    }
-
-    @Nullable
-    public CharSequence getNonLocalizedLabel() {
-        return nonLocalizedLabel;
-    }
-
-    public int getLogo() {
-        return logo;
-    }
-
-    public int getBanner() {
-        return banner;
-    }
-
-    public int getDescriptionRes() {
-        return descriptionRes;
-    }
-
-    public int getFlags() {
-        return flags;
-    }
-
-    @NonNull
-    public String getPackageName() {
-        return packageName;
-    }
-
-    @Nullable
-    public Bundle getMetaData() {
-        return metaData;
-    }
-
-    @NonNull
-    public Map<String, Property> getProperties() {
-        return mProperties;
-    }
+    Map<String, Property> getProperties();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java b/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java
new file mode 100644
index 0000000..1c46a10
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import static java.util.Collections.emptyMap;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PackageManager.Property;
+import android.content.pm.parsing.ParsingUtils;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genConstructor = false, genBuilder = false)
+@DataClass.Suppress({"setComponentName", "setProperties", "setIntents"})
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public abstract class ParsedComponentImpl implements ParsedComponent {
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String name;
+    private int icon;
+    private int labelRes;
+    @Nullable
+    private CharSequence nonLocalizedLabel;
+    private int logo;
+    private int banner;
+    private int descriptionRes;
+
+    // TODO(b/135203078): Replace flags with individual booleans, scoped by subclass
+    private int flags;
+
+    @NonNull
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String packageName;
+
+    @NonNull
+    @DataClass.PluralOf("intent")
+    private List<ParsedIntentInfo> intents = Collections.emptyList();
+
+    @Nullable
+    private ComponentName componentName;
+
+    @Nullable
+    private Bundle metaData;
+
+    @NonNull
+    private Map<String, Property> mProperties = emptyMap();
+
+    public ParsedComponentImpl() {
+
+    }
+
+    protected ParsedComponentImpl(ParsedComponent other) {
+        this.metaData = other.getMetaData();
+        this.name = other.getName();
+        this.icon = other.getIcon();
+        this.labelRes = other.getLabelRes();
+        this.nonLocalizedLabel = other.getNonLocalizedLabel();
+        this.logo = other.getLogo();
+        this.banner = other.getBanner();
+        this.descriptionRes = other.getDescriptionRes();
+        this.flags = other.getFlags();
+        this.packageName = other.getPackageName();
+        this.componentName = other.getComponentName();
+        this.intents = new ArrayList<>(other.getIntents());
+        this.mProperties = new ArrayMap<>();
+        this.mProperties.putAll(other.getProperties());
+    }
+
+    public void addIntent(ParsedIntentInfo intent) {
+        this.intents = CollectionUtils.add(this.intents, intent);
+    }
+
+    /** Add a property to the component */
+    public void addProperty(@NonNull Property property) {
+        this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
+    }
+
+    public ParsedComponentImpl setName(String name) {
+        this.name = TextUtils.safeIntern(name);
+        return this;
+    }
+
+    @CallSuper
+    public void setPackageName(@NonNull String packageName) {
+        this.packageName = TextUtils.safeIntern(packageName);
+        //noinspection ConstantConditions
+        this.componentName = null;
+
+        // Note: this method does not edit name (which can point to a class), because this package
+        // name change is not changing the package in code, but the identifier used by the system.
+    }
+
+    @Override
+    @NonNull
+    public ComponentName getComponentName() {
+        if (componentName == null) {
+            componentName = new ComponentName(getPackageName(), getName());
+        }
+        return componentName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.name);
+        dest.writeInt(this.getIcon());
+        dest.writeInt(this.getLabelRes());
+        dest.writeCharSequence(this.getNonLocalizedLabel());
+        dest.writeInt(this.getLogo());
+        dest.writeInt(this.getBanner());
+        dest.writeInt(this.getDescriptionRes());
+        dest.writeInt(this.getFlags());
+        sForInternedString.parcel(this.packageName, dest, flags);
+        dest.writeTypedList(this.getIntents());
+        dest.writeBundle(this.metaData);
+        dest.writeMap(this.mProperties);
+    }
+
+    protected ParsedComponentImpl(Parcel in) {
+        // We use the boot classloader for all classes that we load.
+        final ClassLoader boot = Object.class.getClassLoader();
+        //noinspection ConstantConditions
+        this.name = in.readString();
+        this.icon = in.readInt();
+        this.labelRes = in.readInt();
+        this.nonLocalizedLabel = in.readCharSequence();
+        this.logo = in.readInt();
+        this.banner = in.readInt();
+        this.descriptionRes = in.readInt();
+        this.flags = in.readInt();
+        //noinspection ConstantConditions
+        this.packageName = sForInternedString.unparcel(in);
+        this.intents = ParsingUtils.createTypedInterfaceList(in, ParsedIntentInfoImpl.CREATOR);
+        this.metaData = in.readBundle(boot);
+        this.mProperties = in.readHashMap(boot);
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public int getIcon() {
+        return icon;
+    }
+
+    @DataClass.Generated.Member
+    public int getLabelRes() {
+        return labelRes;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getNonLocalizedLabel() {
+        return nonLocalizedLabel;
+    }
+
+    @DataClass.Generated.Member
+    public int getLogo() {
+        return logo;
+    }
+
+    @DataClass.Generated.Member
+    public int getBanner() {
+        return banner;
+    }
+
+    @DataClass.Generated.Member
+    public int getDescriptionRes() {
+        return descriptionRes;
+    }
+
+    @DataClass.Generated.Member
+    public int getFlags() {
+        return flags;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return packageName;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull List<ParsedIntentInfo> getIntents() {
+        return intents;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable Bundle getMetaData() {
+        return metaData;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Map<String,Property> getProperties() {
+        return mProperties;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setIcon( int value) {
+        icon = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setLabelRes( int value) {
+        labelRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setNonLocalizedLabel(@NonNull CharSequence value) {
+        nonLocalizedLabel = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setLogo( int value) {
+        logo = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setBanner( int value) {
+        banner = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setDescriptionRes( int value) {
+        descriptionRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setFlags( int value) {
+        flags = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedComponentImpl setMetaData(@NonNull Bundle value) {
+        metaData = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627680195484L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java",
+            inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate  int icon\nprivate  int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate  int logo\nprivate  int banner\nprivate  int descriptionRes\nprivate  int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<android.content.pm.parsing.component.ParsedIntentInfo> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\npublic  void addIntent(android.content.pm.parsing.component.ParsedIntentInfo)\npublic  void addProperty(android.content.pm.PackageManager.Property)\npublic  android.content.pm.parsing.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedComponent]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index 6d798fd..5c33cfd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -40,7 +40,7 @@
 
     @NonNull
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    static <Component extends ParsedComponent> ParseResult<Component> parseComponent(
+    static <Component extends ParsedComponentImpl> ParseResult<Component> parseComponent(
             Component component, String tag, ParsingPackage pkg, TypedArray array,
             boolean useRoundIcon, ParseInput input, int bannerAttr, int descriptionAttr,
             int iconAttr, int labelAttr, int logoAttr, int nameAttr, int roundIconAttr) {
@@ -96,7 +96,7 @@
         return input.success(component);
     }
 
-    static ParseResult<Bundle> addMetaData(ParsedComponent component, ParsingPackage pkg,
+    static ParseResult<Bundle> addMetaData(ParsedComponentImpl component, ParsingPackage pkg,
             Resources resources, XmlResourceParser parser, ParseInput input) {
         ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component,
                 resources, parser, "<meta-data>", input);
@@ -110,7 +110,7 @@
         return input.success(component.getMetaData());
     }
 
-    static ParseResult<Property> addProperty(ParsedComponent component, ParsingPackage pkg,
+    static ParseResult<Property> addProperty(ParsedComponentImpl component, ParsingPackage pkg,
             Resources resources, XmlResourceParser parser, ParseInput input) {
         ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component,
                 resources, parser, "<property>", input);
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
index 4178920..e8fcc00 100644
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
@@ -16,114 +16,18 @@
 
 package android.content.pm.parsing.component;
 
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
 /** @hide */
-public class ParsedInstrumentation extends ParsedComponent {
+public interface ParsedInstrumentation extends ParsedComponent {
 
     @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String targetPackage;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String targetProcesses;
-    private boolean handleProfiling;
-    private boolean functionalTest;
-
-    public ParsedInstrumentation() {
-    }
-
-    public ParsedInstrumentation setFunctionalTest(boolean functionalTest) {
-        this.functionalTest = functionalTest;
-        return this;
-    }
-
-    public ParsedInstrumentation setHandleProfiling(boolean handleProfiling) {
-        this.handleProfiling = handleProfiling;
-        return this;
-    }
-
-    public ParsedInstrumentation setTargetPackage(@Nullable String targetPackage) {
-        this.targetPackage = TextUtils.safeIntern(targetPackage);
-        return this;
-    }
-
-    public ParsedInstrumentation setTargetProcesses(@Nullable String targetProcesses) {
-        this.targetProcesses = TextUtils.safeIntern(targetProcesses);
-        return this;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Instrumentation{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        ComponentName.appendShortString(sb, getPackageName(), getName());
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        sForInternedString.parcel(this.targetPackage, dest, flags);
-        sForInternedString.parcel(this.targetProcesses, dest, flags);
-        dest.writeBoolean(this.handleProfiling);
-        dest.writeBoolean(this.functionalTest);
-    }
-
-    protected ParsedInstrumentation(Parcel in) {
-        super(in);
-        this.targetPackage = sForInternedString.unparcel(in);
-        this.targetProcesses = sForInternedString.unparcel(in);
-        this.handleProfiling = in.readByte() != 0;
-        this.functionalTest = in.readByte() != 0;
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedInstrumentation> CREATOR =
-            new Parcelable.Creator<ParsedInstrumentation>() {
-                @Override
-                public ParsedInstrumentation createFromParcel(Parcel source) {
-                    return new ParsedInstrumentation(source);
-                }
-
-                @Override
-                public ParsedInstrumentation[] newArray(int size) {
-                    return new ParsedInstrumentation[size];
-                }
-            };
+    String getTargetPackage();
 
     @Nullable
-    public String getTargetPackage() {
-        return targetPackage;
-    }
+    String getTargetProcesses();
 
-    @Nullable
-    public String getTargetProcesses() {
-        return targetProcesses;
-    }
+    boolean isFunctionalTest();
 
-    public boolean isHandleProfiling() {
-        return handleProfiling;
-    }
-
-    public boolean isFunctionalTest() {
-        return functionalTest;
-    }
+    boolean isHandleProfiling();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java
new file mode 100644
index 0000000..d2b5368
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedInstrumentationImpl extends ParsedComponentImpl implements
+        ParsedInstrumentation {
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetPackage;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String targetProcesses;
+    private boolean handleProfiling;
+    private boolean functionalTest;
+
+    public ParsedInstrumentationImpl() {
+    }
+
+    public ParsedInstrumentationImpl setTargetPackage(@Nullable String targetPackage) {
+        this.targetPackage = TextUtils.safeIntern(targetPackage);
+        return this;
+    }
+
+    public ParsedInstrumentationImpl setTargetProcesses(@Nullable String targetProcesses) {
+        this.targetProcesses = TextUtils.safeIntern(targetProcesses);
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Instrumentation{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        sForInternedString.parcel(this.targetPackage, dest, flags);
+        sForInternedString.parcel(this.targetProcesses, dest, flags);
+        dest.writeBoolean(this.handleProfiling);
+        dest.writeBoolean(this.functionalTest);
+    }
+
+    protected ParsedInstrumentationImpl(Parcel in) {
+        super(in);
+        this.targetPackage = sForInternedString.unparcel(in);
+        this.targetProcesses = sForInternedString.unparcel(in);
+        this.handleProfiling = in.readByte() != 0;
+        this.functionalTest = in.readByte() != 0;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedInstrumentationImpl> CREATOR =
+            new Parcelable.Creator<ParsedInstrumentationImpl>() {
+                @Override
+                public ParsedInstrumentationImpl createFromParcel(Parcel source) {
+                    return new ParsedInstrumentationImpl(source);
+                }
+
+                @Override
+                public ParsedInstrumentationImpl[] newArray(int size) {
+                    return new ParsedInstrumentationImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedInstrumentationImpl(
+            @Nullable String targetPackage,
+            @Nullable String targetProcesses,
+            boolean handleProfiling,
+            boolean functionalTest) {
+        this.targetPackage = targetPackage;
+        this.targetProcesses = targetProcesses;
+        this.handleProfiling = handleProfiling;
+        this.functionalTest = functionalTest;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getTargetPackage() {
+        return targetPackage;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getTargetProcesses() {
+        return targetProcesses;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isHandleProfiling() {
+        return handleProfiling;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isFunctionalTest() {
+        return functionalTest;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedInstrumentationImpl setHandleProfiling( boolean value) {
+        handleProfiling = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedInstrumentationImpl setFunctionalTest( boolean value) {
+        functionalTest = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627595809880L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate  boolean handleProfiling\nprivate  boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedInstrumentationImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedInstrumentation]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
index f122fd6..df5e73e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
@@ -39,13 +39,13 @@
     public static ParseResult<ParsedInstrumentation> parseInstrumentation(ParsingPackage pkg,
             Resources res, XmlResourceParser parser, boolean useRoundIcon,
             ParseInput input) throws IOException, XmlPullParserException {
-        ParsedInstrumentation
-                instrumentation = new ParsedInstrumentation();
+        ParsedInstrumentationImpl
+                instrumentation = new ParsedInstrumentationImpl();
         String tag = "<" + parser.getName() + ">";
 
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation);
         try {
-            ParseResult<ParsedInstrumentation> result = ParsedComponentUtils.parseComponent(
+            ParseResult<ParsedInstrumentationImpl> result = ParsedComponentUtils.parseComponent(
                     instrumentation, tag, pkg, sa, useRoundIcon, input,
                     R.styleable.AndroidManifestInstrumentation_banner,
                     NOT_SET /*descriptionAttr*/,
@@ -55,7 +55,7 @@
                     R.styleable.AndroidManifestInstrumentation_name,
                     R.styleable.AndroidManifestInstrumentation_roundIcon);
             if (result.isError()) {
-                return result;
+                return input.error(result);
             }
 
             // @formatter:off
@@ -70,7 +70,13 @@
             sa.recycle();
         }
 
-        return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, instrumentation,
-                input);
+        ParseResult<ParsedInstrumentationImpl> result =
+                ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, instrumentation, input);
+
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(result.getResult());
     }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
index 59d4a95..1e36ccca 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -20,188 +20,25 @@
 import android.annotation.Nullable;
 import android.content.IntentFilter;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.Pair;
 
+import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /** @hide **/
-public final class ParsedIntentInfo extends IntentFilter {
+public interface ParsedIntentInfo extends Parcelable {
 
-    public static final Parceler PARCELER = new Parceler();
+    boolean isHasDefault();
 
-    public ParsedIntentInfo setHasDefault(boolean hasDefault) {
-        this.hasDefault = hasDefault;
-        return this;
-    }
+    int getLabelRes();
 
-    public ParsedIntentInfo setIcon(int icon) {
-        this.icon = icon;
-        return this;
-    }
+    @Nullable CharSequence getNonLocalizedLabel();
 
-    public ParsedIntentInfo setLabelRes(int labelRes) {
-        this.labelRes = labelRes;
-        return this;
-    }
+    int getIcon();
 
-    public ParsedIntentInfo setNonLocalizedLabel(CharSequence nonLocalizedLabel) {
-        this.nonLocalizedLabel = nonLocalizedLabel;
-        return this;
-    }
-
-    public static class Parceler implements Parcelling<ParsedIntentInfo> {
-
-        @Override
-        public void parcel(ParsedIntentInfo item, Parcel dest, int parcelFlags) {
-            item.writeIntentInfoToParcel(dest, parcelFlags);
-        }
-
-        @NonNull
-        @Override
-        public ParsedIntentInfo unparcel(Parcel source) {
-            return new ParsedIntentInfo(source);
-        }
-    }
-
-    public static class ListParceler implements Parcelling<List<ParsedIntentInfo>> {
-
-        /**
-         * <p>
-         * Implementation note: The serialized form for the intent list also contains the name
-         * of the concrete class that's stored in the list, and assumes that every element of the
-         * list is of the same type. This is very similar to the original parcelable mechanism.
-         * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable
-         * and is public API. It also declares Parcelable related methods as final which means
-         * we can't extend them. The approach of using composition instead of inheritance leads to
-         * a large set of cascading changes in the PackageManagerService, which seem undesirable.
-         *
-         * <p>
-         * <b>WARNING: </b> The list of objects returned by this function might need to be fixed up
-         * to make sure their owner fields are consistent. See {@code fixupOwner}.
-         */
-        @Override
-        public void parcel(List<ParsedIntentInfo> item, Parcel dest, int parcelFlags) {
-            if (item == null) {
-                dest.writeInt(-1);
-                return;
-            }
-
-            final int size = item.size();
-            dest.writeInt(size);
-
-            for (int index = 0; index < size; index++) {
-                PARCELER.parcel(item.get(index), dest, parcelFlags);
-            }
-        }
-
-        @Override
-        public List<ParsedIntentInfo> unparcel(Parcel source) {
-            int size = source.readInt();
-            if (size == -1) {
-                return null;
-            }
-
-            if (size == 0) {
-                return new ArrayList<>(0);
-            }
-
-            final ArrayList<ParsedIntentInfo> intentsList = new ArrayList<>(size);
-            for (int i = 0; i < size; ++i) {
-                intentsList.add(PARCELER.unparcel(source));
-            }
-
-            return intentsList;
-        }
-    }
-
-    public static class StringPairListParceler implements Parcelling<List<Pair<String, ParsedIntentInfo>>> {
-
-        @Override
-        public void parcel(List<Pair<String, ParsedIntentInfo>> item, Parcel dest,
-                int parcelFlags) {
-            if (item == null) {
-                dest.writeInt(-1);
-                return;
-            }
-
-            final int size = item.size();
-            dest.writeInt(size);
-
-            for (int index = 0; index < size; index++) {
-                Pair<String, ParsedIntentInfo> pair = item.get(index);
-                dest.writeString(pair.first);
-                PARCELER.parcel(pair.second, dest, parcelFlags);
-            }
-        }
-
-        @Override
-        public List<Pair<String, ParsedIntentInfo>> unparcel(Parcel source) {
-            int size = source.readInt();
-            if (size == -1) {
-                return null;
-            }
-
-            if (size == 0) {
-                return new ArrayList<>(0);
-            }
-
-            final List<Pair<String, ParsedIntentInfo>> list = new ArrayList<>(size);
-            for (int i = 0; i < size; ++i) {
-                list.add(Pair.create(source.readString(), PARCELER.unparcel(source)));
-            }
-
-            return list;
-        }
-    }
-
-    private boolean hasDefault;
-    private int labelRes;
-    @Nullable
-    private CharSequence nonLocalizedLabel;
-    private int icon;
-
-    public ParsedIntentInfo() {
-    }
-
-    public ParsedIntentInfo(Parcel in) {
-        super(in);
-        hasDefault = in.readBoolean();
-        labelRes = in.readInt();
-        nonLocalizedLabel = in.readCharSequence();
-        icon = in.readInt();
-    }
-
-    public void writeIntentInfoToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeBoolean(hasDefault);
-        dest.writeInt(labelRes);
-        dest.writeCharSequence(nonLocalizedLabel);
-        dest.writeInt(icon);
-    }
-
-    public String toString() {
-        return "ParsedIntentInfo{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + '}';
-    }
-
-    public boolean isHasDefault() {
-        return hasDefault;
-    }
-
-    public int getLabelRes() {
-        return labelRes;
-    }
-
-    @Nullable
-    public CharSequence getNonLocalizedLabel() {
-        return nonLocalizedLabel;
-    }
-
-    public int getIcon() {
-        return icon;
-    }
+    @NonNull IntentFilter getIntentFilter();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java
new file mode 100644
index 0000000..9ff7a16
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide **/
+@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
+        genBuilder = false, genConstructor = false)
+@DataClass.Suppress({"setIntentFilter"})
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedIntentInfoImpl implements ParsedIntentInfo {
+
+    private boolean mHasDefault;
+    private int mLabelRes;
+    @Nullable
+    private CharSequence mNonLocalizedLabel;
+    private int mIcon;
+
+    @NonNull
+    private IntentFilter mIntentFilter = new IntentFilter();
+
+    public ParsedIntentInfoImpl() {}
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public boolean isHasDefault() {
+        return mHasDefault;
+    }
+
+    @DataClass.Generated.Member
+    public int getLabelRes() {
+        return mLabelRes;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getNonLocalizedLabel() {
+        return mNonLocalizedLabel;
+    }
+
+    @DataClass.Generated.Member
+    public int getIcon() {
+        return mIcon;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull IntentFilter getIntentFilter() {
+        return mIntentFilter;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedIntentInfoImpl setHasDefault( boolean value) {
+        mHasDefault = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedIntentInfoImpl setLabelRes( int value) {
+        mLabelRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedIntentInfoImpl setNonLocalizedLabel(@NonNull CharSequence value) {
+        mNonLocalizedLabel = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedIntentInfoImpl setIcon( int value) {
+        mIcon = value;
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mHasDefault) flg |= 0x1;
+        if (mNonLocalizedLabel != null) flg |= 0x4;
+        dest.writeByte(flg);
+        dest.writeInt(mLabelRes);
+        if (mNonLocalizedLabel != null) dest.writeCharSequence(mNonLocalizedLabel);
+        dest.writeInt(mIcon);
+        dest.writeTypedObject(mIntentFilter, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedIntentInfoImpl(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean hasDefault = (flg & 0x1) != 0;
+        int labelRes = in.readInt();
+        CharSequence nonLocalizedLabel = (flg & 0x4) == 0 ? null : (CharSequence) in.readCharSequence();
+        int icon = in.readInt();
+        IntentFilter intentFilter = (IntentFilter) in.readTypedObject(IntentFilter.CREATOR);
+
+        this.mHasDefault = hasDefault;
+        this.mLabelRes = labelRes;
+        this.mNonLocalizedLabel = nonLocalizedLabel;
+        this.mIcon = icon;
+        this.mIntentFilter = intentFilter;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mIntentFilter);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedIntentInfoImpl> CREATOR
+            = new Parcelable.Creator<ParsedIntentInfoImpl>() {
+        @Override
+        public ParsedIntentInfoImpl[] newArray(int size) {
+            return new ParsedIntentInfoImpl[size];
+        }
+
+        @Override
+        public ParsedIntentInfoImpl createFromParcel(@NonNull Parcel in) {
+            return new ParsedIntentInfoImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627691925408L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java",
+            inputSignatures = "private  boolean mHasDefault\nprivate  int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate  int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedIntentInfo]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
index 668b9cc..cb72c2b 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
@@ -53,11 +53,13 @@
             ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
             boolean allowAutoVerify, ParseInput input)
             throws XmlPullParserException, IOException {
-        ParsedIntentInfo intentInfo = new ParsedIntentInfo();
+        ParsedIntentInfoImpl intentInfo = new ParsedIntentInfoImpl();
+        IntentFilter intentFilter = intentInfo.getIntentFilter();
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter);
         try {
-            intentInfo.setPriority(sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0));
-            intentInfo.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0));
+            intentFilter.setPriority(
+                    sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0));
+            intentFilter.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0));
 
             TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
             if (v != null) {
@@ -78,7 +80,7 @@
             }
 
             if (allowAutoVerify) {
-                intentInfo.setAutoVerify(sa.getBoolean(
+                intentFilter.setAutoVerify(sa.getBoolean(
                         R.styleable.AndroidManifestIntentFilter_autoVerify,
                         false));
             }
@@ -102,12 +104,12 @@
                     if (value == null) {
                         result = input.error("No value supplied for <android:name>");
                     } else if (value.isEmpty()) {
-                        intentInfo.addAction(value);
+                        intentFilter.addAction(value);
                         // Prior to R, this was not a failure
                         result = input.deferError("No value supplied for <android:name>",
                                 ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
                     } else {
-                        intentInfo.addAction(value);
+                        intentFilter.addAction(value);
                         result = input.success(null);
                     }
                     break;
@@ -117,12 +119,12 @@
                     if (value == null) {
                         result = input.error("No value supplied for <android:name>");
                     } else if (value.isEmpty()) {
-                        intentInfo.addCategory(value);
+                        intentFilter.addCategory(value);
                         // Prior to R, this was not a failure
                         result = input.deferError("No value supplied for <android:name>",
                                 ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY);
                     } else {
-                        intentInfo.addCategory(value);
+                        intentFilter.addCategory(value);
                         result = input.success(null);
                     }
                     break;
@@ -140,14 +142,14 @@
             }
         }
 
-        intentInfo.setHasDefault(intentInfo.hasCategory(Intent.CATEGORY_DEFAULT));
+        intentInfo.setHasDefault(intentFilter.hasCategory(Intent.CATEGORY_DEFAULT));
 
         if (DEBUG) {
             final StringBuilder cats = new StringBuilder("Intent d=");
             cats.append(intentInfo.isHasDefault());
             cats.append(", cat=");
 
-            final Iterator<String> it = intentInfo.categoriesIterator();
+            final Iterator<String> it = intentFilter.categoriesIterator();
             if (it != null) {
                 while (it.hasNext()) {
                     cats.append(' ');
@@ -163,13 +165,14 @@
     @NonNull
     private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo,
             Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
+        IntentFilter intentFilter = intentInfo.getIntentFilter();
         TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData);
         try {
             String str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_mimeType, 0);
             if (str != null) {
                 try {
-                    intentInfo.addDataType(str);
+                    intentFilter.addDataType(str);
                 } catch (IntentFilter.MalformedMimeTypeException e) {
                     return input.error(e.toString());
                 }
@@ -178,26 +181,26 @@
             str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_mimeGroup, 0);
             if (str != null) {
-                intentInfo.addMimeGroup(str);
+                intentFilter.addMimeGroup(str);
             }
 
             str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_scheme, 0);
             if (str != null) {
-                intentInfo.addDataScheme(str);
+                intentFilter.addDataScheme(str);
             }
 
             str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_ssp, 0);
             if (str != null) {
-                intentInfo.addDataSchemeSpecificPart(str,
+                intentFilter.addDataSchemeSpecificPart(str,
                         PatternMatcher.PATTERN_LITERAL);
             }
 
             str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_sspPrefix, 0);
             if (str != null) {
-                intentInfo.addDataSchemeSpecificPart(str,
+                intentFilter.addDataSchemeSpecificPart(str,
                         PatternMatcher.PATTERN_PREFIX);
             }
 
@@ -208,7 +211,7 @@
                     return input.error(
                             "sspPattern not allowed here; ssp must be literal");
                 }
-                intentInfo.addDataSchemeSpecificPart(str,
+                intentFilter.addDataSchemeSpecificPart(str,
                         PatternMatcher.PATTERN_SIMPLE_GLOB);
             }
 
@@ -219,14 +222,14 @@
                     return input.error(
                             "sspAdvancedPattern not allowed here; ssp must be literal");
                 }
-                intentInfo.addDataSchemeSpecificPart(str,
+                intentFilter.addDataSchemeSpecificPart(str,
                         PatternMatcher.PATTERN_ADVANCED_GLOB);
             }
 
             str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_sspSuffix, 0);
             if (str != null) {
-                intentInfo.addDataSchemeSpecificPart(str,
+                intentFilter.addDataSchemeSpecificPart(str,
                         PatternMatcher.PATTERN_SUFFIX);
             }
 
@@ -236,19 +239,19 @@
             String port = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_port, 0);
             if (host != null) {
-                intentInfo.addDataAuthority(host, port);
+                intentFilter.addDataAuthority(host, port);
             }
 
             str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_path, 0);
             if (str != null) {
-                intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
             }
 
             str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_pathPrefix, 0);
             if (str != null) {
-                intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
             }
 
             str = sa.getNonConfigurationString(
@@ -258,7 +261,7 @@
                     return input.error(
                             "pathPattern not allowed here; path must be literal");
                 }
-                intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
             }
 
             str = sa.getNonConfigurationString(
@@ -268,13 +271,13 @@
                     return input.error(
                             "pathAdvancedPattern not allowed here; path must be literal");
                 }
-                intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
             }
 
             str = sa.getNonConfigurationString(
                     R.styleable.AndroidManifestData_pathSuffix, 0);
             if (str != null) {
-                intentInfo.addDataPath(str, PatternMatcher.PATTERN_SUFFIX);
+                intentFilter.addDataPath(str, PatternMatcher.PATTERN_SUFFIX);
             }
 
 
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
index 433bfd3..2507205 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
@@ -16,157 +16,30 @@
 
 package android.content.pm.parsing.component;
 
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
 import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
 /** @hide */
-public class ParsedMainComponent extends ParsedComponent {
+public interface ParsedMainComponent extends ParsedComponent {
 
     @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    protected String processName;
-    protected boolean directBootAware;
-    protected boolean enabled = true;
-    protected boolean exported;
-    protected int order;
-
-    @Nullable
-    protected String splitName;
-    @Nullable
-    protected String[] attributionTags;
-
-    public ParsedMainComponent() {
-    }
-
-    public ParsedMainComponent(ParsedMainComponent other) {
-        super(other);
-        this.processName = other.processName;
-        this.directBootAware = other.directBootAware;
-        this.enabled = other.enabled;
-        this.exported = other.exported;
-        this.order = other.order;
-        this.splitName = other.splitName;
-        this.attributionTags = other.attributionTags;
-    }
-
-    public ParsedMainComponent setOrder(int order) {
-        this.order = order;
-        return this;
-    }
-
-    public ParsedMainComponent setProcessName(String processName) {
-        this.processName = TextUtils.safeIntern(processName);
-        return this;
-    }
-
-    public ParsedMainComponent setEnabled(boolean enabled) {
-        this.enabled = enabled;
-        return this;
-    }
+    String[] getAttributionTags();
 
     /**
      * A main component's name is a class name. This makes code slightly more readable.
      */
-    public String getClassName() {
-        return getName();
-    }
+    String getClassName();
 
-    @Override
-    public int describeContents() {
-        return 0;
-    }
+    boolean isDirectBootAware();
 
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        sForInternedString.parcel(this.processName, dest, flags);
-        dest.writeBoolean(this.directBootAware);
-        dest.writeBoolean(this.enabled);
-        dest.writeBoolean(this.exported);
-        dest.writeInt(this.order);
-        dest.writeString(this.splitName);
-        dest.writeString8Array(this.attributionTags);
-    }
+    boolean isEnabled();
 
-    protected ParsedMainComponent(Parcel in) {
-        super(in);
-        this.processName = sForInternedString.unparcel(in);
-        this.directBootAware = in.readBoolean();
-        this.enabled = in.readBoolean();
-        this.exported = in.readBoolean();
-        this.order = in.readInt();
-        this.splitName = in.readString();
-        this.attributionTags = in.createString8Array();
-    }
+    boolean isExported();
 
-    public static final Parcelable.Creator<ParsedMainComponent> CREATOR =
-            new Parcelable.Creator<ParsedMainComponent>() {
-                @Override
-                public ParsedMainComponent createFromParcel(Parcel source) {
-                    return new ParsedMainComponent(source);
-                }
-
-                @Override
-                public ParsedMainComponent[] newArray(int size) {
-                    return new ParsedMainComponent[size];
-                }
-            };
+    int getOrder();
 
     @Nullable
-    public String getProcessName() {
-        return processName;
-    }
-
-    public boolean isDirectBootAware() {
-        return directBootAware;
-    }
-
-    public boolean isEnabled() {
-        return enabled;
-    }
-
-    public boolean isExported() {
-        return exported;
-    }
-
-    public int getOrder() {
-        return order;
-    }
+    String getProcessName();
 
     @Nullable
-    public String getSplitName() {
-        return splitName;
-    }
-
-    @Nullable
-    public String[] getAttributionTags() {
-        return attributionTags;
-    }
-
-    public ParsedMainComponent setDirectBootAware(boolean value) {
-        directBootAware = value;
-        return this;
-    }
-
-    public ParsedMainComponent setExported(boolean value) {
-        exported = value;
-        return this;
-    }
-
-    public ParsedMainComponent setSplitName(@Nullable String value) {
-        splitName = value;
-        return this;
-    }
-
-    public ParsedMainComponent setAttributionTags(@Nullable String[] value) {
-        attributionTags = value;
-        return this;
-    }
+    String getSplitName();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java
new file mode 100644
index 0000000..6051435
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedMainComponentImpl extends ParsedComponentImpl implements ParsedMainComponent {
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String processName;
+    private boolean directBootAware;
+    private boolean enabled = true;
+    private boolean exported;
+    private int order;
+
+    @Nullable
+    private String splitName;
+    @Nullable
+    private String[] attributionTags;
+
+    public ParsedMainComponentImpl() {
+    }
+
+    public ParsedMainComponentImpl(ParsedMainComponent other) {
+        super(other);
+        this.processName = other.getProcessName();
+        this.directBootAware = other.isDirectBootAware();
+        this.enabled = other.isEnabled();
+        this.exported = other.isExported();
+        this.order = other.getOrder();
+        this.splitName = other.getSplitName();
+        this.attributionTags = other.getAttributionTags();
+    }
+
+    public ParsedMainComponentImpl setProcessName(String processName) {
+        this.processName = TextUtils.safeIntern(processName);
+        return this;
+    }
+
+    /**
+     * A main component's name is a class name. This makes code slightly more readable.
+     */
+    public String getClassName() {
+        return getName();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        sForInternedString.parcel(this.processName, dest, flags);
+        dest.writeBoolean(this.directBootAware);
+        dest.writeBoolean(this.enabled);
+        dest.writeBoolean(this.exported);
+        dest.writeInt(this.order);
+        dest.writeString(this.splitName);
+        dest.writeString8Array(this.attributionTags);
+    }
+
+    protected ParsedMainComponentImpl(Parcel in) {
+        super(in);
+        this.processName = sForInternedString.unparcel(in);
+        this.directBootAware = in.readBoolean();
+        this.enabled = in.readBoolean();
+        this.exported = in.readBoolean();
+        this.order = in.readInt();
+        this.splitName = in.readString();
+        this.attributionTags = in.createString8Array();
+    }
+
+    public static final Parcelable.Creator<ParsedMainComponentImpl> CREATOR =
+            new Parcelable.Creator<ParsedMainComponentImpl>() {
+                @Override
+                public ParsedMainComponentImpl createFromParcel(Parcel source) {
+                    return new ParsedMainComponentImpl(source);
+                }
+
+                @Override
+                public ParsedMainComponentImpl[] newArray(int size) {
+                    return new ParsedMainComponentImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedMainComponentImpl(
+            @Nullable String processName,
+            boolean directBootAware,
+            boolean enabled,
+            boolean exported,
+            int order,
+            @Nullable String splitName,
+            @Nullable String[] attributionTags) {
+        this.processName = processName;
+        this.directBootAware = directBootAware;
+        this.enabled = enabled;
+        this.exported = exported;
+        this.order = order;
+        this.splitName = splitName;
+        this.attributionTags = attributionTags;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getProcessName() {
+        return processName;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isDirectBootAware() {
+        return directBootAware;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isExported() {
+        return exported;
+    }
+
+    @DataClass.Generated.Member
+    public int getOrder() {
+        return order;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getSplitName() {
+        return splitName;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String[] getAttributionTags() {
+        return attributionTags;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setDirectBootAware( boolean value) {
+        directBootAware = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setEnabled( boolean value) {
+        enabled = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setExported( boolean value) {
+        exported = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setOrder( int value) {
+        order = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setSplitName(@android.annotation.NonNull String value) {
+        splitName = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedMainComponentImpl setAttributionTags(@android.annotation.NonNull String... value) {
+        attributionTags = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627324857874L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final  android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedMainComponentImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedMainComponent]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
index 869e81c..87f75b0 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -19,6 +19,7 @@
 import static android.content.pm.parsing.ParsingUtils.NOT_SET;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.IntentFilter;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingUtils;
@@ -44,12 +45,12 @@
 
     @NonNull
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    static <Component extends ParsedMainComponent> ParseResult<Component> parseMainComponent(
+    static <Component extends ParsedMainComponentImpl> ParseResult<Component> parseMainComponent(
             Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
-            TypedArray array, int flags, boolean useRoundIcon, ParseInput input, int bannerAttr,
-            int descriptionAttr, int directBootAwareAttr, int enabledAttr, int iconAttr,
-            int labelAttr, int logoAttr, int nameAttr, int processAttr, int roundIconAttr,
-            int splitNameAttr, int attributionTagsAttr) {
+            TypedArray array, int flags, boolean useRoundIcon,  @Nullable String defaultSplitName,
+            @NonNull ParseInput input, int bannerAttr, int descriptionAttr, int directBootAwareAttr,
+            int enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
+            int processAttr, int roundIconAttr, int splitNameAttr, int attributionTagsAttr) {
         ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
                 array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
                 logoAttr, nameAttr, roundIconAttr);
@@ -95,6 +96,10 @@
             component.setSplitName(array.getNonConfigurationString(splitNameAttr, 0));
         }
 
+        if (defaultSplitName != null && component.getSplitName() == null) {
+            component.setSplitName(defaultSplitName);
+        }
+
         if (attributionTagsAttr != NOT_SET) {
             final String attributionTags = array.getNonConfigurationString(attributionTagsAttr, 0);
             if (attributionTags != null) {
@@ -119,7 +124,8 @@
         }
 
         ParsedIntentInfo intent = intentResult.getResult();
-        int actionCount = intent.countActions();
+        IntentFilter intentFilter = intent.getIntentFilter();
+        int actionCount = intentFilter.countActions();
         if (actionCount == 0 && failOnNoActions) {
             Slog.w(TAG, "No actions in " + parser.getName() + " at " + pkg.getBaseApkPath() + " "
                     + parser.getPositionDescription());
@@ -136,7 +142,7 @@
         } else {
             intentVisibility = IntentFilter.VISIBILITY_NONE;
         }
-        intent.setVisibilityToInstantApp(intentVisibility);
+        intentFilter.setVisibilityToInstantApp(intentVisibility);
 
         return input.success(intentResult.getResult());
     }
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
index 0f82941..6acdb6e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -16,206 +16,28 @@
 
 package android.content.pm.parsing.component;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.PermissionInfo;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.ArraySet;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
-
-import java.util.Locale;
 import java.util.Set;
 
 /** @hide */
-public class ParsedPermission extends ParsedComponent {
-
-    private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+public interface ParsedPermission extends ParsedComponent {
 
     @Nullable
-    private String backgroundPermission;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String group;
-    private int requestRes;
-    private int protectionLevel;
-    private boolean tree;
-    @Nullable
-    private ParsedPermissionGroup parsedPermissionGroup;
-    @Nullable
-    private Set<String> knownCerts;
-
-    @VisibleForTesting
-    public ParsedPermission() {
-    }
-
-    public ParsedPermission(ParsedPermission other) {
-        super(other);
-        this.backgroundPermission = other.backgroundPermission;
-        this.group = other.group;
-        this.requestRes = other.requestRes;
-        this.protectionLevel = other.protectionLevel;
-        this.tree = other.tree;
-        this.parsedPermissionGroup = other.parsedPermissionGroup;
-    }
-
-    public ParsedPermission setBackgroundPermission(String backgroundPermission) {
-        this.backgroundPermission = backgroundPermission;
-        return this;
-    }
-
-    public ParsedPermission setGroup(String group) {
-        this.group = TextUtils.safeIntern(group);
-        return this;
-    }
-
-    public boolean isRuntime() {
-        return getProtection() == PermissionInfo.PROTECTION_DANGEROUS;
-    }
-
-    public boolean isAppOp() {
-        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
-    }
-
-    @PermissionInfo.Protection
-    public int getProtection() {
-        return protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
-    }
-
-    public int getProtectionFlags() {
-        return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE;
-    }
-
-    public @Nullable Set<String> getKnownCerts() {
-        return knownCerts;
-    }
-
-    protected void setKnownCert(String knownCert) {
-        // Convert the provided digest to upper case for consistent Set membership
-        // checks when verifying the signing certificate digests of requesting apps.
-        this.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
-    }
-
-    protected void setKnownCerts(String[] knownCerts) {
-        this.knownCerts = new ArraySet<>();
-        for (String knownCert : knownCerts) {
-            this.knownCerts.add(knownCert.toUpperCase(Locale.US));
-        }
-    }
-
-    public int calculateFootprint() {
-        int size = getName().length();
-        if (getNonLocalizedLabel() != null) {
-            size += getNonLocalizedLabel().length();
-        }
-        return size;
-    }
-
-    public ParsedPermission setKnownCerts(Set<String> knownCerts) {
-        this.knownCerts = knownCerts;
-        return this;
-    }
-
-    public ParsedPermission setRequestRes(int requestRes) {
-        this.requestRes = requestRes;
-        return this;
-    }
-
-    public ParsedPermission setTree(boolean tree) {
-        this.tree = tree;
-        return this;
-    }
-
-    public String toString() {
-        return "Permission{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " " + getName() + "}";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeString(this.backgroundPermission);
-        dest.writeString(this.group);
-        dest.writeInt(this.requestRes);
-        dest.writeInt(this.protectionLevel);
-        dest.writeBoolean(this.tree);
-        dest.writeParcelable(this.parsedPermissionGroup, flags);
-        sForStringSet.parcel(knownCerts, dest, flags);
-    }
-
-    protected ParsedPermission(Parcel in) {
-        super(in);
-        // We use the boot classloader for all classes that we load.
-        final ClassLoader boot = Object.class.getClassLoader();
-        this.backgroundPermission = in.readString();
-        this.group = in.readString();
-        this.requestRes = in.readInt();
-        this.protectionLevel = in.readInt();
-        this.tree = in.readBoolean();
-        this.parsedPermissionGroup = in.readParcelable(boot);
-        this.knownCerts = sForStringSet.unparcel(in);
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedPermission> CREATOR =
-            new Parcelable.Creator<ParsedPermission>() {
-                @Override
-                public ParsedPermission createFromParcel(Parcel source) {
-                    return new ParsedPermission(source);
-                }
-
-                @Override
-                public ParsedPermission[] newArray(int size) {
-                    return new ParsedPermission[size];
-                }
-            };
+    String getBackgroundPermission();
 
     @Nullable
-    public String getBackgroundPermission() {
-        return backgroundPermission;
-    }
+    String getGroup();
 
     @Nullable
-    public String getGroup() {
-        return group;
-    }
-
-    public int getRequestRes() {
-        return requestRes;
-    }
-
-    public int getProtectionLevel() {
-        return protectionLevel;
-    }
-
-    public boolean isTree() {
-        return tree;
-    }
+    Set<String> getKnownCerts();
 
     @Nullable
-    public ParsedPermissionGroup getParsedPermissionGroup() {
-        return parsedPermissionGroup;
-    }
+    ParsedPermissionGroup getParsedPermissionGroup();
 
-    public ParsedPermission setProtectionLevel(int value) {
-        protectionLevel = value;
-        return this;
-    }
+    int getProtectionLevel();
 
-    public ParsedPermission setParsedPermissionGroup(@Nullable ParsedPermissionGroup value) {
-        parsedPermissionGroup = value;
-        return this;
-    }
+    int getRequestRes();
+
+    boolean isTree();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
index 9fb95c4..22aa085 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
@@ -16,184 +16,16 @@
 
 package android.content.pm.parsing.component;
 
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.DataClass;
-
 /** @hide */
-@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
-        genAidl = false)
-public class ParsedPermissionGroup extends ParsedComponent {
+public interface ParsedPermissionGroup extends ParsedComponent {
 
-    private int requestDetailResourceId;
-    private int backgroundRequestResourceId;
-    private int backgroundRequestDetailResourceId;
-    private int requestRes;
-    private int priority;
+    int getBackgroundRequestDetailResourceId();
 
-    public String toString() {
-        return "PermissionGroup{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " " + getName() + "}";
-    }
+    int getBackgroundRequestResourceId();
 
-    public ParsedPermissionGroup() {
-    }
+    int getPriority();
 
+    int getRequestDetailResourceId();
 
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedPermissionGroup(
-            int requestDetailResourceId,
-            int backgroundRequestResourceId,
-            int backgroundRequestDetailResourceId,
-            int requestRes,
-            int priority) {
-        this.requestDetailResourceId = requestDetailResourceId;
-        this.backgroundRequestResourceId = backgroundRequestResourceId;
-        this.backgroundRequestDetailResourceId = backgroundRequestDetailResourceId;
-        this.requestRes = requestRes;
-        this.priority = priority;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public int getRequestDetailResourceId() {
-        return requestDetailResourceId;
-    }
-
-    @DataClass.Generated.Member
-    public int getBackgroundRequestResourceId() {
-        return backgroundRequestResourceId;
-    }
-
-    @DataClass.Generated.Member
-    public int getBackgroundRequestDetailResourceId() {
-        return backgroundRequestDetailResourceId;
-    }
-
-    @DataClass.Generated.Member
-    public int getRequestRes() {
-        return requestRes;
-    }
-
-    @DataClass.Generated.Member
-    public int getPriority() {
-        return priority;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroup setRequestDetailResourceId( int value) {
-        requestDetailResourceId = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroup setBackgroundRequestResourceId( int value) {
-        backgroundRequestResourceId = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroup setBackgroundRequestDetailResourceId( int value) {
-        backgroundRequestDetailResourceId = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroup setRequestRes( int value) {
-        requestRes = value;
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @android.annotation.NonNull ParsedPermissionGroup setPriority( int value) {
-        priority = value;
-        return this;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        super.writeToParcel(dest, flags);
-
-        dest.writeInt(requestDetailResourceId);
-        dest.writeInt(backgroundRequestResourceId);
-        dest.writeInt(backgroundRequestDetailResourceId);
-        dest.writeInt(requestRes);
-        dest.writeInt(priority);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedPermissionGroup(@android.annotation.NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        super(in);
-
-        int _requestDetailResourceId = in.readInt();
-        int _backgroundRequestResourceId = in.readInt();
-        int _backgroundRequestDetailResourceId = in.readInt();
-        int _requestRes = in.readInt();
-        int _priority = in.readInt();
-
-        this.requestDetailResourceId = _requestDetailResourceId;
-        this.backgroundRequestResourceId = _backgroundRequestResourceId;
-        this.backgroundRequestDetailResourceId = _backgroundRequestDetailResourceId;
-        this.requestRes = _requestRes;
-        this.priority = _priority;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @android.annotation.NonNull Parcelable.Creator<ParsedPermissionGroup> CREATOR
-            = new Parcelable.Creator<ParsedPermissionGroup>() {
-        @Override
-        public ParsedPermissionGroup[] newArray(int size) {
-            return new ParsedPermissionGroup[size];
-        }
-
-        @Override
-        public ParsedPermissionGroup createFromParcel(@android.annotation.NonNull Parcel in) {
-            return new ParsedPermissionGroup(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1624052057830L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java",
-            inputSignatures = "private  int requestDetailResourceId\nprivate  int backgroundRequestResourceId\nprivate  int backgroundRequestDetailResourceId\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\nclass ParsedPermissionGroup extends android.content.pm.parsing.component.ParsedComponent implements []\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
+    int getRequestRes();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java b/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java
new file mode 100644
index 0000000..1fa04cf
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import android.os.Parcel;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
+        genAidl = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements
+        ParsedPermissionGroup {
+
+    private int requestDetailResourceId;
+    private int backgroundRequestResourceId;
+    private int backgroundRequestDetailResourceId;
+    private int requestRes;
+    private int priority;
+
+    public String toString() {
+        return "PermissionGroup{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + getName() + "}";
+    }
+
+    public ParsedPermissionGroupImpl() {
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedPermissionGroupImpl(
+            int requestDetailResourceId,
+            int backgroundRequestResourceId,
+            int backgroundRequestDetailResourceId,
+            int requestRes,
+            int priority) {
+        this.requestDetailResourceId = requestDetailResourceId;
+        this.backgroundRequestResourceId = backgroundRequestResourceId;
+        this.backgroundRequestDetailResourceId = backgroundRequestDetailResourceId;
+        this.requestRes = requestRes;
+        this.priority = priority;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public int getRequestDetailResourceId() {
+        return requestDetailResourceId;
+    }
+
+    @DataClass.Generated.Member
+    public int getBackgroundRequestResourceId() {
+        return backgroundRequestResourceId;
+    }
+
+    @DataClass.Generated.Member
+    public int getBackgroundRequestDetailResourceId() {
+        return backgroundRequestDetailResourceId;
+    }
+
+    @DataClass.Generated.Member
+    public int getRequestRes() {
+        return requestRes;
+    }
+
+    @DataClass.Generated.Member
+    public int getPriority() {
+        return priority;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setRequestDetailResourceId( int value) {
+        requestDetailResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setBackgroundRequestResourceId( int value) {
+        backgroundRequestResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setBackgroundRequestDetailResourceId( int value) {
+        backgroundRequestDetailResourceId = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setRequestRes( int value) {
+        requestRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @android.annotation.NonNull ParsedPermissionGroupImpl setPriority( int value) {
+        priority = value;
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        super.writeToParcel(dest, flags);
+
+        dest.writeInt(requestDetailResourceId);
+        dest.writeInt(backgroundRequestResourceId);
+        dest.writeInt(backgroundRequestDetailResourceId);
+        dest.writeInt(requestRes);
+        dest.writeInt(priority);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedPermissionGroupImpl(@android.annotation.NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        super(in);
+
+        int _requestDetailResourceId = in.readInt();
+        int _backgroundRequestResourceId = in.readInt();
+        int _backgroundRequestDetailResourceId = in.readInt();
+        int _requestRes = in.readInt();
+        int _priority = in.readInt();
+
+        this.requestDetailResourceId = _requestDetailResourceId;
+        this.backgroundRequestResourceId = _backgroundRequestResourceId;
+        this.backgroundRequestDetailResourceId = _backgroundRequestDetailResourceId;
+        this.requestRes = _requestRes;
+        this.priority = _priority;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull android.os.Parcelable.Creator<ParsedPermissionGroupImpl> CREATOR
+            = new android.os.Parcelable.Creator<ParsedPermissionGroupImpl>() {
+        @Override
+        public ParsedPermissionGroupImpl[] newArray(int size) {
+            return new ParsedPermissionGroupImpl[size];
+        }
+
+        @Override
+        public ParsedPermissionGroupImpl createFromParcel(@android.annotation.NonNull Parcel in) {
+            return new ParsedPermissionGroupImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627602253988L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroupImpl.java",
+            inputSignatures = "private  int requestDetailResourceId\nprivate  int backgroundRequestResourceId\nprivate  int backgroundRequestDetailResourceId\nprivate  int requestRes\nprivate  int priority\npublic  java.lang.String toString()\nclass ParsedPermissionGroupImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermissionGroup]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java b/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
new file mode 100644
index 0000000..2145e44
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
+
+import java.util.Locale;
+import java.util.Set;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedPermission {
+
+    private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+
+    @Nullable
+    private String backgroundPermission;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String group;
+    private int requestRes;
+    private int protectionLevel;
+    private boolean tree;
+    @Nullable
+    private ParsedPermissionGroup parsedPermissionGroup;
+    @Nullable
+    private Set<String> knownCerts;
+
+    @VisibleForTesting
+    public ParsedPermissionImpl() {
+    }
+
+    public ParsedPermissionImpl(ParsedPermission other) {
+        super(other);
+        this.backgroundPermission = other.getBackgroundPermission();
+        this.group = other.getGroup();
+        this.requestRes = other.getRequestRes();
+        this.protectionLevel = other.getProtectionLevel();
+        this.tree = other.isTree();
+        this.parsedPermissionGroup = other.getParsedPermissionGroup();
+    }
+
+    public ParsedPermissionImpl setGroup(String group) {
+        this.group = TextUtils.safeIntern(group);
+        return this;
+    }
+
+    protected void setKnownCert(String knownCert) {
+        // Convert the provided digest to upper case for consistent Set membership
+        // checks when verifying the signing certificate digests of requesting apps.
+        this.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
+    }
+
+    protected void setKnownCerts(String[] knownCerts) {
+        this.knownCerts = new ArraySet<>();
+        for (String knownCert : knownCerts) {
+            this.knownCerts.add(knownCert.toUpperCase(Locale.US));
+        }
+    }
+
+    public String toString() {
+        return "Permission{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + getName() + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(this.backgroundPermission);
+        dest.writeString(this.group);
+        dest.writeInt(this.requestRes);
+        dest.writeInt(this.protectionLevel);
+        dest.writeBoolean(this.tree);
+        dest.writeParcelable(this.parsedPermissionGroup, flags);
+        sForStringSet.parcel(knownCerts, dest, flags);
+    }
+
+    protected ParsedPermissionImpl(Parcel in) {
+        super(in);
+        // We use the boot classloader for all classes that we load.
+        final ClassLoader boot = Object.class.getClassLoader();
+        this.backgroundPermission = in.readString();
+        this.group = TextUtils.safeIntern(in.readString());
+        this.requestRes = in.readInt();
+        this.protectionLevel = in.readInt();
+        this.tree = in.readBoolean();
+        this.parsedPermissionGroup = in.readParcelable(boot);
+        this.knownCerts = sForStringSet.unparcel(in);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedPermissionImpl> CREATOR =
+            new Parcelable.Creator<ParsedPermissionImpl>() {
+                @Override
+                public ParsedPermissionImpl createFromParcel(Parcel source) {
+                    return new ParsedPermissionImpl(source);
+                }
+
+                @Override
+                public ParsedPermissionImpl[] newArray(int size) {
+                    return new ParsedPermissionImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedPermissionImpl(
+            @Nullable String backgroundPermission,
+            @Nullable String group,
+            int requestRes,
+            int protectionLevel,
+            boolean tree,
+            @Nullable ParsedPermissionGroup parsedPermissionGroup,
+            @Nullable Set<String> knownCerts) {
+        this.backgroundPermission = backgroundPermission;
+        this.group = group;
+        this.requestRes = requestRes;
+        this.protectionLevel = protectionLevel;
+        this.tree = tree;
+        this.parsedPermissionGroup = parsedPermissionGroup;
+        this.knownCerts = knownCerts;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getBackgroundPermission() {
+        return backgroundPermission;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getGroup() {
+        return group;
+    }
+
+    @DataClass.Generated.Member
+    public int getRequestRes() {
+        return requestRes;
+    }
+
+    @DataClass.Generated.Member
+    public int getProtectionLevel() {
+        return protectionLevel;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isTree() {
+        return tree;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable ParsedPermissionGroup getParsedPermissionGroup() {
+        return parsedPermissionGroup;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable Set<String> getKnownCerts() {
+        return knownCerts;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setBackgroundPermission(@NonNull String value) {
+        backgroundPermission = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setRequestRes( int value) {
+        requestRes = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setProtectionLevel( int value) {
+        protectionLevel = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setTree( boolean value) {
+        tree = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setParsedPermissionGroup(@NonNull ParsedPermissionGroup value) {
+        parsedPermissionGroup = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedPermissionImpl setKnownCerts(@NonNull Set<String> value) {
+        knownCerts = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627598236506L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java",
+            inputSignatures = "private static  com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate  int requestRes\nprivate  int protectionLevel\nprivate  boolean tree\nprivate @android.annotation.Nullable android.content.pm.parsing.component.ParsedPermissionGroup parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedPermissionImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected  void setKnownCert(java.lang.String)\nprotected  void setKnownCerts(java.lang.String[])\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermission]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index 5a7a5ef..66e9d3d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -45,10 +45,9 @@
             XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
             throws IOException, XmlPullParserException {
         String packageName = pkg.getPackageName();
-        ParsedPermission
-                permission = new ParsedPermission();
+        ParsedPermissionImpl permission = new ParsedPermissionImpl();
         String tag = "<" + parser.getName() + ">";
-        final ParseResult<ParsedPermission> result;
+        ParseResult<ParsedPermissionImpl> result;
 
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission);
         try {
@@ -62,7 +61,7 @@
                     R.styleable.AndroidManifestPermission_name,
                     R.styleable.AndroidManifestPermission_roundIcon);
             if (result.isError()) {
-                return result;
+                return input.error(result);
             }
 
             if (sa.hasValue(
@@ -121,7 +120,7 @@
             }
 
             // For now only platform runtime permissions can be restricted
-            if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) {
+            if (!isRuntime(permission) || !"android".equals(permission.getPackageName())) {
                 permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_HARD_RESTRICTED);
                 permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_SOFT_RESTRICTED);
             } else {
@@ -139,26 +138,31 @@
         permission.setProtectionLevel(
                 PermissionInfo.fixProtectionLevel(permission.getProtectionLevel()));
 
-        final int otherProtectionFlags = permission.getProtectionFlags()
+        final int otherProtectionFlags = getProtectionFlags(permission)
                 & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
                 | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY);
         if (otherProtectionFlags != 0
-                && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE
-                && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) {
+                && getProtection(permission) != PermissionInfo.PROTECTION_SIGNATURE
+                && getProtection(permission) != PermissionInfo.PROTECTION_INTERNAL) {
             return input.error("<permission> protectionLevel specifies a non-instant, non-appop,"
                     + " non-runtimeOnly flag but is not based on signature or internal type");
         }
 
-        return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
+        result = ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(result.getResult());
     }
 
     @NonNull
     public static ParseResult<ParsedPermission> parsePermissionTree(ParsingPackage pkg, Resources res,
             XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
             throws IOException, XmlPullParserException {
-        ParsedPermission permission = new ParsedPermission();
+        ParsedPermissionImpl permission = new ParsedPermissionImpl();
         String tag = "<" + parser.getName() + ">";
-        final ParseResult<ParsedPermission> result;
+        ParseResult<ParsedPermissionImpl> result;
 
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree);
         try {
@@ -172,7 +176,7 @@
                     R.styleable.AndroidManifestPermissionTree_name,
                     R.styleable.AndroidManifestPermissionTree_roundIcon);
             if (result.isError()) {
-                return result;
+                return input.error(result);
             }
         } finally {
             sa.recycle();
@@ -190,21 +194,25 @@
         permission.setProtectionLevel(PermissionInfo.PROTECTION_NORMAL)
                 .setTree(true);
 
-        return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission,
-                input);
+        result = ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(result.getResult());
     }
 
     @NonNull
     public static ParseResult<ParsedPermissionGroup> parsePermissionGroup(ParsingPackage pkg,
             Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
             throws IOException, XmlPullParserException {
-        ParsedPermissionGroup
-                permissionGroup = new ParsedPermissionGroup();
+        ParsedPermissionGroupImpl
+                permissionGroup = new ParsedPermissionGroupImpl();
         String tag = "<" + parser.getName() + ">";
 
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup);
         try {
-            ParseResult<ParsedPermissionGroup> result = ParsedComponentUtils.parseComponent(
+            ParseResult<ParsedPermissionGroupImpl> result = ParsedComponentUtils.parseComponent(
                     permissionGroup, tag, pkg, sa, useRoundIcon, input,
                     R.styleable.AndroidManifestPermissionGroup_banner,
                     R.styleable.AndroidManifestPermissionGroup_description,
@@ -214,7 +222,7 @@
                     R.styleable.AndroidManifestPermissionGroup_name,
                     R.styleable.AndroidManifestPermissionGroup_roundIcon);
             if (result.isError()) {
-                return result;
+                return input.error(result);
             }
 
             // @formatter:off
@@ -229,7 +237,38 @@
             sa.recycle();
         }
 
-        return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permissionGroup,
-                input);
+        ParseResult<ParsedPermissionGroupImpl> result = ComponentParseUtils.parseAllMetaData(pkg,
+                res, parser, tag, permissionGroup, input);
+        if (result.isError()) {
+            return input.error(result);
+        }
+
+        return input.success(result.getResult());
+    }
+
+    public static boolean isRuntime(@NonNull ParsedPermission permission) {
+        return getProtection(permission) == PermissionInfo.PROTECTION_DANGEROUS;
+    }
+
+    public static boolean isAppOp(@NonNull ParsedPermission permission) {
+        return (permission.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+    }
+
+    @PermissionInfo.Protection
+    public static int getProtection(@NonNull ParsedPermission permission) {
+        return permission.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE;
+    }
+
+    public static int getProtectionFlags(@NonNull ParsedPermission permission) {
+        return permission.getProtectionLevel() & ~PermissionInfo.PROTECTION_MASK_BASE;
+    }
+
+    public static int calculateFootprint(@NonNull ParsedPermission permission) {
+        int size = permission.getName().length();
+        CharSequence nonLocalizedLabel = permission.getNonLocalizedLabel();
+        if (nonLocalizedLabel != null) {
+            size += nonLocalizedLabel.length();
+        }
+        return size;
     }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index fe10225..c2d5163 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -16,240 +16,27 @@
 
 package android.content.pm.parsing.component;
 
-import static java.util.Collections.emptySet;
-
 import android.annotation.NonNull;
 import android.content.pm.ApplicationInfo;
-import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.ArraySet;
-
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
 
 import java.util.Set;
 
 /** @hide */
-@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
-        genBuilder = false)
-public class ParsedProcess implements Parcelable {
+public interface ParsedProcess extends Parcelable {
 
     @NonNull
-    private String name;
-    @NonNull
-    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
-    private Set<String> deniedPermissions = emptySet();
+    Set<String> getDeniedPermissions();
 
     @ApplicationInfo.GwpAsanMode
-    private int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+    int getGwpAsanMode();
+
     @ApplicationInfo.MemtagMode
-    private int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
+    int getMemtagMode();
+
+    @NonNull
+    String getName();
+
     @ApplicationInfo.NativeHeapZeroInitialized
-    private int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
-
-    public ParsedProcess() {
-    }
-
-    public ParsedProcess(@NonNull ParsedProcess other) {
-        name = other.name;
-        deniedPermissions = new ArraySet<>(other.deniedPermissions);
-    }
-
-    public void addStateFrom(@NonNull ParsedProcess other) {
-        deniedPermissions = CollectionUtils.addAll(deniedPermissions, other.deniedPermissions);
-    }
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedProcess(
-            @NonNull String name,
-            @NonNull Set<String> deniedPermissions,
-            @ApplicationInfo.GwpAsanMode int gwpAsanMode,
-            @ApplicationInfo.MemtagMode int memtagMode,
-            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
-        this.name = name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.deniedPermissions = deniedPermissions;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, deniedPermissions);
-        this.gwpAsanMode = gwpAsanMode;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
-        this.memtagMode = memtagMode;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.MemtagMode.class, null, memtagMode);
-        this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return name;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull Set<String> getDeniedPermissions() {
-        return deniedPermissions;
-    }
-
-    @DataClass.Generated.Member
-    public @ApplicationInfo.GwpAsanMode int getGwpAsanMode() {
-        return gwpAsanMode;
-    }
-
-    @DataClass.Generated.Member
-    public @ApplicationInfo.MemtagMode int getMemtagMode() {
-        return memtagMode;
-    }
-
-    @DataClass.Generated.Member
-    public @ApplicationInfo.NativeHeapZeroInitialized int getNativeHeapZeroInitialized() {
-        return nativeHeapZeroInitialized;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcess setName(@NonNull String value) {
-        name = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcess setDeniedPermissions(@NonNull Set<String> value) {
-        deniedPermissions = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, deniedPermissions);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcess setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
-        gwpAsanMode = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcess setMemtagMode(@ApplicationInfo.MemtagMode int value) {
-        memtagMode = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.MemtagMode.class, null, memtagMode);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedProcess setNativeHeapZeroInitialized(@ApplicationInfo.NativeHeapZeroInitialized int value) {
-        nativeHeapZeroInitialized = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    static Parcelling<Set<String>> sParcellingForDeniedPermissions =
-            Parcelling.Cache.get(
-                    Parcelling.BuiltIn.ForInternedStringSet.class);
-    static {
-        if (sParcellingForDeniedPermissions == null) {
-            sParcellingForDeniedPermissions = Parcelling.Cache.put(
-                    new Parcelling.BuiltIn.ForInternedStringSet());
-        }
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeString(name);
-        sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
-        dest.writeInt(gwpAsanMode);
-        dest.writeInt(memtagMode);
-        dest.writeInt(nativeHeapZeroInitialized);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedProcess(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        String _name = in.readString();
-        Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
-        int _gwpAsanMode = in.readInt();
-        int _memtagMode = in.readInt();
-        int _nativeHeapZeroInitialized = in.readInt();
-
-        this.name = _name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.deniedPermissions = _deniedPermissions;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, deniedPermissions);
-        this.gwpAsanMode = _gwpAsanMode;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
-        this.memtagMode = _memtagMode;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.MemtagMode.class, null, memtagMode);
-        this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
-        com.android.internal.util.AnnotationValidations.validate(
-                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ParsedProcess> CREATOR
-            = new Parcelable.Creator<ParsedProcess>() {
-        @Override
-        public ParsedProcess[] newArray(int size) {
-            return new ParsedProcess[size];
-        }
-
-        @Override
-        public ParsedProcess createFromParcel(@NonNull Parcel in) {
-            return new ParsedProcess(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1623692988845L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
+    int getNativeHeapZeroInitialized();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java b/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
new file mode 100644
index 0000000..3fd60eb
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
@@ -0,0 +1,263 @@
+/*
+ * 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 android.content.pm.parsing.component;
+
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Set;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
+        genBuilder = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedProcessImpl implements ParsedProcess {
+
+    @NonNull
+    private String name;
+    @NonNull
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
+    private Set<String> deniedPermissions = emptySet();
+
+    @ApplicationInfo.GwpAsanMode
+    private int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+    @ApplicationInfo.MemtagMode
+    private int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
+    @ApplicationInfo.NativeHeapZeroInitialized
+    private int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
+
+    public ParsedProcessImpl() {
+    }
+
+    public ParsedProcessImpl(@NonNull ParsedProcess other) {
+        name = other.getName();
+        deniedPermissions = new ArraySet<>(other.getDeniedPermissions());
+        gwpAsanMode = other.getGwpAsanMode();
+        memtagMode = other.getMemtagMode();
+        nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
+    }
+
+    public void addStateFrom(@NonNull ParsedProcess other) {
+        deniedPermissions = CollectionUtils.addAll(deniedPermissions, other.getDeniedPermissions());
+        gwpAsanMode = other.getGwpAsanMode();
+        memtagMode = other.getMemtagMode();
+        nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedProcessImpl(
+            @NonNull String name,
+            @NonNull Set<String> deniedPermissions,
+            @ApplicationInfo.GwpAsanMode int gwpAsanMode,
+            @ApplicationInfo.MemtagMode int memtagMode,
+            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.deniedPermissions = deniedPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        this.gwpAsanMode = gwpAsanMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        this.memtagMode = memtagMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Set<String> getDeniedPermissions() {
+        return deniedPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public @ApplicationInfo.GwpAsanMode int getGwpAsanMode() {
+        return gwpAsanMode;
+    }
+
+    @DataClass.Generated.Member
+    public @ApplicationInfo.MemtagMode int getMemtagMode() {
+        return memtagMode;
+    }
+
+    @DataClass.Generated.Member
+    public @ApplicationInfo.NativeHeapZeroInitialized int getNativeHeapZeroInitialized() {
+        return nativeHeapZeroInitialized;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setName(@NonNull String value) {
+        name = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setDeniedPermissions(@NonNull Set<String> value) {
+        deniedPermissions = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
+        gwpAsanMode = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setMemtagMode(@ApplicationInfo.MemtagMode int value) {
+        memtagMode = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setNativeHeapZeroInitialized(@ApplicationInfo.NativeHeapZeroInitialized int value) {
+        nativeHeapZeroInitialized = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<Set<String>> sParcellingForDeniedPermissions =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedStringSet.class);
+    static {
+        if (sParcellingForDeniedPermissions == null) {
+            sParcellingForDeniedPermissions = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedStringSet());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(name);
+        sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+        dest.writeInt(gwpAsanMode);
+        dest.writeInt(memtagMode);
+        dest.writeInt(nativeHeapZeroInitialized);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedProcessImpl(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _name = in.readString();
+        Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+        int _gwpAsanMode = in.readInt();
+        int _memtagMode = in.readInt();
+        int _nativeHeapZeroInitialized = in.readInt();
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.deniedPermissions = _deniedPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, deniedPermissions);
+        this.gwpAsanMode = _gwpAsanMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+        this.memtagMode = _memtagMode;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.MemtagMode.class, null, memtagMode);
+        this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
+        com.android.internal.util.AnnotationValidations.validate(
+                ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedProcessImpl> CREATOR
+            = new Parcelable.Creator<ParsedProcessImpl>() {
+        @Override
+        public ParsedProcessImpl[] newArray(int size) {
+            return new ParsedProcessImpl[size];
+        }
+
+        @Override
+        public ParsedProcessImpl createFromParcel(@NonNull Parcel in) {
+            return new ParsedProcessImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627605368434L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index 54dd295..cf83586 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -81,7 +81,7 @@
     private static ParseResult<ParsedProcess> parseProcess(Set<String> perms, String[] separateProcesses,
             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
             ParseInput input) throws IOException, XmlPullParserException {
-        ParsedProcess proc = new ParsedProcess();
+        ParsedProcessImpl proc = new ParsedProcessImpl();
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
         try {
             if (perms != null) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedProvider.java b/core/java/android/content/pm/parsing/component/ParsedProvider.java
index 9a12b48..1211ce2 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProvider.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProvider.java
@@ -16,214 +16,31 @@
 
 package android.content.pm.parsing.component;
 
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ComponentName;
 import android.content.pm.PathPermission;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.os.PatternMatcher;
-import android.text.TextUtils;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
 /** @hide **/
-public class ParsedProvider extends ParsedMainComponent {
-
-    @NonNull
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String authority;
-    private boolean syncable;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String readPermission;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String writePermission;
-    private boolean grantUriPermissions;
-    private boolean forceUriPermissions;
-    private boolean multiProcess;
-    private int initOrder;
-    @Nullable
-    private PatternMatcher[] uriPermissionPatterns;
-    @Nullable
-    private PathPermission[] pathPermissions;
-
-    public ParsedProvider(ParsedProvider other) {
-        super(other);
-
-        this.authority = other.authority;
-        this.syncable = other.syncable;
-        this.readPermission = other.readPermission;
-        this.writePermission = other.writePermission;
-        this.grantUriPermissions = other.grantUriPermissions;
-        this.forceUriPermissions = other.forceUriPermissions;
-        this.multiProcess = other.multiProcess;
-        this.initOrder = other.initOrder;
-        this.uriPermissionPatterns = other.uriPermissionPatterns;
-        this.pathPermissions = other.pathPermissions;
-    }
-
-    public ParsedProvider setAuthority(String authority) {
-        this.authority = TextUtils.safeIntern(authority);
-        return this;
-    }
-
-    public ParsedProvider setForceUriPermissions(boolean forceUriPermissions) {
-        this.forceUriPermissions = forceUriPermissions;
-        return this;
-    }
-
-    public ParsedProvider setGrantUriPermissions(boolean grantUriPermissions) {
-        this.grantUriPermissions = grantUriPermissions;
-        return this;
-    }
-
-    public ParsedProvider setInitOrder(int initOrder) {
-        this.initOrder = initOrder;
-        return this;
-    }
-
-    public ParsedProvider setMultiProcess(boolean multiProcess) {
-        this.multiProcess = multiProcess;
-        return this;
-    }
-
-    public ParsedProvider setPathPermissions(PathPermission[] pathPermissions) {
-        this.pathPermissions = pathPermissions;
-        return this;
-    }
-
-    public ParsedProvider setSyncable(boolean syncable) {
-        this.syncable = syncable;
-        return this;
-    }
-
-    public ParsedProvider setReadPermission(String readPermission) {
-        // Empty string must be converted to null
-        this.readPermission = TextUtils.isEmpty(readPermission)
-                ? null : readPermission.intern();
-        return this;
-    }
-
-    public ParsedProvider setUriPermissionPatterns(PatternMatcher[] uriPermissionPatterns) {
-        this.uriPermissionPatterns = uriPermissionPatterns;
-        return this;
-    }
-
-    public ParsedProvider setWritePermission(String writePermission) {
-        // Empty string must be converted to null
-        this.writePermission = TextUtils.isEmpty(writePermission)
-                ? null : writePermission.intern();
-        return this;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Provider{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        ComponentName.appendShortString(sb, getPackageName(), getName());
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeString(this.authority);
-        dest.writeBoolean(this.syncable);
-        sForInternedString.parcel(this.readPermission, dest, flags);
-        sForInternedString.parcel(this.writePermission, dest, flags);
-        dest.writeBoolean(this.grantUriPermissions);
-        dest.writeBoolean(this.forceUriPermissions);
-        dest.writeBoolean(this.multiProcess);
-        dest.writeInt(this.initOrder);
-        dest.writeTypedArray(this.uriPermissionPatterns, flags);
-        dest.writeTypedArray(this.pathPermissions, flags);
-    }
-
-    public ParsedProvider() {
-    }
-
-    protected ParsedProvider(Parcel in) {
-        super(in);
-        //noinspection ConstantConditions
-        this.authority = in.readString();
-        this.syncable = in.readBoolean();
-        this.readPermission = sForInternedString.unparcel(in);
-        this.writePermission = sForInternedString.unparcel(in);
-        this.grantUriPermissions = in.readBoolean();
-        this.forceUriPermissions = in.readBoolean();
-        this.multiProcess = in.readBoolean();
-        this.initOrder = in.readInt();
-        this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
-        this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedProvider> CREATOR = new Creator<ParsedProvider>() {
-        @Override
-        public ParsedProvider createFromParcel(Parcel source) {
-            return new ParsedProvider(source);
-        }
-
-        @Override
-        public ParsedProvider[] newArray(int size) {
-            return new ParsedProvider[size];
-        }
-    };
-
-    @NonNull
-    public String getAuthority() {
-        return authority;
-    }
-
-    public boolean isSyncable() {
-        return syncable;
-    }
+public interface ParsedProvider extends ParsedMainComponent {
 
     @Nullable
-    public String getReadPermission() {
-        return readPermission;
-    }
+    String getAuthority();
 
-    @Nullable
-    public String getWritePermission() {
-        return writePermission;
-    }
+    int getInitOrder();
 
-    public boolean isGrantUriPermissions() {
-        return grantUriPermissions;
-    }
+    boolean isMultiProcess();
 
-    public boolean isForceUriPermissions() {
-        return forceUriPermissions;
-    }
+    @Nullable PathPermission[] getPathPermissions();
 
-    public boolean isMultiProcess() {
-        return multiProcess;
-    }
+    @Nullable String getReadPermission();
 
-    public int getInitOrder() {
-        return initOrder;
-    }
+    @Nullable PatternMatcher[] getUriPermissionPatterns();
 
-    @Nullable
-    public PatternMatcher[] getUriPermissionPatterns() {
-        return uriPermissionPatterns;
-    }
+    @Nullable String getWritePermission();
 
-    @Nullable
-    public PathPermission[] getPathPermissions() {
-        return pathPermissions;
-    }
+    boolean isForceUriPermissions();
+
+    boolean isGrantUriPermissions();
+
+    boolean isSyncable();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java b/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java
new file mode 100644
index 0000000..774c3fc
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PathPermission;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+@DataClass(genSetters = true, genGetters = true, genParcelable = false, genBuilder = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedProviderImpl extends ParsedMainComponentImpl implements ParsedProvider {
+
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String authority;
+    private boolean syncable;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String readPermission;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String writePermission;
+    private boolean grantUriPermissions;
+    private boolean forceUriPermissions;
+    private boolean multiProcess;
+    private int initOrder;
+    @Nullable
+    private PatternMatcher[] uriPermissionPatterns;
+    @Nullable
+    private PathPermission[] pathPermissions;
+
+    public ParsedProviderImpl(ParsedProvider other) {
+        super(other);
+
+        this.authority = other.getAuthority();
+        this.syncable = other.isSyncable();
+        this.readPermission = other.getReadPermission();
+        this.writePermission = other.getWritePermission();
+        this.grantUriPermissions = other.isGrantUriPermissions();
+        this.forceUriPermissions = other.isForceUriPermissions();
+        this.multiProcess = other.isMultiProcess();
+        this.initOrder = other.getInitOrder();
+        this.uriPermissionPatterns = other.getUriPermissionPatterns();
+        this.pathPermissions = other.getPathPermissions();
+    }
+
+    public ParsedProviderImpl setReadPermission(String readPermission) {
+        // Empty string must be converted to null
+        this.readPermission = TextUtils.isEmpty(readPermission)
+                ? null : readPermission.intern();
+        return this;
+    }
+
+    public ParsedProviderImpl setWritePermission(String writePermission) {
+        // Empty string must be converted to null
+        this.writePermission = TextUtils.isEmpty(writePermission)
+                ? null : writePermission.intern();
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Provider{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(this.authority);
+        dest.writeBoolean(this.syncable);
+        sForInternedString.parcel(this.readPermission, dest, flags);
+        sForInternedString.parcel(this.writePermission, dest, flags);
+        dest.writeBoolean(this.grantUriPermissions);
+        dest.writeBoolean(this.forceUriPermissions);
+        dest.writeBoolean(this.multiProcess);
+        dest.writeInt(this.initOrder);
+        dest.writeTypedArray(this.uriPermissionPatterns, flags);
+        dest.writeTypedArray(this.pathPermissions, flags);
+    }
+
+    public ParsedProviderImpl() {
+    }
+
+    protected ParsedProviderImpl(Parcel in) {
+        super(in);
+        this.authority = in.readString();
+        this.syncable = in.readBoolean();
+        this.readPermission = sForInternedString.unparcel(in);
+        this.writePermission = sForInternedString.unparcel(in);
+        this.grantUriPermissions = in.readBoolean();
+        this.forceUriPermissions = in.readBoolean();
+        this.multiProcess = in.readBoolean();
+        this.initOrder = in.readInt();
+        this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
+        this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedProviderImpl> CREATOR =
+            new Parcelable.Creator<ParsedProviderImpl>() {
+                @Override
+                public ParsedProviderImpl createFromParcel(Parcel source) {
+                    return new ParsedProviderImpl(source);
+                }
+
+                @Override
+                public ParsedProviderImpl[] newArray(int size) {
+                    return new ParsedProviderImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedProviderImpl(
+            @Nullable String authority,
+            boolean syncable,
+            @Nullable String readPermission,
+            @Nullable String writePermission,
+            boolean grantUriPermissions,
+            boolean forceUriPermissions,
+            boolean multiProcess,
+            int initOrder,
+            @Nullable PatternMatcher[] uriPermissionPatterns,
+            @Nullable PathPermission[] pathPermissions) {
+        this.authority = authority;
+        this.syncable = syncable;
+        this.readPermission = readPermission;
+        this.writePermission = writePermission;
+        this.grantUriPermissions = grantUriPermissions;
+        this.forceUriPermissions = forceUriPermissions;
+        this.multiProcess = multiProcess;
+        this.initOrder = initOrder;
+        this.uriPermissionPatterns = uriPermissionPatterns;
+        this.pathPermissions = pathPermissions;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getAuthority() {
+        return authority;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isSyncable() {
+        return syncable;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getReadPermission() {
+        return readPermission;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getWritePermission() {
+        return writePermission;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isGrantUriPermissions() {
+        return grantUriPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isForceUriPermissions() {
+        return forceUriPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public boolean isMultiProcess() {
+        return multiProcess;
+    }
+
+    @DataClass.Generated.Member
+    public int getInitOrder() {
+        return initOrder;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable PatternMatcher[] getUriPermissionPatterns() {
+        return uriPermissionPatterns;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable PathPermission[] getPathPermissions() {
+        return pathPermissions;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setAuthority(@NonNull String value) {
+        authority = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setSyncable( boolean value) {
+        syncable = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setGrantUriPermissions( boolean value) {
+        grantUriPermissions = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setForceUriPermissions( boolean value) {
+        forceUriPermissions = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setMultiProcess( boolean value) {
+        multiProcess = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setInitOrder( int value) {
+        initOrder = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setUriPermissionPatterns(@NonNull PatternMatcher... value) {
+        uriPermissionPatterns = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedProviderImpl setPathPermissions(@NonNull PathPermission... value) {
+        pathPermissions = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627590522169L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProviderImpl.java",
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate  boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate  boolean grantUriPermissions\nprivate  boolean forceUriPermissions\nprivate  boolean multiProcess\nprivate  int initOrder\nprivate @android.annotation.Nullable android.os.PatternMatcher[] uriPermissionPatterns\nprivate @android.annotation.Nullable android.content.pm.PathPermission[] pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedProviderImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedProvider]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
index c4b8b98..de9dd44 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -20,6 +20,8 @@
 import static android.content.pm.parsing.component.ComponentParseUtils.flag;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
 import android.content.pm.PathPermission;
 import android.content.pm.ProviderInfo;
 import android.content.pm.parsing.ParsingPackage;
@@ -49,35 +51,35 @@
     @NonNull
     public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
-            boolean useRoundIcon, ParseInput input)
+            boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)
             throws IOException, XmlPullParserException {
         String authority;
         boolean visibleToEphemeral;
 
         final int targetSdkVersion = pkg.getTargetSdkVersion();
         final String packageName = pkg.getPackageName();
-        final ParsedProvider provider = new ParsedProvider();
+        final ParsedProviderImpl provider = new ParsedProviderImpl();
         final String tag = parser.getName();
 
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider);
         try {
-            ParseResult<ParsedProvider> result =
+            ParseResult<ParsedProviderImpl> result =
                     ParsedMainComponentUtils.parseMainComponent(provider, tag, separateProcesses,
-                    pkg, sa, flags, useRoundIcon, input,
-                    R.styleable.AndroidManifestProvider_banner,
-                    R.styleable.AndroidManifestProvider_description,
-                    R.styleable.AndroidManifestProvider_directBootAware,
-                    R.styleable.AndroidManifestProvider_enabled,
-                    R.styleable.AndroidManifestProvider_icon,
-                    R.styleable.AndroidManifestProvider_label,
-                    R.styleable.AndroidManifestProvider_logo,
-                    R.styleable.AndroidManifestProvider_name,
-                    R.styleable.AndroidManifestProvider_process,
-                    R.styleable.AndroidManifestProvider_roundIcon,
-                    R.styleable.AndroidManifestProvider_splitName,
-                    R.styleable.AndroidManifestProvider_attributionTags);
+                            pkg, sa, flags, useRoundIcon, defaultSplitName, input,
+                            R.styleable.AndroidManifestProvider_banner,
+                            R.styleable.AndroidManifestProvider_description,
+                            R.styleable.AndroidManifestProvider_directBootAware,
+                            R.styleable.AndroidManifestProvider_enabled,
+                            R.styleable.AndroidManifestProvider_icon,
+                            R.styleable.AndroidManifestProvider_label,
+                            R.styleable.AndroidManifestProvider_logo,
+                            R.styleable.AndroidManifestProvider_name,
+                            R.styleable.AndroidManifestProvider_process,
+                            R.styleable.AndroidManifestProvider_roundIcon,
+                            R.styleable.AndroidManifestProvider_splitName,
+                            R.styleable.AndroidManifestProvider_attributionTags);
             if (result.isError()) {
-                return result;
+                return input.error(result);
             }
 
             authority = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_authorities, 0);
@@ -156,7 +158,7 @@
     @NonNull
     private static ParseResult<ParsedProvider> parseProviderTags(ParsingPackage pkg, String tag,
             Resources res, XmlResourceParser parser, boolean visibleToEphemeral,
-            ParsedProvider provider, ParseInput input)
+            ParsedProviderImpl provider, ParseInput input)
             throws XmlPullParserException, IOException {
         final int depth = parser.getDepth();
         int type;
@@ -179,7 +181,8 @@
                     result = intentResult;
                     if (intentResult.isSuccess()) {
                         ParsedIntentInfo intent = intentResult.getResult();
-                        provider.setOrder(Math.max(intent.getOrder(), provider.getOrder()));
+                        IntentFilter intentFilter = intent.getIntentFilter();
+                        provider.setOrder(Math.max(intentFilter.getOrder(), provider.getOrder()));
                         provider.addIntent(intent);
                     }
                     break;
@@ -211,7 +214,7 @@
     }
 
     @NonNull
-    private static ParseResult<ParsedProvider> parseGrantUriPermission(ParsedProvider provider,
+    private static ParseResult<ParsedProvider> parseGrantUriPermission(ParsedProviderImpl provider,
             ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
         TypedArray sa = resources.obtainAttributes(parser,
                 R.styleable.AndroidManifestGrantUriPermission);
@@ -277,7 +280,7 @@
     }
 
     @NonNull
-    private static ParseResult<ParsedProvider> parsePathPermission(ParsedProvider provider,
+    private static ParseResult<ParsedProvider> parsePathPermission(ParsedProviderImpl provider,
             ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
         TypedArray sa = resources.obtainAttributes(parser,
                 R.styleable.AndroidManifestPathPermission);
diff --git a/core/java/android/content/pm/parsing/component/ParsedService.java b/core/java/android/content/pm/parsing/component/ParsedService.java
index 5499a13..6736afa 100644
--- a/core/java/android/content/pm/parsing/component/ParsedService.java
+++ b/core/java/android/content/pm/parsing/component/ParsedService.java
@@ -16,93 +16,13 @@
 
 package android.content.pm.parsing.component;
 
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
-import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
 /** @hide **/
-public class ParsedService extends ParsedMainComponent {
+public interface ParsedService extends ParsedMainComponent {
 
-    private int foregroundServiceType;
-    @Nullable
-    @DataClass.ParcelWith(ForInternedString.class)
-    private String permission;
-
-    public ParsedService(ParsedService other) {
-        super(other);
-        this.foregroundServiceType = other.foregroundServiceType;
-        this.permission = other.permission;
-    }
-
-    public ParsedService setForegroundServiceType(int foregroundServiceType) {
-        this.foregroundServiceType = foregroundServiceType;
-        return this;
-    }
-
-    public ParsedMainComponent setPermission(String permission) {
-        // Empty string must be converted to null
-        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
-        return this;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Service{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(' ');
-        ComponentName.appendShortString(sb, getPackageName(), getName());
-        sb.append('}');
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeInt(this.foregroundServiceType);
-        sForInternedString.parcel(this.permission, dest, flags);
-    }
-
-    public ParsedService() {
-    }
-
-    protected ParsedService(Parcel in) {
-        super(in);
-        this.foregroundServiceType = in.readInt();
-        this.permission = sForInternedString.unparcel(in);
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<ParsedService> CREATOR = new Creator<ParsedService>() {
-        @Override
-        public ParsedService createFromParcel(Parcel source) {
-            return new ParsedService(source);
-        }
-
-        @Override
-        public ParsedService[] newArray(int size) {
-            return new ParsedService[size];
-        }
-    };
-
-    public int getForegroundServiceType() {
-        return foregroundServiceType;
-    }
+    int getForegroundServiceType();
 
     @Nullable
-    public String getPermission() {
-        return permission;
-    }
+    String getPermission();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java b/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java
new file mode 100644
index 0000000..a85fb5c
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 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.content.pm.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+@DataClass(genSetters = true, genGetters = true, genParcelable = false, genBuilder = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedServiceImpl extends ParsedMainComponentImpl implements ParsedService {
+
+    private int foregroundServiceType;
+    @Nullable
+    @DataClass.ParcelWith(ForInternedString.class)
+    private String permission;
+
+    public ParsedServiceImpl(ParsedServiceImpl other) {
+        super(other);
+        this.foregroundServiceType = other.foregroundServiceType;
+        this.permission = other.permission;
+    }
+
+    public ParsedMainComponent setPermission(String permission) {
+        // Empty string must be converted to null
+        this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
+        return this;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Service{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(' ');
+        ComponentName.appendShortString(sb, getPackageName(), getName());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(this.foregroundServiceType);
+        sForInternedString.parcel(this.permission, dest, flags);
+    }
+
+    public ParsedServiceImpl() {
+    }
+
+    protected ParsedServiceImpl(Parcel in) {
+        super(in);
+        this.foregroundServiceType = in.readInt();
+        this.permission = sForInternedString.unparcel(in);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParsedServiceImpl> CREATOR =
+            new Parcelable.Creator<ParsedServiceImpl>() {
+                @Override
+                public ParsedServiceImpl createFromParcel(Parcel source) {
+                    return new ParsedServiceImpl(source);
+                }
+
+                @Override
+                public ParsedServiceImpl[] newArray(int size) {
+                    return new ParsedServiceImpl[size];
+                }
+            };
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedServiceImpl(
+            int foregroundServiceType,
+            @Nullable String permission) {
+        this.foregroundServiceType = foregroundServiceType;
+        this.permission = permission;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public int getForegroundServiceType() {
+        return foregroundServiceType;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getPermission() {
+        return permission;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedServiceImpl setForegroundServiceType( int value) {
+        foregroundServiceType = value;
+        return this;
+    }
+
+    @DataClass.Generated(
+            time = 1627592563052L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java",
+            inputSignatures = "private  int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedServiceImpl> CREATOR\npublic  android.content.pm.parsing.component.ParsedMainComponent setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedService]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
index 59267f9..d27a0ed 100644
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -19,6 +19,8 @@
 import static android.content.pm.parsing.component.ComponentParseUtils.flag;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.parsing.ParsingPackage;
@@ -45,19 +47,20 @@
     @NonNull
     public static ParseResult<ParsedService> parseService(String[] separateProcesses,
             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
-            boolean useRoundIcon, ParseInput input)
+            boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)
             throws XmlPullParserException, IOException {
         boolean visibleToEphemeral;
         boolean setExported;
 
         final String packageName = pkg.getPackageName();
-        final ParsedService service = new ParsedService();
+        final ParsedServiceImpl service = new ParsedServiceImpl();
         String tag = parser.getName();
 
         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestService);
         try {
-            ParseResult<ParsedService> result = ParsedMainComponentUtils.parseMainComponent(
-                    service, tag, separateProcesses, pkg, sa, flags, useRoundIcon, input,
+            ParseResult<ParsedServiceImpl> result = ParsedMainComponentUtils.parseMainComponent(
+                    service, tag, separateProcesses, pkg, sa, flags, useRoundIcon, defaultSplitName,
+                    input,
                     R.styleable.AndroidManifestService_banner,
                     R.styleable.AndroidManifestService_description,
                     R.styleable.AndroidManifestService_directBootAware,
@@ -73,7 +76,7 @@
             );
 
             if (result.isError()) {
-                return result;
+                return input.error(result);
             }
 
             setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
@@ -138,7 +141,8 @@
                     parseResult = intentResult;
                     if (intentResult.isSuccess()) {
                         ParsedIntentInfo intent = intentResult.getResult();
-                        service.setOrder(Math.max(intent.getOrder(), service.getOrder()));
+                        IntentFilter intentFilter = intent.getIntentFilter();
+                        service.setOrder(Math.max(intentFilter.getOrder(), service.getOrder()));
                         service.addIntent(intent);
                     }
                     break;
diff --git a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
index 020784d..e2f5f14 100644
--- a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
@@ -16,17 +16,11 @@
 
 package android.content.pm.parsing.component;
 
-import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.pm.PackageInfo;
-import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -36,24 +30,14 @@
  *
  * @hide
  */
-@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
-        genAidl = false)
-public class ParsedUsesPermission implements Parcelable {
-
-    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
-    @NonNull
-    private String name;
-
-    @UsesPermissionFlags
-    private int usesPermissionFlags;
+public interface ParsedUsesPermission extends Parcelable {
 
     /**
      * Strong assertion by a developer that they will never use this permission to derive the
      * physical location of the device, regardless of ACCESS_FINE_LOCATION and/or
      * ACCESS_COARSE_LOCATION being granted.
      */
-    public static final int FLAG_NEVER_FOR_LOCATION =
-            PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
+    int FLAG_NEVER_FOR_LOCATION = PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
 
     /**
      * @hide
@@ -62,132 +46,11 @@
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_NEVER_FOR_LOCATION
     })
-    public @interface UsesPermissionFlags {}
+    @interface UsesPermissionFlags {}
 
+    @NonNull
+    String getName();
 
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    public ParsedUsesPermission(
-            @NonNull String name,
-            @UsesPermissionFlags int usesPermissionFlags) {
-        this.name = name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.usesPermissionFlags = usesPermissionFlags;
-        com.android.internal.util.AnnotationValidations.validate(
-                UsesPermissionFlags.class, null, usesPermissionFlags);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return name;
-    }
-
-    @DataClass.Generated.Member
-    public @UsesPermissionFlags int getUsesPermissionFlags() {
-        return usesPermissionFlags;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedUsesPermission setName(@NonNull String value) {
-        name = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull ParsedUsesPermission setUsesPermissionFlags(@UsesPermissionFlags int value) {
-        usesPermissionFlags = value;
-        com.android.internal.util.AnnotationValidations.validate(
-                UsesPermissionFlags.class, null, usesPermissionFlags);
-        return this;
-    }
-
-    @DataClass.Generated.Member
-    static Parcelling<String> sParcellingForName =
-            Parcelling.Cache.get(
-                    Parcelling.BuiltIn.ForInternedString.class);
-    static {
-        if (sParcellingForName == null) {
-            sParcellingForName = Parcelling.Cache.put(
-                    new Parcelling.BuiltIn.ForInternedString());
-        }
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        sParcellingForName.parcel(name, dest, flags);
-        dest.writeInt(usesPermissionFlags);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    protected ParsedUsesPermission(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        String _name = sParcellingForName.unparcel(in);
-        int _usesPermissionFlags = in.readInt();
-
-        this.name = _name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, name);
-        this.usesPermissionFlags = _usesPermissionFlags;
-        com.android.internal.util.AnnotationValidations.validate(
-                UsesPermissionFlags.class, null, usesPermissionFlags);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ParsedUsesPermission> CREATOR
-            = new Parcelable.Creator<ParsedUsesPermission>() {
-        @Override
-        public ParsedUsesPermission[] newArray(int size) {
-            return new ParsedUsesPermission[size];
-        }
-
-        @Override
-        public ParsedUsesPermission createFromParcel(@NonNull Parcel in) {
-            return new ParsedUsesPermission(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1626207990753L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java",
-            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\npublic static final  int FLAG_NEVER_FOR_LOCATION\nclass ParsedUsesPermission extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
+    @UsesPermissionFlags
+    int getUsesPermissionFlags();
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java b/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java
new file mode 100644
index 0000000..d3c7afb
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java
@@ -0,0 +1,171 @@
+/*
+ * 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 android.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestUsesPermission
+ * &lt;uses-permission&gt;} tag parsed from the manifest.
+ *
+ * @hide
+ */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
+        genAidl = false)
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedUsesPermissionImpl implements ParsedUsesPermission {
+
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+    @NonNull
+    private String name;
+
+    @ParsedUsesPermission.UsesPermissionFlags
+    private int usesPermissionFlags;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public ParsedUsesPermissionImpl(
+            @NonNull String name,
+            @ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags) {
+        this.name = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.usesPermissionFlags = usesPermissionFlags;
+        com.android.internal.util.AnnotationValidations.validate(
+                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return name;
+    }
+
+    @DataClass.Generated.Member
+    public @ParsedUsesPermission.UsesPermissionFlags int getUsesPermissionFlags() {
+        return usesPermissionFlags;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedUsesPermissionImpl setName(@NonNull String value) {
+        name = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ParsedUsesPermissionImpl setUsesPermissionFlags(@ParsedUsesPermission.UsesPermissionFlags int value) {
+        usesPermissionFlags = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    static Parcelling<String> sParcellingForName =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForInternedString.class);
+    static {
+        if (sParcellingForName == null) {
+            sParcellingForName = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForInternedString());
+        }
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        sParcellingForName.parcel(name, dest, flags);
+        dest.writeInt(usesPermissionFlags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ParsedUsesPermissionImpl(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String _name = sParcellingForName.unparcel(in);
+        int _usesPermissionFlags = in.readInt();
+
+        this.name = _name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, name);
+        this.usesPermissionFlags = _usesPermissionFlags;
+        com.android.internal.util.AnnotationValidations.validate(
+                ParsedUsesPermission.UsesPermissionFlags.class, null, usesPermissionFlags);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ParsedUsesPermissionImpl> CREATOR
+            = new Parcelable.Creator<ParsedUsesPermissionImpl>() {
+        @Override
+        public ParsedUsesPermissionImpl[] newArray(int size) {
+            return new ParsedUsesPermissionImpl[size];
+        }
+
+        @Override
+        public ParsedUsesPermissionImpl createFromParcel(@NonNull Parcel in) {
+            return new ParsedUsesPermissionImpl(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1627674645598L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java",
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedUsesPermission]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
index 7370823..b70353a4 100644
--- a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
+++ b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
@@ -17,8 +17,11 @@
 package android.content.pm.permission;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.content.pm.parsing.component.ParsedUsesPermission;
 
+import com.android.internal.util.DataClass;
+
 /**
  * Implements compatibility support for permissions, and old applications
  * will be automatically granted it.
@@ -32,9 +35,12 @@
  *
  * @hide
  */
-public class CompatibilityPermissionInfo extends ParsedUsesPermission {
+@DataClass(genGetters = true, genBuilder = false)
+public class CompatibilityPermissionInfo {
 
-    public final int sdkVersion;
+    @NonNull
+    private final String mName;
+    private final int mSdkVersion;
 
     /**
      * List of new permissions that have been added since 1.0.
@@ -47,16 +53,60 @@
     public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
             new CompatibilityPermissionInfo[]{
                     new CompatibilityPermissionInfo(Manifest.permission.POST_NOTIFICATIONS,
-                            android.os.Build.VERSION_CODES.TIRAMISU, 0 /*usesPermissionFlags*/),
+                            android.os.Build.VERSION_CODES.TIRAMISU),
                     new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
-                            android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/),
+                            android.os.Build.VERSION_CODES.DONUT),
                     new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
-                            android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/)
+                            android.os.Build.VERSION_CODES.DONUT)
             };
 
-    private CompatibilityPermissionInfo(String name, int sdkVersion,
-            @UsesPermissionFlags int usesPermissionFlags) {
-        super(name, usesPermissionFlags);
-        this.sdkVersion = sdkVersion;
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public CompatibilityPermissionInfo(
+            @NonNull String name,
+            int sdkVersion) {
+        this.mName = name;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mName);
+        this.mSdkVersion = sdkVersion;
+
+        // onConstructed(); // You can define this method to get a callback
     }
+
+    @DataClass.Generated.Member
+    public @NonNull String getName() {
+        return mName;
+    }
+
+    @DataClass.Generated.Member
+    public int getSdkVersion() {
+        return mSdkVersion;
+    }
+
+    @DataClass.Generated(
+            time = 1627674427184L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final  int mSdkVersion\npublic static final  android.content.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 12e41e2..a6f2e40 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -27,6 +27,7 @@
 import android.annotation.ColorInt;
 import android.annotation.ColorRes;
 import android.annotation.DimenRes;
+import android.annotation.Discouraged;
 import android.annotation.DrawableRes;
 import android.annotation.FontRes;
 import android.annotation.FractionRes;
@@ -1466,6 +1467,12 @@
      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
      *
      */
+    @Discouraged(message = "Use of this function is discouraged because it makes internal calls to "
+                         + "`getIdentifier()`, which uses resource reflection. Reflection makes it "
+                         + "harder to perform build optimizations and compile-time verification of "
+                         + "code. It is much more efficient to retrieve resource values by "
+                         + "identifier (e.g. `getValue(R.foo.bar, outValue, true)`) than by name "
+                         + "(e.g. `getValue(\"foo\", outvalue, true)`).")
     public void getValue(String name, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
         mResourcesImpl.getValue(name, outValue, resolveRefs);
@@ -2198,6 +2205,11 @@
      * @return int The associated resource identifier.  Returns 0 if no such
      *         resource was found.  (0 is not a valid resource ID.)
      */
+    @Discouraged(message = "Use of this function is discouraged because resource reflection makes "
+                         + "it harder to perform build optimizations and compile-time "
+                         + "verification of code. It is much more efficient to retrieve "
+                         + "resources by identifier (e.g. `R.foo.bar`) than by name (e.g. "
+                         + "`getIdentifier(\"bar\", \"foo\", null)`).")
     public int getIdentifier(String name, String defType, String defPackage) {
         return mResourcesImpl.getIdentifier(name, defType, defPackage);
     }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 9f77a7e..afea4e5 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1320,6 +1320,42 @@
             new Key<Boolean>("android.flash.info.available", boolean.class);
 
     /**
+     * <p>Maximum flashlight brightness level.</p>
+     * <p>If this value is greater than 1, then the device supports controlling the
+     * flashlight brightness level via
+     * {android.hardware.camera2.CameraManager#setTorchStrengthLevel}.
+     * If this value is equal to 1, flashlight brightness control is not supported.
+     * This value will be -1 if the flash unit is not available.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<Integer> FLASH_INFO_STRENGTH_MAXIMUM_LEVEL =
+            new Key<Integer>("android.flash.info.strengthMaximumLevel", int.class);
+
+    /**
+     * <p>Default flashlight brightness level to be set via
+     * {android.hardware.camera2.CameraManager#setTorchStrengthLevel}.</p>
+     * <p>If flash unit is available this will be greater than or equal to 1 and less
+     * or equal to <code>{@link CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL android.flash.info.strengthMaximumLevel}</code>.
+     * If flash unit is not available this will be set to -1.</p>
+     * <p>Setting flashlight brightness above the default level
+     * (i.e.<code>{@link CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL android.flash.info.strengthDefaultLevel}</code>) may make the device more
+     * likely to reach thermal throttling conditions and slow down, or drain the
+     * battery quicker than normal. To minimize such issues, it is recommended to
+     * start the flashlight at this default brightness until a user explicitly requests
+     * a brighter level.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL
+     * @see CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL
+     */
+    @PublicKey
+    @NonNull
+    public static final Key<Integer> FLASH_INFO_STRENGTH_DEFAULT_LEVEL =
+            new Key<Integer>("android.flash.info.strengthDefaultLevel", int.class);
+
+    /**
      * <p>List of hot pixel correction modes for {@link CaptureRequest#HOT_PIXEL_MODE android.hotPixel.mode} that are supported by this
      * camera device.</p>
      * <p>FULL mode camera devices will always support FAST.</p>
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 0276815..08276ac 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1022,8 +1022,7 @@
      * <p>which define a transform from input sensor colors, <code>P_in = [ r g b ]</code>,
      * to output linear sRGB, <code>P_out = [ r' g' b' ]</code>,</p>
      * <p>with colors as follows:</p>
-     * <pre><code>
-     * r' = I0r + I1g + I2b
+     * <pre><code>r' = I0r + I1g + I2b
      * g' = I3r + I4g + I5b
      * b' = I6r + I7g + I8b
      * </code></pre>
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index 1a52f11..cee6a5b 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -63,6 +63,9 @@
         if (listener == null) {
             throw new IllegalArgumentException("listener must not be null.");
         }
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null.");
+        }
         try {
             mService.deviceSelect(logicalAddress,
                     getCallbackWrapper(logicalAddress, executor, listener));
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 0a5496e..5874385 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -115,8 +115,6 @@
     public static final int POWER_STATUS_TRANSIENT_TO_ON = 2;
     public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3;
 
-    /** @hide */
-    @SystemApi
     @IntDef ({
         RESULT_SUCCESS,
         RESULT_TIMEOUT,
@@ -297,19 +295,19 @@
     /**
      * HDMI CEC enabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_ENABLED
      */
-    @SystemApi
     public static final int HDMI_CEC_CONTROL_ENABLED = 1;
     /**
      * HDMI CEC disabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_ENABLED
      */
-    @SystemApi
     public static final int HDMI_CEC_CONTROL_DISABLED = 0;
     /**
      * @hide
+     *
+     * @see HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_ENABLED
      */
     @IntDef(prefix = { "HDMI_CEC_CONTROL_" }, value = {
             HDMI_CEC_CONTROL_ENABLED,
@@ -322,18 +320,17 @@
     /**
      * Version constant for HDMI-CEC v1.4b.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_VERSION
      */
-    @SystemApi
     public static final int HDMI_CEC_VERSION_1_4_B = 0x05;
     /**
      * Version constant for HDMI-CEC v2.0.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_VERSION
      */
-    @SystemApi
     public static final int HDMI_CEC_VERSION_2_0 = 0x06;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_VERSION
      * @hide
      */
     @IntDef(prefix = { "HDMI_CEC_VERSION_" }, value = {
@@ -347,18 +344,17 @@
     /**
      * Routing Control feature enabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_ROUTING_CONTROL
      */
-    @SystemApi
     public static final int ROUTING_CONTROL_ENABLED = 1;
     /**
      * Routing Control feature disabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_ROUTING_CONTROL
      */
-    @SystemApi
     public static final int ROUTING_CONTROL_DISABLED = 0;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_ROUTING_CONTROL
      * @hide
      */
     @IntDef(prefix = { "ROUTING_CONTROL_" }, value = {
@@ -375,9 +371,8 @@
      * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} but do not turn on the
      * Audio system via {@code <System Audio Mode Request>}.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_CONTROL_MODE
      */
-    @SystemApi
     public static final String POWER_CONTROL_MODE_TV = "to_tv";
     /**
      * Send CEC power control messages to TV and Audio System:
@@ -385,9 +380,8 @@
      * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
      * the Audio system via {@code <System Audio Mode Request>}.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_CONTROL_MODE
      */
-    @SystemApi
     public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
     /**
      * Broadcast CEC power control messages to all devices in the network:
@@ -395,9 +389,8 @@
      * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
      * the Audio system via {@code <System Audio Mode Request>}.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_CONTROL_MODE
      */
-    @SystemApi
     public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
     /**
      * Don't send any CEC power control messages:
@@ -405,11 +398,11 @@
      * Upon waking up, do not turn on the TV via {@code <One Touch Play>} and do not turn on the
      * Audio system via {@code <System Audio Mode Request>}.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_CONTROL_MODE
      */
-    @SystemApi
     public static final String POWER_CONTROL_MODE_NONE = "none";
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_CONTROL_MODE
      * @hide
      */
     @StringDef(prefix = { "POWER_CONTROL_MODE_" }, value = {
@@ -425,18 +418,17 @@
     /**
      * No action to be taken.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST
      */
-    @SystemApi
     public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
     /**
      * Go to standby immediately.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST
      */
-    @SystemApi
     public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST
      * @hide
      */
     @StringDef(prefix = { "POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_" }, value = {
@@ -450,18 +442,17 @@
     /**
      * System Audio Control enabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL
      */
-    @SystemApi
     public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1;
     /**
      * System Audio Control disabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL
      */
-    @SystemApi
     public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL
      * @hide
      */
     @IntDef(prefix = { "SYSTEM_AUDIO_CONTROL_" }, value = {
@@ -475,18 +466,17 @@
     /**
      * System Audio Mode muting enabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING
      */
-    @SystemApi
     public static final int SYSTEM_AUDIO_MODE_MUTING_ENABLED = 1;
     /**
      * System Audio Mode muting disabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING
      */
-    @SystemApi
     public static final int SYSTEM_AUDIO_MODE_MUTING_DISABLED = 0;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING
      * @hide
      */
     @IntDef(prefix = { "SYSTEM_AUDIO_MODE_MUTING_" }, value = {
@@ -501,17 +491,13 @@
      * HDMI CEC enabled.
      *
      * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
-     * @hide
      */
-    @SystemApi
     public static final int VOLUME_CONTROL_ENABLED = 1;
     /**
      * HDMI CEC disabled.
      *
      * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
-     * @hide
      */
-    @SystemApi
     public static final int VOLUME_CONTROL_DISABLED = 0;
     /**
      * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
@@ -528,18 +514,17 @@
     /**
      * TV Wake on One Touch Play enabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY
      */
-    @SystemApi
     public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED = 1;
     /**
      * TV Wake on One Touch Play disabled.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY
      */
-    @SystemApi
     public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED = 0;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY
      * @hide
      */
     @IntDef(prefix = { "TV_WAKE_ON_ONE_TOUCH_PLAY_" }, value = {
@@ -553,18 +538,17 @@
     /**
      * Sending &lt;Standby&gt; on sleep.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP
      */
-    @SystemApi
     public static final int TV_SEND_STANDBY_ON_SLEEP_ENABLED = 1;
     /**
      * Not sending &lt;Standby&gt; on sleep.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP
      */
-    @SystemApi
     public static final int TV_SEND_STANDBY_ON_SLEEP_DISABLED = 0;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP
      * @hide
      */
     @IntDef(prefix = { "TV_SEND_STANDBY_ON_SLEEP_" }, value = {
@@ -578,34 +562,40 @@
     /**
      * RC profile none.
      *
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_TV
      * @hide
      */
     public static final int RC_PROFILE_TV_NONE = 0x0;
     /**
      * RC profile 1.
      *
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_TV
      * @hide
      */
     public static final int RC_PROFILE_TV_ONE = 0x2;
     /**
      * RC profile 2.
      *
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_TV
      * @hide
      */
     public static final int RC_PROFILE_TV_TWO = 0x6;
     /**
      * RC profile 3.
      *
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_TV
      * @hide
      */
     public static final int RC_PROFILE_TV_THREE = 0xA;
     /**
      * RC profile 4.
      *
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_TV
      * @hide
      */
     public static final int RC_PROFILE_TV_FOUR = 0xE;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_TV
      * @hide
      */
     @IntDef(prefix = { "RC_PROFILE_TV_" }, value = {
@@ -618,175 +608,157 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface RcProfileTv {}
 
-    // -- RC profile parameter defining if a source handles the root menu.
+    // -- RC profile parameter defining if a source handles a specific menu.
     /**
-     * Handles the root menu.
+     * Handles the menu.
      *
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU
+     * @see HdmiControlManager#
+     * CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU
      * @hide
      */
-    public static final int RC_PROFILE_SOURCE_ROOT_MENU_HANDLED = 1;
+    public static final int RC_PROFILE_SOURCE_MENU_HANDLED = 1;
     /**
-     * Doesn't handle the root menu.
+     * Doesn't handle the menu.
      *
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU
+     * @see HdmiControlManager#
+     * CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU
      * @hide
      */
-    public static final int RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED = 0;
+    public static final int RC_PROFILE_SOURCE_MENU_NOT_HANDLED = 0;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU
+     * @see HdmiControlManager#CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU
+     * @see HdmiControlManager#
+     * CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU
      * @hide
      */
-    @IntDef(prefix = { "RC_PROFILE_SOURCE_ROOT_MENU_" }, value = {
-            RC_PROFILE_SOURCE_ROOT_MENU_HANDLED,
-            RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED
+    @IntDef(prefix = { "RC_PROFILE_SOURCE_MENU_" }, value = {
+            RC_PROFILE_SOURCE_MENU_HANDLED,
+            RC_PROFILE_SOURCE_MENU_NOT_HANDLED
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface RcProfileSourceHandlesRootMenu {}
+    public @interface RcProfileSourceHandlesMenu {}
 
-    // -- RC profile parameter defining if a source handles the setup menu.
+    // -- Whether the Short Audio Descriptor (SAD) for a specific codec should be queried or not.
     /**
-     * Handles the setup menu.
+     * Query the SAD.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_LPCM
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MPEG1
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MP3
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MPEG2
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_AAC
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DTS
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_ATRAC
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DDP
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DTSHD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_TRUEHD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DST
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_WMAPRO
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MAX
      */
-    public static final int RC_PROFILE_SOURCE_SETUP_MENU_HANDLED = 1;
+    public static final int QUERY_SAD_ENABLED = 1;
     /**
-     * Doesn't handle the setup menu.
+     * Don't query the SAD.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_LPCM
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MPEG1
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MP3
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MPEG2
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_AAC
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DTS
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_ATRAC
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DDP
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DTSHD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_TRUEHD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DST
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_WMAPRO
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MAX
      */
-    public static final int RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED = 0;
+    public static final int QUERY_SAD_DISABLED = 0;
     /**
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_LPCM
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MPEG1
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MP3
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MPEG2
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_AAC
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DTS
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_ATRAC
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DDP
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DTSHD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_TRUEHD
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_DST
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_WMAPRO
+     * @see HdmiControlManager#CEC_SETTING_NAME_QUERY_SAD_MAX
      * @hide
      */
-    @IntDef(prefix = { "RC_PROFILE_SOURCE_SETUP_MENU_" }, value = {
-            RC_PROFILE_SOURCE_SETUP_MENU_HANDLED,
-            RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED
+    @IntDef(prefix = { "QUERY_SAD_" }, value = {
+            QUERY_SAD_ENABLED,
+            QUERY_SAD_DISABLED
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface RcProfileSourceHandlesSetupMenu {}
-
-
-    // -- RC profile parameter defining if a source handles the contents menu.
-    /**
-     * Handles the contents menu.
-     *
-     * @hide
-     */
-    public static final int RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED = 1;
-    /**
-     * Doesn't handle the contents menu.
-     *
-     * @hide
-     */
-    public static final int RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED = 0;
-    /**
-     * @hide
-     */
-    @IntDef(prefix = { "RC_PROFILE_SOURCE_CONTENTS_MENU_" }, value = {
-            RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED,
-            RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RcProfileSourceHandlesContentsMenu {}
-
-
-    // -- RC profile parameter defining if a source handles the top menu.
-    /**
-     * Handles the top menu.
-     *
-     * @hide
-     */
-    public static final int RC_PROFILE_SOURCE_TOP_MENU_HANDLED = 1;
-    /**
-     * Doesn't handle the top menu.
-     *
-     * @hide
-     */
-    public static final int RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED = 0;
-    /**
-     * @hide
-     */
-    @IntDef(prefix = { "RC_PROFILE_SOURCE_TOP_MENU_" }, value = {
-            RC_PROFILE_SOURCE_TOP_MENU_HANDLED,
-            RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RcProfileSourceHandlesTopMenu {}
-
-
-    // -- RC profile parameter defining if a source handles the media context sensitive menu.
-    /**
-     * Handles the media context sensitive menu.
-     *
-     * @hide
-     */
-    public static final int RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED = 1;
-    /**
-     * Doesn't handle the media context sensitive menu.
-     *
-     * @hide
-     */
-    public static final int RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED = 0;
-    /**
-     * @hide
-     */
-    @IntDef(prefix = { "RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_" }, value = {
-            RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED,
-            RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RcProfileSourceHandlesMediaContextSensitiveMenu {}
+    public @interface SadPresenceInQuery {}
 
     // -- Settings available in the CEC Configuration.
     /**
      * Name of a setting deciding whether the CEC is enabled.
      *
-     * @hide
+     * @see HdmiControlManager#setHdmiCecEnabled(int)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_HDMI_CEC_ENABLED = "hdmi_cec_enabled";
     /**
      * Name of a setting controlling the version of HDMI-CEC used.
      *
-     * @hide
+     * @see HdmiControlManager#setHdmiCecVersion(int)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
     /**
      * Name of a setting deciding whether the Routing Control feature is enabled.
      *
-     * @hide
+     * @see HdmiControlManager#setRoutingControl(int)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
     /**
      * Name of a setting deciding on the power control mode.
      *
-     * @hide
+     * @see HdmiControlManager#setPowerControlMode(String)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "power_control_mode";
     /**
      * Name of a setting deciding on power state action when losing Active Source.
      *
-     * @hide
+     * @see HdmiControlManager#setPowerStateChangeOnActiveSourceLost(String)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
             "power_state_change_on_active_source_lost";
     /**
      * Name of a setting deciding whether System Audio Control is enabled.
      *
-     * @hide
+     * @see HdmiControlManager#setSystemAudioControl(int)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL =
             "system_audio_control";
     /**
      * Name of a setting deciding whether System Audio Muting is allowed.
      *
-     * @hide
+     * @see HdmiControlManager#setSystemAudioModeMuting(int)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING =
             "system_audio_mode_muting";
     /**
@@ -819,28 +791,24 @@
      *
      * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged.
      *
-     * @hide
-     * @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(int)
+     * @see HdmiControlManager#setHdmiCecVolumeControlEnabled(int)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_VOLUME_CONTROL_MODE =
             "volume_control_enabled";
     /**
      * Name of a setting deciding whether the TV will automatically turn on upon reception
      * of the CEC command &lt;Text View On&gt; or &lt;Image View On&gt;.
      *
-     * @hide
+     * @see HdmiControlManager#setTvWakeOnOneTouchPlay(int)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY =
             "tv_wake_on_one_touch_play";
     /**
      * Name of a setting deciding whether the TV will also turn off other CEC devices
      * when it goes to standby mode.
      *
-     * @hide
+     * @see HdmiControlManager#setTvSendStandbyOnSleep(int)
      */
-    @SystemApi
     public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP =
             "tv_send_standby_on_sleep";
     /**
@@ -892,6 +860,111 @@
             CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU =
             "rc_profile_source_handles_media_context_sensitive_menu";
     /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the LPCM codec
+     * (0x1) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_LPCM = "query_sad_lpcm";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the DD codec
+     * (0x2) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_DD = "query_sad_dd";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the MPEG1 codec
+     * (0x3) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_MPEG1 = "query_sad_mpeg1";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the MP3 codec
+     * (0x4) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_MP3 = "query_sad_mp3";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the MPEG2 codec
+     * (0x5) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_MPEG2 = "query_sad_mpeg2";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the AAC codec
+     * (0x6) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_AAC = "query_sad_aac";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the DTS codec
+     * (0x7) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_DTS = "query_sad_dts";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the ATRAC codec
+     * (0x8) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_ATRAC = "query_sad_atrac";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the ONEBITAUDIO
+     * codec (0x9) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO = "query_sad_onebitaudio";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the DDP codec
+     * (0xA) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_DDP = "query_sad_ddp";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the DTSHD codec
+     * (0xB) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_DTSHD = "query_sad_dtshd";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the TRUEHD codec
+     * (0xC) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_TRUEHD = "query_sad_truehd";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the DST codec
+     * (0xD) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_DST = "query_sad_dst";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the WMAPRO codec
+     * (0xE) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_WMAPRO = "query_sad_wmapro";
+    /**
+     * Name of a setting representing whether the Short Audio Descriptor (SAD) for the MAX codec
+     * (0xF) should be queried or not.
+     *
+     * @see HdmiControlManager#setSadPresenceInQuery(String, int)
+     */
+    public static final String CEC_SETTING_NAME_QUERY_SAD_MAX = "query_sad_max";
+    /**
      * @hide
      */
     @StringDef(prefix = { "CEC_SETTING_NAME_" }, value = {
@@ -910,9 +983,46 @@
         CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
         CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
         CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+        CEC_SETTING_NAME_QUERY_SAD_LPCM,
+        CEC_SETTING_NAME_QUERY_SAD_DD,
+        CEC_SETTING_NAME_QUERY_SAD_MPEG1,
+        CEC_SETTING_NAME_QUERY_SAD_MP3,
+        CEC_SETTING_NAME_QUERY_SAD_MPEG2,
+        CEC_SETTING_NAME_QUERY_SAD_AAC,
+        CEC_SETTING_NAME_QUERY_SAD_DTS,
+        CEC_SETTING_NAME_QUERY_SAD_ATRAC,
+        CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
+        CEC_SETTING_NAME_QUERY_SAD_DDP,
+        CEC_SETTING_NAME_QUERY_SAD_DTSHD,
+        CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
+        CEC_SETTING_NAME_QUERY_SAD_DST,
+        CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
+        CEC_SETTING_NAME_QUERY_SAD_MAX,
     })
     public @interface CecSettingName {}
 
+    /**
+     * @hide
+     */
+    @StringDef(prefix = { "CEC_SETTING_NAME_QUERY_SAD_" }, value = {
+            CEC_SETTING_NAME_QUERY_SAD_LPCM,
+            CEC_SETTING_NAME_QUERY_SAD_DD,
+            CEC_SETTING_NAME_QUERY_SAD_MPEG1,
+            CEC_SETTING_NAME_QUERY_SAD_MP3,
+            CEC_SETTING_NAME_QUERY_SAD_MPEG2,
+            CEC_SETTING_NAME_QUERY_SAD_AAC,
+            CEC_SETTING_NAME_QUERY_SAD_DTS,
+            CEC_SETTING_NAME_QUERY_SAD_ATRAC,
+            CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
+            CEC_SETTING_NAME_QUERY_SAD_DDP,
+            CEC_SETTING_NAME_QUERY_SAD_DTSHD,
+            CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
+            CEC_SETTING_NAME_QUERY_SAD_DST,
+            CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
+            CEC_SETTING_NAME_QUERY_SAD_MAX,
+    })
+    public @interface CecSettingSad {}
+
     // True if we have a logical device of type playback hosted in the system.
     private final boolean mHasPlaybackDevice;
     // True if we have a logical device of type TV hosted in the system.
@@ -966,11 +1076,8 @@
      * See {@link HdmiDeviceInfo#DEVICE_PLAYBACK}
      * See {@link HdmiDeviceInfo#DEVICE_TV}
      * See {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}
-     *
-     * @hide
      */
     @Nullable
-    @SystemApi
     @SuppressLint("RequiresPermission")
     public HdmiClient getClient(int type) {
         if (mService == null) {
@@ -999,11 +1106,8 @@
      * system if the system is configured to host more than one type of HDMI-CEC logical devices.
      *
      * @return {@link HdmiPlaybackClient} instance. {@code null} on failure.
-     *
-     * @hide
      */
     @Nullable
-    @SystemApi
     @SuppressLint("RequiresPermission")
     public HdmiPlaybackClient getPlaybackClient() {
         return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK);
@@ -1017,11 +1121,8 @@
      * system if the system is configured to host more than one type of HDMI-CEC logical devices.
      *
      * @return {@link HdmiTvClient} instance. {@code null} on failure.
-     *
-     * @hide
      */
     @Nullable
-    @SystemApi
     @SuppressLint("RequiresPermission")
     public HdmiTvClient getTvClient() {
         return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV);
@@ -1067,11 +1168,8 @@
      *
      * @return a list of {@link HdmiDeviceInfo} of the connected CEC devices on the CEC bus. An
      * empty list will be returned if there is none.
-     *
-     * @hide
      */
     @NonNull
-    @SystemApi
     public List<HdmiDeviceInfo> getConnectedDevices() {
         try {
             return mService.getDeviceList();
@@ -1082,11 +1180,9 @@
 
     /**
      * @removed
-     * @hide
      * @deprecated Please use {@link #getConnectedDevices()} instead.
      */
     @Deprecated
-    @SystemApi
     public List<HdmiDeviceInfo> getConnectedDevicesList() {
         try {
             return mService.getDeviceList();
@@ -1102,10 +1198,7 @@
      * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}.
      *
      * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered off.
-     *
-     * @hide
      */
-    @SystemApi
     public void powerOffDevice(@NonNull HdmiDeviceInfo deviceInfo) {
         Objects.requireNonNull(deviceInfo);
         try {
@@ -1118,11 +1211,9 @@
 
     /**
      * @removed
-     * @hide
      * @deprecated Please use {@link #powerOffDevice(deviceInfo)} instead.
      */
     @Deprecated
-    @SystemApi
     public void powerOffRemoteDevice(@NonNull HdmiDeviceInfo deviceInfo) {
         Objects.requireNonNull(deviceInfo);
         try {
@@ -1155,11 +1246,9 @@
 
     /**
      * @removed
-     * @hide
      * @deprecated Please use {@link #powerOnDevice(deviceInfo)} instead.
      */
     @Deprecated
-    @SystemApi
     public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) {
         Objects.requireNonNull(deviceInfo);
         try {
@@ -1180,10 +1269,7 @@
      * streaming on their TVs.
      *
      * @param deviceInfo HdmiDeviceInfo of the target device
-     *
-     * @hide
      */
-    @SystemApi
     public void setActiveSource(@NonNull HdmiDeviceInfo deviceInfo) {
         Objects.requireNonNull(deviceInfo);
         try {
@@ -1195,11 +1281,9 @@
 
     /**
      * @removed
-     * @hide
      * @deprecated Please use {@link #setActiveSource(deviceInfo)} instead.
      */
     @Deprecated
-    @SystemApi
     public void requestRemoteDeviceToBecomeActiveSource(@NonNull HdmiDeviceInfo deviceInfo) {
         Objects.requireNonNull(deviceInfo);
         try {
@@ -1292,9 +1376,7 @@
      *
      * @param hdmiCecVolumeControlEnabled target state of HDMI CEC volume control.
      * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setHdmiCecVolumeControlEnabled(
             @VolumeControl int hdmiCecVolumeControlEnabled) {
@@ -1308,9 +1390,9 @@
 
     /**
      * Returns whether volume changes via HDMI CEC are enabled.
-     * @hide
+     *
+     * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     @VolumeControl
     public int getHdmiCecVolumeControlEnabled() {
@@ -1340,10 +1422,7 @@
      * <p>Physical address needs to be automatically adjusted when devices are phyiscally or
      * electrically added or removed from the device tree. Please see HDMI Specification Version
      * 1.4b 8.7 Physical Address for more details on the address discovery proccess.
-     *
-     * @hide
      */
-    @SystemApi
     public int getPhysicalAddress() {
         try {
             return mService.getPhysicalAddress();
@@ -1360,10 +1439,7 @@
      * @param targetDevice {@link HdmiDeviceInfo} of the target device.
      * @return true if {@code targetDevice} is directly or indirectly
      * connected to the current device.
-     *
-     * @hide
      */
-    @SystemApi
     public boolean isDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) {
         Objects.requireNonNull(targetDevice);
         int physicalAddress = getPhysicalAddress();
@@ -1380,11 +1456,9 @@
 
     /**
      * @removed
-     * @hide
      * @deprecated Please use {@link #isDeviceConnected(targetDevice)} instead.
      */
     @Deprecated
-    @SystemApi
     public boolean isRemoteDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) {
         Objects.requireNonNull(targetDevice);
         int physicalAddress = getPhysicalAddress();
@@ -1401,10 +1475,7 @@
 
     /**
      * Listener used to get hotplug event from HDMI port.
-     *
-     * @hide
      */
-    @SystemApi
     public interface HotplugEventListener {
         void onReceived(HdmiHotplugEvent event);
     }
@@ -1456,10 +1527,7 @@
 
     /**
      * Listener used to get vendor-specific commands.
-     *
-     * @hide
      */
-    @SystemApi
     public interface VendorCommandListener {
         /**
          * Called when a vendor command is received.
@@ -1503,10 +1571,7 @@
      *
      * @param listener {@link HotplugEventListener} instance
      * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener)
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void addHotplugEventListener(HotplugEventListener listener) {
         addHotplugEventListener(ConcurrentUtils.DIRECT_EXECUTOR, listener);
@@ -1520,10 +1585,7 @@
      *
      * @param listener {@link HotplugEventListener} instance
      * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener)
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void addHotplugEventListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull HotplugEventListener listener) {
@@ -1549,10 +1611,7 @@
      * Removes a listener to stop getting informed of {@link HdmiHotplugEvent}.
      *
      * @param listener {@link HotplugEventListener} instance to be removed
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void removeHotplugEventListener(HotplugEventListener listener) {
         if (mService == null) {
@@ -1761,10 +1820,7 @@
 
     /**
      * Listener used to get setting change notification.
-     *
-     * @hide
      */
-    @SystemApi
     public interface CecSettingChangeListener {
         /**
          * Called when value of a setting changes.
@@ -1845,10 +1901,7 @@
      *
      * @return a set of user-modifiable settings.
      * @throws RuntimeException when the HdmiControlService is not available.
-     *
-     * @hide
      */
-    @SystemApi
     @NonNull
     @CecSettingName
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -1872,10 +1925,7 @@
      * @throws IllegalArgumentException when setting {@code name} does not exist.
      * @throws IllegalArgumentException when setting {@code name} value type is invalid.
      * @throws RuntimeException when the HdmiControlService is not available.
-     *
-     * @hide
      */
-    @SystemApi
     @NonNull
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public List<String> getAllowedCecSettingStringValues(@NonNull @CecSettingName String name) {
@@ -1898,10 +1948,7 @@
      * @throws IllegalArgumentException when setting {@code name} does not exist.
      * @throws IllegalArgumentException when setting {@code name} value type is invalid.
      * @throws RuntimeException when the HdmiControlService is not available.
-     *
-     * @hide
      */
-    @SystemApi
     @NonNull
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public List<Integer> getAllowedCecSettingIntValues(@NonNull @CecSettingName String name) {
@@ -1921,10 +1968,7 @@
      * Set the global status of HDMI CEC.
      *
      * <p>This allows to enable/disable HDMI CEC on the device.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setHdmiCecEnabled(@NonNull @HdmiCecControl int value) {
         if (mService == null) {
@@ -1942,10 +1986,7 @@
      * Get the current global status of HDMI CEC.
      *
      * <p>Reflects whether HDMI CEC is currently enabled on the device.
-     *
-     * @hide
      */
-    @SystemApi
     @NonNull
     @HdmiCecControl
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -1971,10 +2012,7 @@
      * Binder thread. This means that all callback implementations must be
      * thread safe. To specify the execution thread, use
      * {@link addHdmiCecEnabledChangeListener(Executor, CecSettingChangeListener)}.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void addHdmiCecEnabledChangeListener(@NonNull CecSettingChangeListener listener) {
         addHdmiCecEnabledChangeListener(ConcurrentUtils.DIRECT_EXECUTOR, listener);
@@ -1985,10 +2023,7 @@
      *
      * <p>To stop getting the notification,
      * use {@link #removeHdmiCecEnabledChangeListener(CecSettingChangeListener)}.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void addHdmiCecEnabledChangeListener(
             @NonNull @CallbackExecutor Executor executor,
@@ -1998,10 +2033,7 @@
 
     /**
      * Remove change listener for global status of HDMI CEC.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void removeHdmiCecEnabledChangeListener(
             @NonNull CecSettingChangeListener listener) {
@@ -2013,9 +2045,8 @@
      *
      * <p>Allows to select either CEC 1.4b or 2.0 to be used by the device.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_VERSION
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setHdmiCecVersion(@NonNull @HdmiCecVersion int value) {
         if (mService == null) {
@@ -2034,9 +2065,8 @@
      *
      * <p>Reflects which CEC version 1.4b or 2.0 is currently used by the device.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_HDMI_CEC_VERSION
      */
-    @SystemApi
     @NonNull
     @HdmiCecVersion
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2060,9 +2090,8 @@
      * receiving Routing Control related messages. If disabled, you can only
      * switch the input via controls on this device.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_ROUTING_CONTROL
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setRoutingControl(@NonNull @RoutingControl int value) {
         if (mService == null) {
@@ -2084,9 +2113,8 @@
      * receiving Routing Control related messages. If disabled, you can only
      * switch the input via controls on this device.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_ROUTING_CONTROL
      */
-    @SystemApi
     @NonNull
     @RoutingControl
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2108,9 +2136,8 @@
      * <p>Specifies to which devices Power Control messages should be sent:
      * only to the TV, broadcast to all devices, no power control messages.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_CONTROL_MODE
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setPowerControlMode(@NonNull @PowerControlMode String value) {
         if (mService == null) {
@@ -2130,9 +2157,8 @@
      * <p>Reflects to which devices Power Control messages should be sent:
      * only to the TV, broadcast to all devices, no power control messages.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_CONTROL_MODE
      */
-    @SystemApi
     @NonNull
     @PowerControlMode
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2153,9 +2179,8 @@
      *
      * <p>Sets the action taken: do nothing or go to sleep immediately.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setPowerStateChangeOnActiveSourceLost(
             @NonNull @ActiveSourceLostBehavior String value) {
@@ -2176,9 +2201,8 @@
      *
      * <p>Reflects the action taken: do nothing or go to sleep immediately.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST
      */
-    @SystemApi
     @NonNull
     @ActiveSourceLostBehavior
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2204,9 +2228,8 @@
      * the AVR instead of TV speaker or Audio System speakers. If disabled, the
      * System Audio Mode will never be activated.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setSystemAudioControl(@NonNull @SystemAudioControl int value) {
         if (mService == null) {
@@ -2229,9 +2252,8 @@
      * the AVR instead of TV speaker or Audio System speakers. If disabled, the
      * System Audio Mode will never be activated.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL
      */
-    @SystemApi
     @NonNull
     @SystemAudioControl
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2252,9 +2274,8 @@
      *
      * <p>Sets whether the device should be muted when System Audio Mode is turned off.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting int value) {
         if (mService == null) {
@@ -2273,9 +2294,8 @@
      *
      * <p>Reflects whether the device should be muted when System Audio Mode is turned off.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING
      */
-    @SystemApi
     @NonNull
     @SystemAudioModeMuting
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2297,9 +2317,8 @@
      * <p>Sets whether the TV should wake up upon reception of &lt;Text View On&gt;
      * or &lt;Image View On&gt;.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setTvWakeOnOneTouchPlay(@NonNull @TvWakeOnOneTouchPlay int value) {
         if (mService == null) {
@@ -2319,9 +2338,8 @@
      * <p>Reflects whether the TV should wake up upon reception of &lt;Text View On&gt;
      * or &lt;Image View On&gt;.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY
      */
-    @SystemApi
     @NonNull
     @TvWakeOnOneTouchPlay
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2343,9 +2361,8 @@
      * <p>Sets whether the device will also turn off other CEC devices
      * when it goes to standby mode.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setTvSendStandbyOnSleep(@NonNull @TvSendStandbyOnSleep int value) {
         if (mService == null) {
@@ -2365,9 +2382,8 @@
      * <p>Reflects whether the device will also turn off other CEC devices
      * when it goes to standby mode.
      *
-     * @hide
+     * @see HdmiControlManager#CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP
      */
-    @SystemApi
     @NonNull
     @TvSendStandbyOnSleep
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
@@ -2382,4 +2398,102 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Set presence of one Short Audio Descriptor (SAD) in the query.
+     *
+     * <p>Allows the caller to specify whether the SAD for a specific audio codec should be
+     * present in the &lt;Request Short Audio Descriptor&gt; query. Each &lt;Request Short Audio
+     * Descriptor&gt; message can carry at most 4 SADs at a time. This method allows the caller to
+     * limit the amount of SADs queried and therefore limit the amount of CEC messages on the bus.
+     *
+     * <p>When an ARC connection is established, the TV sends a
+     * &lt;Request Short Audio Descriptor&gt; query to the Audio System that it's connected to. If
+     * an SAD is queried and the Audio System reports that it supports that SAD, the TV can send
+     * audio in that format to be output on the Audio System via ARC.
+     * If a codec is not queried, the TV doesn't know if the connected Audio System supports this
+     * SAD and doesn't send audio in that format to the Audio System.
+     *
+     * @param setting SAD to set.
+     * @param value Presence to set the SAD to.
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setSadPresenceInQuery(@NonNull @CecSettingSad String setting,
+            @SadPresenceInQuery int value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingIntValue(setting, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set presence of multiple Short Audio Descriptors (SADs) in the query.
+     *
+     * <p>Allows the caller to specify whether the SADs for specific audio codecs should be present
+     * in the &lt;Request Short Audio Descriptor&gt; query. For audio codecs that are not specified,
+     * the SAD's presence remains at its previous value. Each &lt;Request Short Audio Descriptor&gt;
+     * message can carry at most 4 SADs at a time. This method allows the caller to limit the amount
+     * of SADs queried and therefore limit the amount of CEC messages on the bus.
+     *
+     * <p>When an ARC connection is established, the TV sends a
+     * &lt;Request Short Audio Descriptor&gt; query to the Audio System that it's connected to. If
+     * an SAD is queried and the Audio System reports that it supports that SAD, the TV can send
+     * audio in that format to be output on the Audio System via ARC.
+     * If a codec is not queried, the TV doesn't know if the connected Audio System supports this
+     * SAD and doesn't send audio in that format to the Audio System.
+     *
+     *
+     * @param settings SADs to set.
+     * @param value Presence to set all specified SADs to.
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setSadsPresenceInQuery(@NonNull @CecSettingSad List<String> settings,
+            @SadPresenceInQuery int value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            for (String sad : settings) {
+                mService.setCecSettingIntValue(sad, value);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get presence of one Short Audio Descriptor (SAD) in the query.
+     *
+     * <p>Reflects whether the SAD for a specific audio codec should be present in the
+     * &lt;Request Short Audio Descriptor&gt; query.
+     *
+     * <p>When an ARC connection is established, the TV sends a
+     * &lt;Request Short Audio Descriptor&gt; query to the Audio System that it's connected to. If
+     * an SAD is queried and the Audio System reports that it supports that SAD, the TV can send
+     * audio in that format to be output on the Audio System via ARC.
+     * If a codec is not queried, the TV doesn't know if the connected Audio System supports this
+     * SAD and doesn't send audio in that format to the Audio System.
+     *
+     * @param setting SAD to get.
+     * @return Current presence of the specified SAD.
+     */
+    @SadPresenceInQuery
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public int getSadPresenceInQuery(@NonNull @CecSettingSad String setting) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingIntValue(setting);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 08f75df..74506da 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -160,19 +160,19 @@
     }
 
     /**
-     * Template to match cellular networks with the given IMSI and {@code ratType}.
-     * Use {@link #NETWORK_TYPE_ALL} to include all network types when filtering.
-     * See {@code TelephonyManager.NETWORK_TYPE_*}.
+     * Template to match cellular networks with the given IMSI, {@code ratType} and
+     * {@code metered}. Use {@link #NETWORK_TYPE_ALL} to include all network types when
+     * filtering. See {@code TelephonyManager.NETWORK_TYPE_*}.
      */
     public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
-            @NetworkType int ratType) {
+            @NetworkType int ratType, int metered) {
         if (TextUtils.isEmpty(subscriberId)) {
             return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
-                    METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+                    metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
                     SUBSCRIBER_ID_MATCH_RULE_EXACT);
         }
         return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
-                METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+                metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
                 SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
@@ -305,6 +305,7 @@
         }
     }
 
+    // TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
     @UnsupportedAppUsage
     public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
         this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
@@ -312,9 +313,14 @@
 
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
             String networkId) {
-        this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
-                DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
-                SUBSCRIBER_ID_MATCH_RULE_EXACT);
+        // Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
+        // to metered networks. It is now possible to match mobile with any meteredness, but
+        // in order to preserve backward compatibility of @UnsupportedAppUsage methods, this
+        //constructor passes METERED_YES for these types.
+        this(matchRule, subscriberId, matchSubscriberIds, networkId,
+                (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
+                : METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+                OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
     // TODO: Remove it after updating all of the caller.
@@ -589,11 +595,7 @@
             // TODO: consider matching against WiMAX subscriber identity
             return true;
         } else {
-            // Only metered mobile network would be matched regardless of metered filter.
-            // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650.
-            // TODO: Respect metered filter and remove mMetered condition.
-            return (ident.mType == TYPE_MOBILE && ident.mMetered)
-                    && !ArrayUtils.isEmpty(mMatchSubscriberIds)
+            return ident.mType == TYPE_MOBILE && !ArrayUtils.isEmpty(mMatchSubscriberIds)
                     && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
                     && matchesCollapsedRatType(ident);
         }
@@ -707,8 +709,7 @@
         if (ident.mType == TYPE_WIMAX) {
             return true;
         } else {
-            return (ident.mType == TYPE_MOBILE && ident.mMetered)
-                    && matchesCollapsedRatType(ident);
+            return ident.mType == TYPE_MOBILE && matchesCollapsedRatType(ident);
         }
     }
 
diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING
index 8c13ef9..a379c33 100644
--- a/core/java/android/net/TEST_MAPPING
+++ b/core/java/android/net/TEST_MAPPING
@@ -16,5 +16,24 @@
     {
       "path": "frameworks/opt/net/wifi"
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.net"
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
   ]
 }
diff --git a/core/java/android/net/sntp/Duration64.java b/core/java/android/net/sntp/Duration64.java
index 939b289..7f29cdb 100644
--- a/core/java/android/net/sntp/Duration64.java
+++ b/core/java/android/net/sntp/Duration64.java
@@ -26,7 +26,7 @@
  *
  * @hide
  */
-public class Duration64 {
+public final class Duration64 {
 
     public static final Duration64 ZERO = new Duration64(0);
     private final long mBits;
diff --git a/core/java/android/net/sntp/Timestamp64.java b/core/java/android/net/sntp/Timestamp64.java
index 81a3310..8ddfd77 100644
--- a/core/java/android/net/sntp/Timestamp64.java
+++ b/core/java/android/net/sntp/Timestamp64.java
@@ -15,6 +15,8 @@
  */
 package android.net.sntp;
 
+import android.text.TextUtils;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.time.Instant;
@@ -113,7 +115,7 @@
 
     @Override
     public String toString() {
-        return String.format("%08x.%08x", mEraSeconds, mFractionBits);
+        return TextUtils.formatSimple("%08x.%08x", mEraSeconds, mFractionBits);
     }
 
     /** Returns the instant represented by this value in the specified NTP era. */
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 0c3debb1..a243453 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -51,6 +51,7 @@
             mRegisteredListeners = new ArrayMap<>();
 
     private final Object mLock = new Object();
+    @GuardedBy("mLock")
     private AllVibratorsInfo mVibratorInfo;
 
     @UnsupportedAppUsage
@@ -73,7 +74,15 @@
             int[] vibratorIds = mVibratorManager.getVibratorIds();
             VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
             for (int i = 0; i < vibratorIds.length; i++) {
-                vibratorInfos[i] = mVibratorManager.getVibrator(vibratorIds[i]).getInfo();
+                Vibrator vibrator = mVibratorManager.getVibrator(vibratorIds[i]);
+                if (vibrator instanceof NullVibrator) {
+                    Log.w(TAG, "Vibrator manager service not ready; "
+                            + "Info not yet available for vibrator: " + vibratorIds[i]);
+                    // This should never happen after the vibrator manager service is ready.
+                    // Skip caching this vibrator until then.
+                    return VibratorInfo.EMPTY_VIBRATOR_INFO;
+                }
+                vibratorInfos[i] = vibrator.getInfo();
             }
             return mVibratorInfo = new AllVibratorsInfo(vibratorInfos);
         }
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index feffcbd..aa9028e 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -165,7 +165,11 @@
         return ctx != null ? ctx.getResources().getFloat(resId) : defaultValue;
     }
 
-    /** @hide */
+    /**
+     * Get the info describing this vibrator.
+     *
+     * @hide
+     */
     protected VibratorInfo getInfo() {
         return VibratorInfo.EMPTY_VIBRATOR_INFO;
     }
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 486f9f1..cf51496 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -461,7 +461,8 @@
         int supportedPrimitivesCount = mSupportedPrimitives.size();
         String[] names = new String[supportedPrimitivesCount];
         for (int i = 0; i < supportedPrimitivesCount; i++) {
-            names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
+            names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i))
+                    + "(" + mSupportedPrimitives.valueAt(i) + "ms)";
         }
         return names;
     }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index db9d4e2..d4c9ade 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -219,6 +219,39 @@
 
     /**
      * Checks whether a given data access chain described by the given {@link AttributionSource}
+     * has a given permission. Call this method if you are the datasource which would not blame you
+     * for access to the data since you are the data.
+     *
+     * <strong>NOTE:</strong> Use this method only for permission checks at the
+     * point where you will deliver the permission protected data to clients.
+     *
+     * <p>For example, if an app registers a location listener it should have the location
+     * permission but no data is actually sent to the app at the moment of registration
+     * and you should use {@link #checkPermissionForPreflight(String, AttributionSource)}
+     * to determine if the app has or may have location permission (if app has only foreground
+     * location the grant state depends on the app's fg/gb state) and this check will not
+     * leave a trace that permission protected data was delivered. When you are about to
+     * deliver the location data to a registered listener you should use this method which
+     * will evaluate the permission access based on the current fg/bg state of the app and
+     * leave a record that the data was accessed.
+     *
+     * @param permission The permission to check.
+     * @param attributionSource the permission identity
+     * @param message A message describing the reason the permission was checked
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+     *
+     * @see #checkPermissionForPreflight(String, AttributionSource)
+     */
+    @PermissionCheckerManager.PermissionResult
+    public int checkPermissionForDataDeliveryFromDataSource(@NonNull String permission,
+            @NonNull AttributionSource attributionSource, @Nullable String message) {
+        return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(mContext, permission,
+                PermissionChecker.PID_UNKNOWN, attributionSource, message);
+    }
+
+    /**
+     * Checks whether a given data access chain described by the given {@link AttributionSource}
      * has a given permission.
      *
      * <strong>NOTE:</strong> Use this method only for permission checks at the
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index d544915..f8aa98e 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -5201,7 +5201,7 @@
         @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
         @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
         public static @NonNull Map<String, List<ContentValues>> queryRawContactEntity(
-                @NonNull Context context, long contactId) {
+                @NonNull ContentResolver contentResolver, long contactId) {
             Uri uri = RawContactsEntity.CONTENT_URI;
             long realContactId = contactId;
 
@@ -5219,7 +5219,7 @@
                 final String selection = Data.CONTACT_ID + "=?";
                 final String[] selectionArgs = new String[] {String.valueOf(realContactId)};
 
-                entityIterator = RawContacts.newEntityIterator(context.getContentResolver().query(
+                entityIterator = RawContacts.newEntityIterator(contentResolver.query(
                             uri, null, selection, selectionArgs, null));
 
                 if (entityIterator == null) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 608d006..54b87ab7 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -189,6 +189,14 @@
     public static final String NAMESPACE_CAPTIVEPORTALLOGIN = "captive_portal_login";
 
     /**
+     * Namespace for Tethering module.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_TETHERING = "tethering";
+
+    /**
      * Namespace for content capture feature used by on-device machine intelligence
      * to provide suggestions in a privacy-safe manner.
      *
@@ -306,6 +314,14 @@
     public static final String NAMESPACE_NETD_NATIVE = "netd_native";
 
     /**
+     * Namespace for all Android NNAPI related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_NNAPI_NATIVE = "nnapi_native";
+
+    /**
      * Namespace for features related to the Package Manager Service.
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 535d8b7..9d4a249 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -15429,27 +15429,9 @@
          */
         public static int getInt(ContentResolver cr, String name, int def) {
             String v = getString(cr, name);
-            final boolean isQueryForDeviceProvision = name.equals(DEVICE_PROVISIONED);
             try {
-                // TODO(b/197879371): remove the extra logging after bug is fixed
-                final int result;
-                if (v != null) {
-                    result = Integer.parseInt(v);
-                    if (isQueryForDeviceProvision) {
-                        Log.w(TAG, "Found settings value for provision. Returning " + result);
-                    }
-                } else {
-                    result = def;
-                    if (isQueryForDeviceProvision) {
-                        Log.w(TAG, "Missing settings value for provision. Returning " + result);
-                    }
-                }
-                return result;
+                return v != null ? Integer.parseInt(v) : def;
             } catch (NumberFormatException e) {
-                if (isQueryForDeviceProvision) {
-                    Log.w(TAG, "Wrong settings value for provision. Found: " + v
-                            + ". Returning " + v);
-                }
                 return def;
             }
         }
@@ -17657,40 +17639,42 @@
             "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION";
 
     /**
-     * Activity Action: For system or preinstalled apps to show their {@link Activity} in 2-pane
-     * mode in Settings app on large screen devices.
+     * Activity Action: For system or preinstalled apps to show their {@link Activity} embedded
+     * in Settings app on large screen devices.
      * <p>
-     *     Input: {@link #EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI} must be included to
-     * specify the intent for the activity which will be displayed in 2-pane mode in Settings app.
+     *     Input: {@link #EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI} must be included to
+     * specify the intent for the activity which will be embedded in Settings app.
      * It's an intent URI string from {@code intent.toUri(Intent.URI_INTENT_SCHEME)}.
      *
-     *     Input: {@link #EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY} must be included to
+     *     Input: {@link #EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY} must be included to
      * specify a key that indicates the menu item which will be highlighted on settings home menu.
      * <p>
      * Output: Nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK =
-            "android.settings.SETTINGS_LARGE_SCREEN_DEEP_LINK";
+    public static final String ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY =
+            "android.settings.SETTINGS_EMBED_DEEP_LINK_ACTIVITY";
 
     /**
-     * Activity Extra: Specify the intent for the {@link Activity} which will be displayed in 2-pane
-     * mode in Settings app. It's an intent URI string from
+     * Activity Extra: Specify the intent for the {@link Activity} which will be embedded in
+     * Settings app. It's an intent URI string from
      * {@code intent.toUri(Intent.URI_INTENT_SCHEME)}.
      * <p>
-     * This must be passed as an extra field to {@link #ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK}.
+     * This must be passed as an extra field to
+     * {@link #ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY}.
      */
-    public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI =
-            "android.provider.extra.SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI";
+    public static final String EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI =
+            "android.provider.extra.SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI";
 
     /**
      * Activity Extra: Specify a key that indicates the menu item which should be highlighted on
      * settings home menu.
      * <p>
-     * This must be passed as an extra field to {@link #ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK}.
+     * This must be passed as an extra field to
+     * {@link #ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY}.
      */
-    public static final String EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY =
-            "android.provider.extra.SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY";
+    public static final String EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY =
+            "android.provider.extra.SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY";
 
     /**
      * Performs a strict and comprehensive check of whether a calling package is allowed to
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 4004148..66188cd 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -139,6 +139,33 @@
         this.groupKey = groupKey();
     }
 
+    /**
+     * @hide
+     */
+    public static int getUidFromKey(@NonNull String key) {
+        String[] parts = key.split("\\|");
+        if (parts.length >= 5) {
+            try {
+                int uid = Integer.parseInt(parts[4]);
+                return uid;
+            } catch (NumberFormatException e) {
+                return -1;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    public static String getPkgFromKey(@NonNull String key) {
+        String[] parts = key.split("\\|");
+        if (parts.length >= 2) {
+            return parts[1];
+        }
+        return null;
+    }
+
     private String key() {
         String sbnKey = user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
         if (overrideGroupKey != null && getNotification().isGroupSummary()) {
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 30e4a23..88818b6 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -2044,7 +2044,11 @@
     /**
      * Registers a callback that will be notified when visible activities have been changed.
      *
-     * @param executor The handler to receive the callback.
+     * Note: The {@link VisibleActivityCallback#onVisible(VisibleActivityInfo)} will be called
+     * immediately with current visible activities when the callback is registered for the first
+     * time. If the callback is already registered, this method does nothing.
+     *
+     * @param executor The executor which will be used to invoke the callback.
      * @param callback The callback to receive the response.
      *
      * @throws IllegalStateException if calling this method before onCreate().
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index 49cd600..6b26155 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -61,14 +61,15 @@
      */
     public static VerifiedSigner extractCertificates(String apkFile)
             throws SignatureNotFoundException, SecurityException {
-        V4Signature signature = extractSignature(apkFile);
-        return verify(apkFile, signature, APK_SIGNATURE_SCHEME_DEFAULT);
+        Pair<V4Signature.HashingInfo, V4Signature.SigningInfos> pair = extractSignature(apkFile);
+        return verify(apkFile, pair.first, pair.second, APK_SIGNATURE_SCHEME_DEFAULT);
     }
 
     /**
      * Extracts APK Signature Scheme v4 signature of the provided APK.
      */
-    public static V4Signature extractSignature(String apkFile) throws SignatureNotFoundException {
+    public static Pair<V4Signature.HashingInfo, V4Signature.SigningInfos> extractSignature(
+            String apkFile) throws SignatureNotFoundException {
         final File apk = new File(apkFile);
         final byte[] signatureBytes = IncrementalManager.unsafeGetFileSignature(
                 apk.getAbsolutePath());
@@ -81,7 +82,11 @@
                 throw new SecurityException(
                         "v4 signature version " + signature.version + " is not supported");
             }
-            return signature;
+            final V4Signature.HashingInfo hashingInfo = V4Signature.HashingInfo.fromByteArray(
+                    signature.hashingInfo);
+            final V4Signature.SigningInfos signingInfos = V4Signature.SigningInfos.fromByteArray(
+                    signature.signingInfos);
+            return Pair.create(hashingInfo, signingInfos);
         } catch (IOException e) {
             throw new SignatureNotFoundException("Failed to read V4 signature.", e);
         }
@@ -91,17 +96,9 @@
      * Verifies APK Signature Scheme v4 signature and returns the
      * certificates associated with each signer.
      */
-    public static VerifiedSigner verify(String apkFile, final V4Signature signature,
-            final int v3BlockId) throws SignatureNotFoundException, SecurityException {
-        final V4Signature.HashingInfo hashingInfo;
-        final V4Signature.SigningInfos signingInfos;
-        try {
-            hashingInfo = V4Signature.HashingInfo.fromByteArray(signature.hashingInfo);
-            signingInfos = V4Signature.SigningInfos.fromByteArray(signature.signingInfos);
-        } catch (IOException e) {
-            throw new SignatureNotFoundException("Failed to read V4 signature.", e);
-        }
-
+    public static VerifiedSigner verify(String apkFile, final V4Signature.HashingInfo hashingInfo,
+            final V4Signature.SigningInfos signingInfos, final int v3BlockId)
+            throws SignatureNotFoundException, SecurityException {
         final V4Signature.SigningInfo signingInfo = findSigningInfoForBlockId(signingInfos,
                 v3BlockId);
 
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 13bd587..41b749e 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -33,6 +33,7 @@
 import android.os.Build;
 import android.os.Trace;
 import android.os.incremental.V4Signature;
+import android.util.Pair;
 import android.util.jar.StrictJarFile;
 
 import com.android.internal.util.ArrayUtils;
@@ -191,47 +192,53 @@
             boolean verifyFull) throws SignatureNotFoundException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
         try {
-            final V4Signature v4Signature = ApkSignatureSchemeV4Verifier.extractSignature(apkPath);
+            final Pair<V4Signature.HashingInfo, V4Signature.SigningInfos> v4Pair =
+                    ApkSignatureSchemeV4Verifier.extractSignature(apkPath);
+            final V4Signature.HashingInfo hashingInfo = v4Pair.first;
+            final V4Signature.SigningInfos signingInfos = v4Pair.second;
 
             Signature[] pastSignerSigs = null;
-
-            Map<Integer, byte[]> nonstreamingDigests;
-            Certificate[][] nonstreamingCerts;
+            Map<Integer, byte[]> nonstreamingDigests = null;
+            Certificate[][] nonstreamingCerts = null;
 
             int v3BlockId = APK_SIGNATURE_SCHEME_DEFAULT;
-
-            try {
-                // v4 is an add-on and requires v2 or v3 signature to validate against its
-                // certificate and digest
-                ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
-                        ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
-                nonstreamingDigests = v3Signer.contentDigests;
-                nonstreamingCerts = new Certificate[][]{v3Signer.certs};
-                if (v3Signer.por != null) {
-                    // populate proof-of-rotation information
-                    pastSignerSigs = new Signature[v3Signer.por.certs.size()];
-                    for (int i = 0; i < pastSignerSigs.length; i++) {
-                        pastSignerSigs[i] = new Signature(
-                                v3Signer.por.certs.get(i).getEncoded());
-                        pastSignerSigs[i].setFlags(v3Signer.por.flagsList.get(i));
-                    }
-                }
-                v3BlockId = v3Signer.blockId;
-            } catch (SignatureNotFoundException e) {
+            // If V4 contains additional signing blocks then we need to always run v2/v3 verifier
+            // to figure out which block they use.
+            if (verifyFull || signingInfos.signingInfoBlocks.length > 0) {
                 try {
-                    ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
-                            ApkSignatureSchemeV2Verifier.verify(apkPath, false);
-                    nonstreamingDigests = v2Signer.contentDigests;
-                    nonstreamingCerts = v2Signer.certs;
-                } catch (SignatureNotFoundException ee) {
-                    throw new SecurityException(
-                            "V4 verification failed to collect V2/V3 certificates from : "
-                                    + apkPath, ee);
+                    // v4 is an add-on and requires v2 or v3 signature to validate against its
+                    // certificate and digest
+                    ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
+                            ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
+                    nonstreamingDigests = v3Signer.contentDigests;
+                    nonstreamingCerts = new Certificate[][]{v3Signer.certs};
+                    if (v3Signer.por != null) {
+                        // populate proof-of-rotation information
+                        pastSignerSigs = new Signature[v3Signer.por.certs.size()];
+                        for (int i = 0; i < pastSignerSigs.length; i++) {
+                            pastSignerSigs[i] = new Signature(
+                                    v3Signer.por.certs.get(i).getEncoded());
+                            pastSignerSigs[i].setFlags(v3Signer.por.flagsList.get(i));
+                        }
+                    }
+                    v3BlockId = v3Signer.blockId;
+                } catch (SignatureNotFoundException e) {
+                    try {
+                        ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
+                                ApkSignatureSchemeV2Verifier.verify(apkPath, false);
+                        nonstreamingDigests = v2Signer.contentDigests;
+                        nonstreamingCerts = v2Signer.certs;
+                    } catch (SignatureNotFoundException ee) {
+                        throw new SecurityException(
+                                "V4 verification failed to collect V2/V3 certificates from : "
+                                        + apkPath, ee);
+                    }
                 }
             }
 
             ApkSignatureSchemeV4Verifier.VerifiedSigner vSigner =
-                    ApkSignatureSchemeV4Verifier.verify(apkPath, v4Signature, v3BlockId);
+                    ApkSignatureSchemeV4Verifier.verify(apkPath, hashingInfo, signingInfos,
+                            v3BlockId);
             Certificate[][] signerCerts = new Certificate[][]{vSigner.certs};
             Signature[] signerSigs = convertToSignatures(signerCerts);
 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f74008b..0b4857d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -60,6 +60,7 @@
 import android.view.InputDevice;
 import android.view.IInputFilter;
 import android.view.AppTransitionAnimationSpec;
+import android.view.TaskTransitionSpec;
 import android.view.WindowContentFrameStats;
 import android.view.WindowManager;
 import android.view.SurfaceControl;
@@ -346,6 +347,14 @@
     Bitmap screenshotWallpaper();
 
     /**
+     * Mirrors the wallpaper for the given display.
+     *
+     * @param displayId ID of the display for the wallpaper.
+     * @return A SurfaceControl for the parent of the mirrored wallpaper.
+     */
+    SurfaceControl mirrorWallpaperSurface(int displayId);
+
+    /**
      * Registers a wallpaper visibility listener.
      * @return Current visibility.
      */
@@ -883,4 +892,17 @@
      * @hide
      */
     void setTaskSnapshotEnabled(boolean enabled);
+
+    /**
+     * Customized the task transition animation with a task transition spec.
+     *
+     * @param spec the spec that will be used to customize the task animations
+     */
+    void setTaskTransitionSpec(in TaskTransitionSpec spec);
+
+    /**
+     * Clears any task transition spec that has been previously set and
+     * reverts to using the default task transition with no spec changes.
+     */
+    void clearTaskTransitionSpec();
 }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 3917279..6179881 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -317,6 +317,26 @@
         return insets;
     }
 
+    public Insets calculateInsets(Rect frame, @InsetsType int types,
+            InsetsVisibilities overrideVisibilities) {
+        Insets insets = Insets.NONE;
+        for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+            InsetsSource source = mSources[type];
+            if (source == null) {
+                continue;
+            }
+            int publicType = InsetsState.toPublicType(type);
+            if ((publicType & types) == 0) {
+                continue;
+            }
+            if (!overrideVisibilities.getVisibility(type)) {
+                continue;
+            }
+            insets = Insets.max(source.calculateInsets(frame, true), insets);
+        }
+        return insets;
+    }
+
     public Insets calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
         Insets insets = Insets.NONE;
         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -482,6 +502,26 @@
         return mDisplayCutout.get();
     }
 
+    public void getDisplayCutoutSafe(Rect outBounds) {
+        outBounds.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+        final DisplayCutout cutout = mDisplayCutout.get();
+        final Rect displayFrame = mDisplayFrame;
+        if (!cutout.isEmpty()) {
+            if (cutout.getSafeInsetLeft() > 0) {
+                outBounds.left = displayFrame.left + cutout.getSafeInsetLeft();
+            }
+            if (cutout.getSafeInsetTop() > 0) {
+                outBounds.top = displayFrame.top + cutout.getSafeInsetTop();
+            }
+            if (cutout.getSafeInsetRight() > 0) {
+                outBounds.right = displayFrame.right - cutout.getSafeInsetRight();
+            }
+            if (cutout.getSafeInsetBottom() > 0) {
+                outBounds.bottom = displayFrame.bottom - cutout.getSafeInsetBottom();
+            }
+        }
+    }
+
     public void setRoundedCorners(RoundedCorners roundedCorners) {
         mRoundedCorners = roundedCorners;
     }
diff --git a/core/java/android/view/TaskTransitionSpec.aidl b/core/java/android/view/TaskTransitionSpec.aidl
new file mode 100644
index 0000000..08af15c
--- /dev/null
+++ b/core/java/android/view/TaskTransitionSpec.aidl
@@ -0,0 +1,20 @@
+/*
+** Copyright 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 android.view;
+
+/** @hide */
+parcelable TaskTransitionSpec;
diff --git a/core/java/android/view/TaskTransitionSpec.java b/core/java/android/view/TaskTransitionSpec.java
new file mode 100644
index 0000000..e90d6e1
--- /dev/null
+++ b/core/java/android/view/TaskTransitionSpec.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.view;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Holds information about how to execute task transition animations.
+ *
+ * This class is intended to be used with IWindowManager.setTaskTransitionSpec methods when
+ * we want more customization over the way default task transitions are executed.
+ *
+ * @hide
+ */
+public class TaskTransitionSpec implements Parcelable {
+    /**
+     * The background color to use during task animations (override the default background color)
+     */
+    public final int backgroundColor;
+
+    /**
+     * TEMPORARY FIELD (b/202383002)
+     * TODO: Remove once we use surfaceflinger rounded corners on tasks rather than taskbar overlays
+     *
+     * A set of {@InsetsState.InternalInsetsType}s we want to use as the source to set the bounds
+     * of the task during the animation. Used to make sure that task animate above the taskbar.
+     * Will also be used to crop to the frame size of the inset source to the inset size to prevent
+     * the taskbar rounded corners overlay from being invisible during task transition animation.
+     */
+    public final Set<Integer> animationBoundInsets;
+
+    public TaskTransitionSpec(
+            int backgroundColor, Set<Integer> animationBoundInsets) {
+        this.backgroundColor = backgroundColor;
+        this.animationBoundInsets = animationBoundInsets;
+    }
+
+    public TaskTransitionSpec(Parcel in) {
+        this.backgroundColor = in.readInt();
+
+        int animationBoundInsetsSize = in.readInt();
+        this.animationBoundInsets = new ArraySet<>(animationBoundInsetsSize);
+        for (int i = 0; i < animationBoundInsetsSize; i++) {
+            this.animationBoundInsets.add(in.readInt());
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(backgroundColor);
+
+        dest.writeInt(animationBoundInsets.size());
+        for (int insetType : animationBoundInsets) {
+            dest.writeInt(insetType);
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<TaskTransitionSpec>
+            CREATOR = new Parcelable.Creator<TaskTransitionSpec>() {
+                public TaskTransitionSpec createFromParcel(Parcel in) {
+                    return new TaskTransitionSpec(in);
+                }
+
+                public TaskTransitionSpec[] newArray(int size) {
+                    return new TaskTransitionSpec[size];
+                }
+            };
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cddf9ee..89e1e08 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -140,7 +140,6 @@
 import android.os.UserHandle;
 import android.sysprop.DisplayProperties;
 import android.util.AndroidRuntimeException;
-import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.IndentingPrintWriter;
@@ -160,7 +159,6 @@
 import android.view.View.FocusDirection;
 import android.view.View.MeasureSpec;
 import android.view.Window.OnContentApplyWindowInsetsListener;
-import android.view.WindowInsets.Side.InsetsSide;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -511,9 +509,11 @@
     private final Point mSurfaceSize = new Point();
     private final Point mLastSurfaceSize = new Point();
 
-    final Rect mTempRect; // used in the transaction to not thrash the heap.
-    final Rect mVisRect; // used to retrieve visible rect of focused view.
-    private final Rect mTempBoundsRect = new Rect(); // used to set the size of the bounds surface.
+    private final Rect mVisRect = new Rect(); // used to retrieve visible rect of focused view.
+    private final Rect mTempRect = new Rect();
+    private final Rect mTempRect2 = new Rect();
+
+    private final WindowLayout mWindowLayout = new WindowLayout();
 
     // This is used to reduce the race between window focus changes being dispatched from
     // the window manager and input events coming through the input system.
@@ -736,47 +736,20 @@
     }
 
     /**
-     * This is only used when the UI thread is paused due to {@link #mNextDrawUseBlastSync} being
-     * set. Specifically, it's only used when calling
-     * {@link BLASTBufferQueue#setNextTransaction(Transaction)} and then merged with
-     * {@link #mSurfaceChangedTransaction}. It doesn't need to be thread safe since it's only
-     * accessed when the UI thread is paused.
+     * This is only used on the RenderThread when handling a blast sync. Specifically, it's only
+     * used when calling {@link BLASTBufferQueue#setNextTransaction(Transaction)} and then merged
+     * with a tmp transaction on the Render Thread. The tmp transaction is then merged into
+     * {@link #mSurfaceChangedTransaction} on the UI Thread, avoiding any threading issues.
      */
     private final SurfaceControl.Transaction mRtBLASTSyncTransaction =
             new SurfaceControl.Transaction();
 
     /**
-     * Keeps track of whether the WM requested to use BLAST Sync when calling relayout. When set,
-     * we pause the UI thread to ensure we don't get overlapping requests. We then send a
-     * transaction to {@link BLASTBufferQueue#setNextTransaction(Transaction)}, which is then sent
-     * back to WM to synchronize.
-     *
-     * This flag is set to false only after the synchronized transaction that contains the buffer
-     * has been sent to SurfaceFlinger.
-     */
-    private boolean mNextDrawUseBlastSync = false;
-
-    /**
-     * Wait for the blast sync transaction complete callback before drawing and queuing up more
-     * frames. This will prevent out of order buffers submissions when WM has requested to
-     * synchronize with the client.
-     */
-    private boolean mWaitForBlastSyncComplete = false;
-
-    /**
      * Keeps track of the last frame number that was attempted to draw. Should only be accessed on
      * the RenderThread.
      */
     private long mRtLastAttemptedDrawFrameNum = 0;
 
-    /**
-     * Keeps track of whether a traverse was triggered while the UI thread was paused. This can
-     * occur when the client is waiting on another process to submit the transaction that
-     * contains the buffer. The UI thread needs to wait on the callback before it can submit
-     * another buffer.
-     */
-    private boolean mRequestedTraverseWhilePaused = false;
-
     private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
 
     private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
@@ -809,8 +782,6 @@
         mWidth = -1;
         mHeight = -1;
         mDirty = new Rect();
-        mTempRect = new Rect();
-        mVisRect = new Rect();
         mWinFrame = new Rect();
         mWindow = new W(this);
         mLeashToken = new Binder();
@@ -985,29 +956,6 @@
         }
     }
 
-    // TODO(b/161810301): Make this private after window layout is moved to the client side.
-    public static void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
-            Rect displayFrame, Rect outBounds) {
-        final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
-        final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
-        final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
-        final Rect df = displayFrame;
-        Insets insets = Insets.of(0, 0, 0, 0);
-        for (int i = types.size() - 1; i >= 0; i--) {
-            final InsetsSource source = state.peekSource(types.valueAt(i));
-            if (source == null) {
-                continue;
-            }
-            insets = Insets.max(insets, source.calculateInsets(
-                    df, attrs.isFitInsetsIgnoringVisibility()));
-        }
-        final int left = (sidesToFit & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
-        final int top = (sidesToFit & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
-        final int right = (sidesToFit & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
-        final int bottom = (sidesToFit & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
-        outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom);
-    }
-
     private Configuration getConfiguration() {
         return mContext.getResources().getConfiguration();
     }
@@ -1168,8 +1116,13 @@
                 mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
                 mInsetsController.onStateChanged(mTempInsets);
                 mInsetsController.onControlsChanged(mTempControls);
-                computeWindowBounds(mWindowAttributes, mInsetsController.getState(),
-                        getConfiguration().windowConfiguration.getBounds(), mTmpFrames.frame);
+                final InsetsState state = mInsetsController.getState();
+                final Rect displayCutoutSafe = mTempRect;
+                state.getDisplayCutoutSafe(displayCutoutSafe);
+                mWindowLayout.computeWindowFrames(mWindowAttributes, state,
+                        displayCutoutSafe, getConfiguration().windowConfiguration.getBounds(),
+                        mInsetsController.getRequestedVisibilities(),
+                        null /* attachedWindowFrame */, mTmpFrames.frame, mTempRect2);
                 setFrame(mTmpFrames.frame);
                 if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
@@ -1621,7 +1574,7 @@
         mForceNextWindowRelayout = forceNextWindowRelayout;
         mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
 
-        if (msg == MSG_RESIZED_REPORT && !mNextDrawUseBlastSync) {
+        if (msg == MSG_RESIZED_REPORT) {
             reportNextDraw();
         }
 
@@ -1966,11 +1919,11 @@
     private void setBoundsLayerCrop(Transaction t) {
         // Adjust of insets and update the bounds layer so child surfaces do not draw into
         // the surface inset region.
-        mTempBoundsRect.set(0, 0, mSurfaceSize.x, mSurfaceSize.y);
-        mTempBoundsRect.inset(mWindowAttributes.surfaceInsets.left,
+        mTempRect.set(0, 0, mSurfaceSize.x, mSurfaceSize.y);
+        mTempRect.inset(mWindowAttributes.surfaceInsets.left,
                 mWindowAttributes.surfaceInsets.top,
                 mWindowAttributes.surfaceInsets.right, mWindowAttributes.surfaceInsets.bottom);
-        t.setWindowCrop(mBoundsLayer, mTempBoundsRect);
+        t.setWindowCrop(mBoundsLayer, mTempRect);
     }
 
     /**
@@ -2516,23 +2469,6 @@
             return;
         }
 
-        // This is to ensure we don't end up queueing new frames while waiting on a previous frame
-        // to get latched. This can happen when there's been a sync request for this window. The
-        // frame could be in a transaction that's passed to different processes to ensure
-        // synchronization. It continues to block until ViewRootImpl receives a callback that the
-        // transaction containing the buffer has been sent to SurfaceFlinger. Once we receive, that
-        // signal, we know it's safe to start queuing new buffers.
-        //
-        // When the callback is invoked, it will trigger a traversal request if
-        // mRequestedTraverseWhilePaused is set so there's no need to attempt a retry here.
-        if (mWaitForBlastSyncComplete) {
-            if (DEBUG_BLAST) {
-                Log.w(mTag, "Can't perform draw while waiting for a transaction complete");
-            }
-            mRequestedTraverseWhilePaused = true;
-            return;
-        }
-
         mIsInTraversal = true;
         mWillDrawSoon = true;
         boolean windowSizeMayChange = false;
@@ -2776,6 +2712,7 @@
             }
         }
         final boolean wasReportNextDraw = mReportNextDraw;
+        boolean useBlastSync = false;
 
         if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
                 || mForceNextWindowRelayout) {
@@ -2828,7 +2765,7 @@
                     }
                     reportNextDraw();
                     if (isHardwareEnabled()) {
-                        mNextDrawUseBlastSync = true;
+                        useBlastSync = true;
                     }
                 }
 
@@ -3302,7 +3239,7 @@
                 }
                 mPendingTransitions.clear();
             }
-            performDraw();
+            performDraw(useBlastSync);
         } else {
             if (isViewVisible) {
                 // Try again
@@ -3988,34 +3925,19 @@
     }
 
     /**
-     * Only call this on the UI Thread.
-     */
-    void clearBlastSync() {
-        mNextDrawUseBlastSync = false;
-        mWaitForBlastSyncComplete = false;
-        if (DEBUG_BLAST) {
-            Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
-                    + " due to a previous skipped traversal.");
-        }
-        if (mRequestedTraverseWhilePaused) {
-            mRequestedTraverseWhilePaused = false;
-            scheduleTraversals();
-        }
-    }
-
-    /**
      * @hide
      */
     public boolean isHardwareEnabled() {
         return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
     }
 
-    private boolean addFrameCompleteCallbackIfNeeded(boolean reportNextDraw) {
+    private boolean addFrameCompleteCallbackIfNeeded(boolean useBlastSync,
+            boolean reportNextDraw) {
         if (!isHardwareEnabled()) {
             return false;
         }
 
-        if (!mNextDrawUseBlastSync && !reportNextDraw) {
+        if (!useBlastSync && !reportNextDraw) {
             return false;
         }
 
@@ -4037,30 +3959,22 @@
             // for the current draw attempt.
             if (frameWasNotDrawn) {
                 mBlastBufferQueue.setNextTransaction(null);
-                mBlastBufferQueue.setTransactionCompleteCallback(mRtLastAttemptedDrawFrameNum,
-                        null);
                 // Apply the transactions that were sent to mergeWithNextTransaction since the
                 // frame didn't draw on this vsync. It's possible the frame will draw later, but
                 // it's better to not be sync than to block on a frame that may never come.
                 mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum);
             }
 
+            Transaction tmpTransaction = new Transaction();
+            tmpTransaction.merge(mRtBLASTSyncTransaction);
             mHandler.postAtFrontOfQueue(() -> {
-                if (mNextDrawUseBlastSync) {
-                    // We don't need to synchronize mRtBLASTSyncTransaction here since we're
-                    // guaranteed that this is called after onFrameDraw and mNextDrawUseBlastSync
-                    // is only true when the UI thread is paused. Therefore, no one should be
-                    // modifying this object until the next vsync.
-                    mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
+                if (useBlastSync) {
+                    mSurfaceChangedTransaction.merge(tmpTransaction);
                 }
 
                 if (reportNextDraw) {
                     pendingDrawFinished();
                 }
-
-                if (frameWasNotDrawn) {
-                    clearBlastSync();
-                }
             });
         });
         return true;
@@ -4096,21 +4010,19 @@
         });
     }
 
-    private void addFrameCallbackIfNeeded() {
-        final boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
+    private void addFrameCallbackIfNeeded(boolean useBlastSync) {
         final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
         final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
 
-        if (!nextDrawUseBlastSync && !needsCallbackForBlur) {
+        if (!useBlastSync && !needsCallbackForBlur) {
             return;
         }
 
         if (DEBUG_BLAST) {
             Log.d(mTag, "Creating frameDrawingCallback"
-                    + " nextDrawUseBlastSync=" + nextDrawUseBlastSync
+                    + " nextDrawUseBlastSync=" + useBlastSync
                     + " hasBlurUpdates=" + hasBlurUpdates);
         }
-        mWaitForBlastSyncComplete = nextDrawUseBlastSync;
         final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame =
                 needsCallbackForBlur ?  mBlurRegionAggregator.getBlurRegionsCopyForRT() : null;
 
@@ -4118,7 +4030,7 @@
         HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> {
             if (DEBUG_BLAST) {
                 Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "."
-                        + " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
+                        + " Creating transactionCompleteCallback=" + useBlastSync);
             }
 
             mRtLastAttemptedDrawFrameNum = frame;
@@ -4132,7 +4044,7 @@
                 return;
             }
 
-            if (nextDrawUseBlastSync) {
+            if (useBlastSync) {
                 // Frame callbacks will always occur after submitting draw requests and before
                 // the draw actually occurs. This will ensure that we set the next transaction
                 // for the frame that's about to get drawn and not on a previous frame that.
@@ -4140,35 +4052,27 @@
                 // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
                 // being modified and only sent to BlastBufferQueue.
                 mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
-
-                mBlastBufferQueue.setTransactionCompleteCallback(frame, frameNumber -> {
-                    if (DEBUG_BLAST) {
-                        Log.d(mTag, "Received transactionCompleteCallback frameNum=" + frame);
-                    }
-                    mHandler.postAtFrontOfQueue(this::clearBlastSync);
-                });
             }
         };
         registerRtFrameCallback(frameDrawingCallback);
     }
 
-    private void performDraw() {
+    private void performDraw(boolean useBlastSync) {
         if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
             return;
         } else if (mView == null) {
             return;
         }
 
-        final boolean fullRedrawNeeded =
-                mFullRedrawNeeded || mReportNextDraw || mNextDrawUseBlastSync;
+        final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw || useBlastSync;
         mFullRedrawNeeded = false;
 
         mIsDrawing = true;
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
 
-        addFrameCallbackIfNeeded();
+        addFrameCallbackIfNeeded(useBlastSync);
         addFrameCommitCallbackIfNeeded();
-        boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(mReportNextDraw);
+        boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(useBlastSync, mReportNextDraw);
 
         try {
             boolean canUseAsync = draw(fullRedrawNeeded);
@@ -4413,7 +4317,7 @@
                 mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
 
         boolean useAsyncReport = false;
-        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {
+        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
             if (isHardwareEnabled()) {
                 // If accessibility focus moved, always invalidate the root.
                 boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
@@ -5655,7 +5559,7 @@
         }
 
         /**
-         * Marks the the input event as finished then forwards it to the next stage.
+         * Marks the input event as finished then forwards it to the next stage.
          */
         protected void finish(QueuedInputEvent q, boolean handled) {
             q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
new file mode 100644
index 0000000..cdc1977
--- /dev/null
+++ b/core/java/android/view/WindowLayout.java
@@ -0,0 +1,152 @@
+/*
+ * 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 android.view;
+
+import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+
+/**
+ * Computes window frames.
+ * @hide
+ */
+public class WindowLayout {
+    private final Rect mTempDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
+    private final Rect mTempRect = new Rect();
+
+    public boolean computeWindowFrames(WindowManager.LayoutParams attrs, InsetsState state,
+            Rect displayCutoutSafe, Rect windowBounds, InsetsVisibilities requestedVisibilities,
+            Rect attachedWindowFrame, Rect outDisplayFrame, Rect outParentFrame) {
+        final int type = attrs.type;
+        final int fl = attrs.flags;
+        final int pfl = attrs.privateFlags;
+        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
+
+        // Compute bounds restricted by insets
+        final Insets insets = state.calculateInsets(windowBounds, attrs.getFitInsetsTypes(),
+                attrs.isFitInsetsIgnoringVisibility());
+        final @WindowInsets.Side.InsetsSide int sides = attrs.getFitInsetsSides();
+        final int left = (sides & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
+        final int top = (sides & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
+        final int right = (sides & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
+        final int bottom = (sides & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
+        outDisplayFrame.set(windowBounds.left + left, windowBounds.top + top,
+                windowBounds.right - right, windowBounds.bottom - bottom);
+
+        if (attachedWindowFrame == null) {
+            outParentFrame.set(outDisplayFrame);
+            if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
+                final InsetsSource source = state.peekSource(ITYPE_IME);
+                if (source != null) {
+                    outParentFrame.inset(source.calculateInsets(
+                            outParentFrame, false /* ignoreVisibility */));
+                }
+            }
+        } else {
+            outParentFrame.set(!layoutInScreen ? attachedWindowFrame : outDisplayFrame);
+        }
+
+        // Compute bounds restricted by display cutout
+        final DisplayCutout cutout = state.getDisplayCutout();
+        if (cutout.isEmpty()) {
+            return false;
+        }
+        boolean clippedByDisplayCutout = false;
+        final Rect displayCutoutSafeExceptMaybeBars = mTempDisplayCutoutSafeExceptMaybeBarsRect;
+        displayCutoutSafeExceptMaybeBars.set(displayCutoutSafe);
+
+        // Ensure that windows with a non-ALWAYS display cutout mode are laid out in
+        // the cutout safe zone.
+        final int cutoutMode = attrs.layoutInDisplayCutoutMode;
+        if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
+            final Rect displayFrame = state.getDisplayFrame();
+            final InsetsSource statusBarSource = state.peekSource(ITYPE_STATUS_BAR);
+            if (statusBarSource != null && displayCutoutSafe.top > displayFrame.top) {
+                // Make sure that the zone we're avoiding for the cutout is at least as tall as the
+                // status bar; otherwise fullscreen apps will end up cutting halfway into the status
+                // bar.
+                displayCutoutSafeExceptMaybeBars.top =
+                        Math.max(statusBarSource.getFrame().bottom, displayCutoutSafe.top);
+            }
+            if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
+                if (displayFrame.width() < displayFrame.height()) {
+                    displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                } else {
+                    displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+                    displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+                }
+            }
+            final boolean layoutInsetDecor = (attrs.flags & FLAG_LAYOUT_INSET_DECOR) != 0;
+            if (layoutInScreen && layoutInsetDecor
+                    && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+                    || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
+                final Insets systemBarsInsets = state.calculateInsets(
+                        displayFrame, WindowInsets.Type.systemBars(), requestedVisibilities);
+                if (systemBarsInsets.left > 0) {
+                    displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+                }
+                if (systemBarsInsets.top > 0) {
+                    displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+                }
+                if (systemBarsInsets.right > 0) {
+                    displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+                }
+                if (systemBarsInsets.bottom > 0) {
+                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                }
+            }
+            if (type == TYPE_INPUT_METHOD) {
+                final InsetsSource navSource = state.peekSource(ITYPE_NAVIGATION_BAR);
+                if (navSource != null && navSource.calculateInsets(displayFrame, true).bottom > 0) {
+                    // The IME can always extend under the bottom cutout if the navbar is there.
+                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                }
+            }
+            final boolean attachedInParent = attachedWindowFrame != null && !layoutInScreen;
+
+            // TYPE_BASE_APPLICATION windows are never considered floating here because they don't
+            // get cropped / shifted to the displayFrame in WindowState.
+            final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
+                    && type != TYPE_BASE_APPLICATION;
+
+            // Windows that are attached to a parent and laid out in said parent already avoid
+            // the cutout according to that parent and don't need to be further constrained.
+            // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
+            // They will later be cropped or shifted using the displayFrame in WindowState,
+            // which prevents overlap with the DisplayCutout.
+            if (!attachedInParent && !floatingInScreenWindow) {
+                mTempRect.set(outParentFrame);
+                outParentFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+                clippedByDisplayCutout = !mTempRect.equals(outParentFrame);
+            }
+            outDisplayFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+        }
+        return clippedByDisplayCutout;
+    }
+}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 18013e8..c92a3a0 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -18,6 +18,7 @@
 
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentCallbacks2;
@@ -709,6 +710,16 @@
             }
         }
     }
+
+    /** @hide */
+    @Nullable
+    public SurfaceControl mirrorWallpaperSurface(int displayId) {
+        try {
+            return getWindowManagerService().mirrorWallpaperSurface(displayId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
 
 final class WindowLeaked extends AndroidRuntimeException {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index e9dec12..4730eaa 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -5075,6 +5075,30 @@
         @NonNull public static final AccessibilityAction ACTION_DRAG_CANCEL =
                 new AccessibilityAction(R.id.accessibilityActionDragCancel);
 
+        /**
+         * Action to perform a left swipe.
+         */
+        @NonNull public static final AccessibilityAction ACTION_SWIPE_LEFT =
+                new AccessibilityAction(R.id.accessibilityActionSwipeLeft);
+
+        /**
+         * Action to perform a right swipe.
+         */
+        @NonNull public static final AccessibilityAction ACTION_SWIPE_RIGHT =
+            new AccessibilityAction(R.id.accessibilityActionSwipeRight);
+
+        /**
+         * Action to perform an up swipe.
+         */
+        @NonNull public static final AccessibilityAction ACTION_SWIPE_UP =
+            new AccessibilityAction(R.id.accessibilityActionSwipeUp);
+
+        /**
+         * Action to perform a down swipe.
+         */
+        @NonNull public static final AccessibilityAction ACTION_SWIPE_DOWN =
+            new AccessibilityAction(R.id.accessibilityActionSwipeDown);
+
         private final int mActionId;
         private final CharSequence mLabel;
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 009afce..e023ed5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -696,7 +696,7 @@
         @Override
         public void finishComposingText() {
             if (mServedInputConnection != null) {
-                mServedInputConnection.finishComposingText();
+                mServedInputConnection.finishComposingTextFromImm();
             }
         }
 
@@ -919,7 +919,7 @@
                             mRestartOnNextWindowFocus = true;
                             // Note that finishComposingText() is allowed to run
                             // even when we are not active.
-                            mFallbackInputConnection.finishComposingText();
+                            mFallbackInputConnection.finishComposingTextFromImm();
                         }
                         // Check focus again in case that "onWindowFocus" is called before
                         // handling this message.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index fe5eb08..0f309f1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -34,6 +34,7 @@
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.Application;
+import android.app.LoadedApk;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.appwidget.AppWidgetHostView;
@@ -5475,7 +5476,8 @@
         // user. So build a context that loads resources from that user but
         // still returns the current users userId so settings like data / time formats
         // are loaded without requiring cross user persmissions.
-        final Context contextForResources = getContextForResources(context);
+        final Context contextForResources =
+                getContextForResourcesEnsuringCorrectCachedApkPaths(context);
         if (colorResources != null) {
             colorResources.apply(contextForResources);
         }
@@ -5853,13 +5855,14 @@
         }
     }
 
-    private Context getContextForResources(Context context) {
+    private Context getContextForResourcesEnsuringCorrectCachedApkPaths(Context context) {
         if (mApplication != null) {
             if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
                     && context.getPackageName().equals(mApplication.packageName)) {
                 return context;
             }
             try {
+                LoadedApk.checkAndUpdateApkPaths(mApplication);
                 return context.createApplicationContext(mApplication,
                         Context.CONTEXT_RESTRICTED);
             } catch (NameNotFoundException e) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 6b33428..8e293f4 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -408,7 +408,7 @@
         }
 
         @Override
-        protected Context getRemoteContext() {
+        protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
             return null;
         }
 
diff --git a/core/java/android/window/ITransitionMetricsReporter.aidl b/core/java/android/window/ITransitionMetricsReporter.aidl
new file mode 100644
index 0000000..00f71dc
--- /dev/null
+++ b/core/java/android/window/ITransitionMetricsReporter.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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 android.window;
+
+import android.os.IBinder;
+
+/**
+ * Implemented by WM Core to know the metrics of transition that runs on a different process.
+ * @hide
+ */
+oneway interface ITransitionMetricsReporter {
+
+    /**
+     * Called when the transition animation starts.
+     *
+     * @param startTime The time when the animation started.
+     */
+    void reportAnimationStart(IBinder transitionToken, long startTime);
+}
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index e65fcdd..3c7cd02 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -23,6 +23,7 @@
 import android.window.IDisplayAreaOrganizerController;
 import android.window.ITaskFragmentOrganizerController;
 import android.window.ITaskOrganizerController;
+import android.window.ITransitionMetricsReporter;
 import android.window.ITransitionPlayer;
 import android.window.IWindowContainerTransactionCallback;
 import android.window.WindowContainerToken;
@@ -98,4 +99,7 @@
      * this will replace the existing one if set.
      */
     void registerTransitionPlayer(in ITransitionPlayer player);
+
+    /** @return An interface enabling the transition players to report its metrics. */
+    ITransitionMetricsReporter getTransitionMetricsReporter();
 }
diff --git a/core/java/android/window/TransitionMetrics.java b/core/java/android/window/TransitionMetrics.java
new file mode 100644
index 0000000..9a93c1a
--- /dev/null
+++ b/core/java/android/window/TransitionMetrics.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.window;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Singleton;
+
+/**
+ * A helper class for who plays transition animation can report its metrics easily.
+ * @hide
+ */
+public class TransitionMetrics {
+
+    private final ITransitionMetricsReporter mTransitionMetricsReporter;
+
+    private TransitionMetrics(ITransitionMetricsReporter reporter) {
+        mTransitionMetricsReporter = reporter;
+    }
+
+    /** Reports the current timestamp as when the transition animation starts. */
+    public void reportAnimationStart(IBinder transitionToken) {
+        try {
+            mTransitionMetricsReporter.reportAnimationStart(transitionToken,
+                    SystemClock.elapsedRealtime());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Gets the singleton instance of TransitionMetrics. */
+    public static TransitionMetrics getInstance() {
+        return sTransitionMetrics.get();
+    }
+
+    private static final Singleton<TransitionMetrics> sTransitionMetrics = new Singleton<>() {
+        @Override
+        protected TransitionMetrics create() {
+            return new TransitionMetrics(WindowOrganizer.getTransitionMetricsReporter());
+        }
+    };
+}
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index e9b8174..4ea5ea5 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -159,7 +159,19 @@
         }
     }
 
-    IWindowOrganizerController getWindowOrganizerController() {
+    /**
+     * @see TransitionMetrics
+     * @hide
+     */
+    public static ITransitionMetricsReporter getTransitionMetricsReporter() {
+        try {
+            return getWindowOrganizerController().getTransitionMetricsReporter();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    static IWindowOrganizerController getWindowOrganizerController() {
         return IWindowOrganizerControllerSingleton.get();
     }
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 6ff74e4..9b1bef06 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -441,14 +441,12 @@
     private final ChooserHandler mChooserHandler = new ChooserHandler();
 
     private class ChooserHandler extends Handler {
-        private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 4;
-        private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 5;
         private static final int LIST_VIEW_UPDATE_MESSAGE = 6;
+        private static final int SHORTCUT_MANAGER_ALL_SHARE_TARGET_RESULTS = 7;
 
         private void removeAllMessages() {
             removeMessages(LIST_VIEW_UPDATE_MESSAGE);
-            removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT);
-            removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED);
+            removeMessages(SHORTCUT_MANAGER_ALL_SHARE_TARGET_RESULTS);
         }
 
         @Override
@@ -468,22 +466,23 @@
                             .refreshListView();
                     break;
 
-                case SHORTCUT_MANAGER_SHARE_TARGET_RESULT:
-                    if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT");
-                    final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj;
-                    if (resultInfo.resultTargets != null) {
-                        ChooserListAdapter adapterForUserHandle =
-                                mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(
-                                        resultInfo.userHandle);
-                        if (adapterForUserHandle != null) {
-                            adapterForUserHandle.addServiceResults(
-                                    resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1,
-                                    mDirectShareShortcutInfoCache);
+                case SHORTCUT_MANAGER_ALL_SHARE_TARGET_RESULTS:
+                    if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_ALL_SHARE_TARGET_RESULTS");
+                    final ServiceResultInfo[] resultInfos = (ServiceResultInfo[]) msg.obj;
+                    for (ServiceResultInfo resultInfo : resultInfos) {
+                        if (resultInfo.resultTargets != null) {
+                            ChooserListAdapter adapterForUserHandle =
+                                    mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(
+                                            resultInfo.userHandle);
+                            if (adapterForUserHandle != null) {
+                                adapterForUserHandle.addServiceResults(
+                                        resultInfo.originalTarget,
+                                        resultInfo.resultTargets, msg.arg1,
+                                        mDirectShareShortcutInfoCache);
+                            }
                         }
                     }
-                    break;
 
-                case SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED:
                     logDirectShareTargetReceived(
                             MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER);
                     sendVoiceChoicesIfNeeded();
@@ -1954,34 +1953,44 @@
         // Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path
         // for direct share targets. After ShareSheet is refactored we should use the
         // ShareShortcutInfos directly.
+        List<ServiceResultInfo> resultRecords = new ArrayList<>();
         for (int i = 0; i < chooserListAdapter.getDisplayResolveInfoCount(); i++) {
-            List<ShortcutManager.ShareShortcutInfo> matchingShortcuts = new ArrayList<>();
-            for (int j = 0; j < resultList.size(); j++) {
-                if (chooserListAdapter.getDisplayResolveInfo(i).getResolvedComponentName().equals(
-                            resultList.get(j).getTargetComponent())) {
-                    matchingShortcuts.add(resultList.get(j));
-                }
-            }
+            DisplayResolveInfo displayResolveInfo = chooserListAdapter.getDisplayResolveInfo(i);
+            List<ShortcutManager.ShareShortcutInfo> matchingShortcuts =
+                    filterShortcutsByTargetComponentName(
+                            resultList, displayResolveInfo.getResolvedComponentName());
             if (matchingShortcuts.isEmpty()) {
                 continue;
             }
             List<ChooserTarget> chooserTargets = convertToChooserTarget(
                     matchingShortcuts, resultList, appTargets, shortcutType);
 
-            final Message msg = Message.obtain();
-            msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
-            msg.obj = new ServiceResultInfo(chooserListAdapter.getDisplayResolveInfo(i),
-                    chooserTargets, userHandle);
-            msg.arg1 = shortcutType;
-            mChooserHandler.sendMessage(msg);
+            ServiceResultInfo resultRecord = new ServiceResultInfo(
+                    displayResolveInfo, chooserTargets, userHandle);
+            resultRecords.add(resultRecord);
         }
 
-        sendShortcutManagerShareTargetResultCompleted();
+        sendShortcutManagerShareTargetResults(
+                shortcutType, resultRecords.toArray(new ServiceResultInfo[0]));
     }
 
-    private void sendShortcutManagerShareTargetResultCompleted() {
+    private List<ShortcutManager.ShareShortcutInfo> filterShortcutsByTargetComponentName(
+            List<ShortcutManager.ShareShortcutInfo> allShortcuts, ComponentName requiredTarget) {
+        List<ShortcutManager.ShareShortcutInfo> matchingShortcuts = new ArrayList<>();
+        for (ShortcutManager.ShareShortcutInfo shortcut : allShortcuts) {
+            if (requiredTarget.equals(shortcut.getTargetComponent())) {
+                matchingShortcuts.add(shortcut);
+            }
+        }
+        return matchingShortcuts;
+    }
+
+    private void sendShortcutManagerShareTargetResults(
+            int shortcutType, ServiceResultInfo[] results) {
         final Message msg = Message.obtain();
-        msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED;
+        msg.what = ChooserHandler.SHORTCUT_MANAGER_ALL_SHARE_TARGET_RESULTS;
+        msg.obj = results;
+        msg.arg1 = shortcutType;
         mChooserHandler.sendMessage(msg);
     }
 
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 6c73e70..29b31d3 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -26,20 +26,25 @@
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 
+import com.android.apex.ApexInfo;
+import com.android.apex.XmlParser;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.om.OverlayConfigParser.OverlayPartition;
 import com.android.internal.content.om.OverlayConfigParser.ParsedConfiguration;
 import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.TriConsumer;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
-import java.util.function.BiConsumer;
 import java.util.function.Supplier;
 
 /**
@@ -77,7 +82,7 @@
     public interface PackageProvider {
 
         /** Performs the given action for each package. */
-        void forEachPackage(BiConsumer<ParsingPackageRead, Boolean> p);
+        void forEachPackage(TriConsumer<ParsingPackageRead, Boolean, File> p);
     }
 
     private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
@@ -119,6 +124,8 @@
                             p)));
         }
 
+        ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);
+
         boolean foundConfigFile = false;
         final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos =
                 packageProvider == null ? null : getOverlayPackageInfos(packageProvider);
@@ -129,7 +136,9 @@
             final OverlayScanner scanner = (scannerFactory == null) ? null : scannerFactory.get();
             final ArrayList<ParsedConfiguration> partitionOverlays =
                     OverlayConfigParser.getConfigurations(partition, scanner,
-                            packageManagerOverlayInfos);
+                            packageManagerOverlayInfos,
+                            activeApexesPerPartition.getOrDefault(partition.type,
+                                    Collections.emptyList()));
             if (partitionOverlays != null) {
                 foundConfigFile = true;
                 overlays.addAll(partitionOverlays);
@@ -147,7 +156,8 @@
                 // Filter out overlays not present in the partition.
                 partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos.values());
                 for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) {
-                    if (!partition.containsFile(partitionOverlayInfos.get(j).path)) {
+                    if (!partition.containsFile(partitionOverlayInfos.get(j)
+                            .getOriginalPartitionPath())) {
                         partitionOverlayInfos.remove(j);
                     }
                 }
@@ -294,16 +304,50 @@
     private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos(
             @NonNull PackageProvider packageManager) {
         final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>();
-        packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem) -> {
+        packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem,
+                @Nullable File preInstalledApexPath) -> {
             if (p.getOverlayTarget() != null && isSystem) {
                 overlays.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(),
                         p.getOverlayTarget(), p.getTargetSdkVersion(), p.isOverlayIsStatic(),
-                        p.getOverlayPriority(), new File(p.getBaseApkPath())));
+                        p.getOverlayPriority(), new File(p.getBaseApkPath()),
+                        preInstalledApexPath));
             }
         });
         return overlays;
     }
 
+    /** Returns a map of PartitionType to List of active APEX module names. */
+    @NonNull
+    private static ArrayMap<Integer, List<String>> getActiveApexes(
+            @NonNull List<OverlayPartition> partitions) {
+        // An Overlay in an APEX, which is an update of an APEX in a given partition,
+        // is considered as belonging to that partition.
+        ArrayMap<Integer, List<String>> result = new ArrayMap<>();
+        for (OverlayPartition partition : partitions) {
+            result.put(partition.type, new ArrayList<String>());
+        }
+        // Read from apex-info-list because ApexManager is not accessible to zygote.
+        File apexInfoList = new File("/apex/apex-info-list.xml");
+        if (apexInfoList.exists() && apexInfoList.canRead()) {
+            try (FileInputStream stream = new FileInputStream(apexInfoList)) {
+                List<ApexInfo> apexInfos = XmlParser.readApexInfoList(stream).getApexInfo();
+                for (ApexInfo info : apexInfos) {
+                    if (info.getIsActive()) {
+                        for (OverlayPartition partition : partitions) {
+                            if (partition.containsPath(info.getPreinstalledModulePath())) {
+                                result.get(partition.type).add(info.getModuleName());
+                                break;
+                            }
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                Log.w(TAG, "Error reading apex-info-list: " + e);
+            }
+        }
+        return result;
+    }
+
     /** Represents a single call to idmap create-multiple. */
     @VisibleForTesting
     public static class IdmapInvocation {
diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java
index 053a341..0ab7b3d 100644
--- a/core/java/com/android/internal/content/om/OverlayConfigParser.java
+++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java
@@ -41,6 +41,7 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -195,13 +196,19 @@
     @Nullable
     static ArrayList<ParsedConfiguration> getConfigurations(
             @NonNull OverlayPartition partition, @Nullable OverlayScanner scanner,
-            @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos) {
-        if (partition.getOverlayFolder() == null) {
-            return null;
+            @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos,
+            @NonNull List<String> activeApexes) {
+        if (scanner != null) {
+            if (partition.getOverlayFolder() != null) {
+                scanner.scanDir(partition.getOverlayFolder());
+            }
+            for (String apex : activeApexes) {
+                scanner.scanDir(new File("/apex/" + apex + "/overlay/"));
+            }
         }
 
-        if (scanner != null) {
-            scanner.scanDir(partition.getOverlayFolder());
+        if (partition.getOverlayFolder() == null) {
+            return null;
         }
 
         final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME);
diff --git a/core/java/com/android/internal/content/om/OverlayScanner.java b/core/java/com/android/internal/content/om/OverlayScanner.java
index 4387d1b..e4e0228 100644
--- a/core/java/com/android/internal/content/om/OverlayScanner.java
+++ b/core/java/com/android/internal/content/om/OverlayScanner.java
@@ -54,23 +54,38 @@
         public final boolean isStatic;
         public final int priority;
         public final File path;
+        @Nullable public final File preInstalledApexPath;
 
         public ParsedOverlayInfo(String packageName, String targetPackageName,
-                int targetSdkVersion, boolean isStatic, int priority, File path) {
+                int targetSdkVersion, boolean isStatic, int priority, File path,
+                @Nullable File preInstalledApexPath) {
             this.packageName = packageName;
             this.targetPackageName = targetPackageName;
             this.targetSdkVersion = targetSdkVersion;
             this.isStatic = isStatic;
             this.priority = priority;
             this.path = path;
+            this.preInstalledApexPath = preInstalledApexPath;
         }
 
         @Override
         public String toString() {
             return getClass().getSimpleName() + String.format("{packageName=%s"
                             + ", targetPackageName=%s, targetSdkVersion=%s, isStatic=%s"
-                            + ", priority=%s, path=%s}",
-                    packageName, targetPackageName, targetSdkVersion, isStatic, priority, path);
+                            + ", priority=%s, path=%s, preInstalledApexPath=%s}",
+                    packageName, targetPackageName, targetSdkVersion, isStatic,
+                    priority, path, preInstalledApexPath);
+        }
+
+        /**
+         * Retrieves the path of the overlay in its original installation partition.
+         *
+         * An Overlay in an APEX, which is an update of an APEX in a given partition,
+         * is considered as belonging to that partition.
+         */
+        @NonNull
+        public File getOriginalPartitionPath() {
+            return preInstalledApexPath != null ? preInstalledApexPath : path;
         }
     }
 
@@ -189,6 +204,6 @@
         }
         return new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(),
                 apkLite.getTargetSdkVersion(), apkLite.isOverlayIsStatic(),
-                apkLite.getOverlayPriority(), new File(apkLite.getPath()));
+                apkLite.getOverlayPriority(), new File(apkLite.getPath()), null);
     }
 }
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
index 2fde981..001e304 100644
--- a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -57,9 +57,14 @@
         return new IInputContextInvoker(inputContext);
     }
 
+    @NonNull
+    InputConnectionCommandHeader createHeader() {
+        return new InputConnectionCommandHeader();
+    }
+
     /**
-     * Invokes {@link IInputContext#getTextAfterCursor(int, int,
-     * com.android.internal.inputmethod.ICharSequenceResultCallback)}.
+     * Invokes {@link IInputContext#getTextAfterCursor(InputConnectionCommandHeader, int, int,
+     * AndroidFuture)}.
      *
      * @param length {@code length} parameter to be passed.
      * @param flags {@code flags} parameter to be passed.
@@ -71,7 +76,7 @@
     public AndroidFuture<CharSequence> getTextAfterCursor(int length, int flags) {
         final AndroidFuture<CharSequence> future = new AndroidFuture<>();
         try {
-            mIInputContext.getTextAfterCursor(length, flags, future);
+            mIInputContext.getTextAfterCursor(createHeader(), length, flags, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -79,7 +84,8 @@
     }
 
     /**
-     * Invokes {@link IInputContext#getTextBeforeCursor(int, int, ICharSequenceResultCallback)}.
+     * Invokes {@link IInputContext#getTextBeforeCursor(InputConnectionCommandHeader, int, int,
+     * AndroidFuture)}.
      *
      * @param length {@code length} parameter to be passed.
      * @param flags {@code flags} parameter to be passed.
@@ -91,7 +97,7 @@
     public AndroidFuture<CharSequence> getTextBeforeCursor(int length, int flags) {
         final AndroidFuture<CharSequence> future = new AndroidFuture<>();
         try {
-            mIInputContext.getTextBeforeCursor(length, flags, future);
+            mIInputContext.getTextBeforeCursor(createHeader(), length, flags, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -99,7 +105,8 @@
     }
 
     /**
-     * Invokes {@link IInputContext#getSelectedText(int, ICharSequenceResultCallback)}.
+     * Invokes
+     * {@link IInputContext#getSelectedText(InputConnectionCommandHeader, int, AndroidFuture)}.
      *
      * @param flags {@code flags} parameter to be passed.
      * @return {@link AndroidFuture<CharSequence>} that can be used to retrieve the invocation
@@ -110,7 +117,7 @@
     public AndroidFuture<CharSequence> getSelectedText(int flags) {
         final AndroidFuture<CharSequence> future = new AndroidFuture<>();
         try {
-            mIInputContext.getSelectedText(flags, future);
+            mIInputContext.getSelectedText(createHeader(), flags, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -119,7 +126,8 @@
 
     /**
      * Invokes
-     * {@link IInputContext#getSurroundingText(int, int, int, ISurroundingTextResultCallback)}.
+     * {@link IInputContext#getSurroundingText(InputConnectionCommandHeader, int, int, int,
+     * AndroidFuture)}.
      *
      * @param beforeLength {@code beforeLength} parameter to be passed.
      * @param afterLength {@code afterLength} parameter to be passed.
@@ -133,7 +141,8 @@
             int flags) {
         final AndroidFuture<SurroundingText> future = new AndroidFuture<>();
         try {
-            mIInputContext.getSurroundingText(beforeLength, afterLength, flags, future);
+            mIInputContext.getSurroundingText(createHeader(), beforeLength, afterLength, flags,
+                    future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -141,7 +150,8 @@
     }
 
     /**
-     * Invokes {@link IInputContext#getCursorCapsMode(int, IIntResultCallback)}.
+     * Invokes
+     * {@link IInputContext#getCursorCapsMode(InputConnectionCommandHeader, int, AndroidFuture)}.
      *
      * @param reqModes {@code reqModes} parameter to be passed.
      * @return {@link AndroidFuture<Integer>} that can be used to retrieve the invocation
@@ -152,7 +162,7 @@
     public AndroidFuture<Integer> getCursorCapsMode(int reqModes) {
         final AndroidFuture<Integer> future = new AndroidFuture<>();
         try {
-            mIInputContext.getCursorCapsMode(reqModes, future);
+            mIInputContext.getCursorCapsMode(createHeader(), reqModes, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -160,8 +170,8 @@
     }
 
     /**
-     * Invokes {@link IInputContext#getExtractedText(ExtractedTextRequest, int,
-     * IExtractedTextResultCallback)}.
+     * Invokes {@link IInputContext#getExtractedText(InputConnectionCommandHeader,
+     * ExtractedTextRequest, int, AndroidFuture)}.
      *
      * @param request {@code request} parameter to be passed.
      * @param flags {@code flags} parameter to be passed.
@@ -174,7 +184,7 @@
             int flags) {
         final AndroidFuture<ExtractedText> future = new AndroidFuture<>();
         try {
-            mIInputContext.getExtractedText(request, flags, future);
+            mIInputContext.getExtractedText(createHeader(), request, flags, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -182,7 +192,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#commitText(CharSequence, int)}.
+     * Invokes {@link IInputContext#commitText(InputConnectionCommandHeader, CharSequence, int)}.
      *
      * @param text {@code text} parameter to be passed.
      * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
@@ -192,7 +202,7 @@
     @AnyThread
     public boolean commitText(CharSequence text, int newCursorPosition) {
         try {
-            mIInputContext.commitText(text, newCursorPosition);
+            mIInputContext.commitText(createHeader(), text, newCursorPosition);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -200,7 +210,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#commitCompletion(CompletionInfo)}.
+     * Invokes {@link IInputContext#commitCompletion(InputConnectionCommandHeader, CompletionInfo)}.
      *
      * @param text {@code text} parameter to be passed.
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
@@ -209,7 +219,7 @@
     @AnyThread
     public boolean commitCompletion(CompletionInfo text) {
         try {
-            mIInputContext.commitCompletion(text);
+            mIInputContext.commitCompletion(createHeader(), text);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -217,7 +227,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#commitCorrection(CorrectionInfo)}.
+     * Invokes {@link IInputContext#commitCorrection(InputConnectionCommandHeader, CorrectionInfo)}.
      *
      * @param correctionInfo {@code correctionInfo} parameter to be passed.
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
@@ -226,7 +236,7 @@
     @AnyThread
     public boolean commitCorrection(CorrectionInfo correctionInfo) {
         try {
-            mIInputContext.commitCorrection(correctionInfo);
+            mIInputContext.commitCorrection(createHeader(), correctionInfo);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -234,7 +244,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#setSelection(int, int)}.
+     * Invokes {@link IInputContext#setSelection(InputConnectionCommandHeader, int, int)}.
      *
      * @param start {@code start} parameter to be passed.
      * @param end {@code start} parameter to be passed.
@@ -244,7 +254,7 @@
     @AnyThread
     public boolean setSelection(int start, int end) {
         try {
-            mIInputContext.setSelection(start, end);
+            mIInputContext.setSelection(createHeader(), start, end);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -252,7 +262,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#performEditorAction(int)}.
+     * Invokes {@link IInputContext#performEditorAction(InputConnectionCommandHeader, int)}.
      *
      * @param actionCode {@code start} parameter to be passed.
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
@@ -261,7 +271,7 @@
     @AnyThread
     public boolean performEditorAction(int actionCode) {
         try {
-            mIInputContext.performEditorAction(actionCode);
+            mIInputContext.performEditorAction(createHeader(), actionCode);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -269,7 +279,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#performContextMenuAction(id)}.
+     * Invokes {@link IInputContext#performContextMenuAction(InputConnectionCommandHeader, int)}.
      *
      * @param id {@code id} parameter to be passed.
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
@@ -278,7 +288,7 @@
     @AnyThread
     public boolean performContextMenuAction(int id) {
         try {
-            mIInputContext.performContextMenuAction(id);
+            mIInputContext.performContextMenuAction(createHeader(), id);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -286,7 +296,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#setComposingRegion(int, int)}.
+     * Invokes {@link IInputContext#setComposingRegion(InputConnectionCommandHeader, int, int)}.
      *
      * @param start {@code id} parameter to be passed.
      * @param end {@code id} parameter to be passed.
@@ -296,7 +306,7 @@
     @AnyThread
     public boolean setComposingRegion(int start, int end) {
         try {
-            mIInputContext.setComposingRegion(start, end);
+            mIInputContext.setComposingRegion(createHeader(), start, end);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -304,7 +314,8 @@
     }
 
     /**
-     * Invokes {@link IInputContext#setComposingText(CharSequence, int)}.
+     * Invokes
+     * {@link IInputContext#setComposingText(InputConnectionCommandHeader, CharSequence, int)}.
      *
      * @param text {@code text} parameter to be passed.
      * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
@@ -314,7 +325,7 @@
     @AnyThread
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
         try {
-            mIInputContext.setComposingText(text, newCursorPosition);
+            mIInputContext.setComposingText(createHeader(), text, newCursorPosition);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -322,7 +333,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#finishComposingText()}.
+     * Invokes {@link IInputContext#finishComposingText(InputConnectionCommandHeader)}.
      *
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
      *         {@code false} otherwise.
@@ -330,7 +341,7 @@
     @AnyThread
     public boolean finishComposingText() {
         try {
-            mIInputContext.finishComposingText();
+            mIInputContext.finishComposingText(createHeader());
             return true;
         } catch (RemoteException e) {
             return false;
@@ -338,7 +349,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#beginBatchEdit()}.
+     * Invokes {@link IInputContext#beginBatchEdit(InputConnectionCommandHeader)}.
      *
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
      *         {@code false} otherwise.
@@ -346,7 +357,7 @@
     @AnyThread
     public boolean beginBatchEdit() {
         try {
-            mIInputContext.beginBatchEdit();
+            mIInputContext.beginBatchEdit(createHeader());
             return true;
         } catch (RemoteException e) {
             return false;
@@ -354,7 +365,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#endBatchEdit()}.
+     * Invokes {@link IInputContext#endBatchEdit(InputConnectionCommandHeader)}.
      *
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
      *         {@code false} otherwise.
@@ -362,7 +373,7 @@
     @AnyThread
     public boolean endBatchEdit() {
         try {
-            mIInputContext.endBatchEdit();
+            mIInputContext.endBatchEdit(createHeader());
             return true;
         } catch (RemoteException e) {
             return false;
@@ -370,7 +381,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#sendKeyEvent(KeyEvent)}.
+     * Invokes {@link IInputContext#sendKeyEvent(InputConnectionCommandHeader, KeyEvent)}.
      *
      * @param event {@code event} parameter to be passed.
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
@@ -379,7 +390,7 @@
     @AnyThread
     public boolean sendKeyEvent(KeyEvent event) {
         try {
-            mIInputContext.sendKeyEvent(event);
+            mIInputContext.sendKeyEvent(createHeader(), event);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -387,7 +398,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#clearMetaKeyStates(int)}.
+     * Invokes {@link IInputContext#clearMetaKeyStates(InputConnectionCommandHeader, int)}.
      *
      * @param states {@code states} parameter to be passed.
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
@@ -396,7 +407,7 @@
     @AnyThread
     public boolean clearMetaKeyStates(int states) {
         try {
-            mIInputContext.clearMetaKeyStates(states);
+            mIInputContext.clearMetaKeyStates(createHeader(), states);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -404,7 +415,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#deleteSurroundingText(int, int)}.
+     * Invokes {@link IInputContext#deleteSurroundingText(InputConnectionCommandHeader, int, int)}.
      *
      * @param beforeLength {@code beforeLength} parameter to be passed.
      * @param afterLength {@code afterLength} parameter to be passed.
@@ -414,7 +425,7 @@
     @AnyThread
     public boolean deleteSurroundingText(int beforeLength, int afterLength) {
         try {
-            mIInputContext.deleteSurroundingText(beforeLength, afterLength);
+            mIInputContext.deleteSurroundingText(createHeader(), beforeLength, afterLength);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -422,7 +433,8 @@
     }
 
     /**
-     * Invokes {@link IInputContext#deleteSurroundingTextInCodePoints(int, int)}.
+     * Invokes {@link IInputContext#deleteSurroundingTextInCodePoints(InputConnectionCommandHeader,
+     * int, int)}.
      *
      * @param beforeLength {@code beforeLength} parameter to be passed.
      * @param afterLength {@code afterLength} parameter to be passed.
@@ -432,7 +444,8 @@
     @AnyThread
     public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
         try {
-            mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+            mIInputContext.deleteSurroundingTextInCodePoints(createHeader(), beforeLength,
+                    afterLength);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -440,7 +453,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#performSpellCheck()}.
+     * Invokes {@link IInputContext#performSpellCheck(InputConnectionCommandHeader)}.
      *
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
      *         {@code false} otherwise.
@@ -448,7 +461,7 @@
     @AnyThread
     public boolean performSpellCheck() {
         try {
-            mIInputContext.performSpellCheck();
+            mIInputContext.performSpellCheck(createHeader());
             return true;
         } catch (RemoteException e) {
             return false;
@@ -456,7 +469,8 @@
     }
 
     /**
-     * Invokes {@link IInputContext#performPrivateCommand(String, Bundle)}.
+     * Invokes
+     * {@link IInputContext#performPrivateCommand(InputConnectionCommandHeader, String, Bundle)}.
      *
      * @param action {@code action} parameter to be passed.
      * @param data {@code data} parameter to be passed.
@@ -466,7 +480,7 @@
     @AnyThread
     public boolean performPrivateCommand(String action, Bundle data) {
         try {
-            mIInputContext.performPrivateCommand(action, data);
+            mIInputContext.performPrivateCommand(createHeader(), action, data);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -474,7 +488,8 @@
     }
 
     /**
-     * Invokes {@link IInputContext#requestCursorUpdates(int, IIntResultCallback)}.
+     * Invokes {@link IInputContext#requestCursorUpdates(InputConnectionCommandHeader, int, int,
+     * AndroidFuture)}.
      *
      * @param cursorUpdateMode {@code cursorUpdateMode} parameter to be passed.
      * @param imeDisplayId the display ID that is associated with the IME.
@@ -486,7 +501,8 @@
     public AndroidFuture<Boolean> requestCursorUpdates(int cursorUpdateMode, int imeDisplayId) {
         final AndroidFuture<Boolean> future = new AndroidFuture<>();
         try {
-            mIInputContext.requestCursorUpdates(cursorUpdateMode, imeDisplayId, future);
+            mIInputContext.requestCursorUpdates(createHeader(), cursorUpdateMode, imeDisplayId,
+                    future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -494,8 +510,8 @@
     }
 
     /**
-     * Invokes
-     * {@link IInputContext#commitContent(InputContentInfo, int, Bundle, IIntResultCallback)}.
+     * Invokes {@link IInputContext#commitContent(InputConnectionCommandHeader, InputContentInfo,
+     * int, Bundle, AndroidFuture)}.
      *
      * @param inputContentInfo {@code inputContentInfo} parameter to be passed.
      * @param flags {@code flags} parameter to be passed.
@@ -509,7 +525,7 @@
             Bundle opts) {
         final AndroidFuture<Boolean> future = new AndroidFuture<>();
         try {
-            mIInputContext.commitContent(inputContentInfo, flags, opts, future);
+            mIInputContext.commitContent(createHeader(), inputContentInfo, flags, opts, future);
         } catch (RemoteException e) {
             future.completeExceptionally(e);
         }
@@ -517,7 +533,7 @@
     }
 
     /**
-     * Invokes {@link IInputContext#setImeConsumesInput(boolean)}.
+     * Invokes {@link IInputContext#setImeConsumesInput(InputConnectionCommandHeader, boolean)}.
      *
      * @param imeConsumesInput {@code imeConsumesInput} parameter to be passed.
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
@@ -526,7 +542,7 @@
     @AnyThread
     public boolean setImeConsumesInput(boolean imeConsumesInput) {
         try {
-            mIInputContext.setImeConsumesInput(imeConsumesInput);
+            mIInputContext.setImeConsumesInput(createHeader(), imeConsumesInput);
             return true;
         } catch (RemoteException e) {
             return false;
diff --git a/core/java/com/android/internal/inputmethod/InputConnectionCommandHeader.aidl b/core/java/com/android/internal/inputmethod/InputConnectionCommandHeader.aidl
new file mode 100644
index 0000000..9de3ffc
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputConnectionCommandHeader.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.internal.inputmethod;
+
+parcelable InputConnectionCommandHeader;
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/InputConnectionCommandHeader.java b/core/java/com/android/internal/inputmethod/InputConnectionCommandHeader.java
new file mode 100644
index 0000000..4bd8d0c
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputConnectionCommandHeader.java
@@ -0,0 +1,53 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A common IPC header used behind {@link RemoteInputConnectionImpl} and
+ * {@link android.inputmethodservice.RemoteInputConnection}.
+ */
+public final class InputConnectionCommandHeader implements Parcelable {
+    public InputConnectionCommandHeader() {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<InputConnectionCommandHeader> CREATOR =
+            new Parcelable.Creator<InputConnectionCommandHeader>() {
+                @NonNull
+                public InputConnectionCommandHeader createFromParcel(Parcel in) {
+                    return new InputConnectionCommandHeader();
+                }
+
+                @NonNull
+                public InputConnectionCommandHeader[] newArray(int size) {
+                    return new InputConnectionCommandHeader[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 7a668fb..3867afd 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -225,7 +225,7 @@
     }
 
     @Override
-    public void getTextAfterCursor(int length, int flags,
+    public void getTextAfterCursor(InputConnectionCommandHeader header, int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getTextAfterCursor", future, () -> {
             final InputConnection ic = getInputConnection();
@@ -243,7 +243,7 @@
     }
 
     @Override
-    public void getTextBeforeCursor(int length, int flags,
+    public void getTextBeforeCursor(InputConnectionCommandHeader header, int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getTextBeforeCursor", future, () -> {
             final InputConnection ic = getInputConnection();
@@ -261,7 +261,8 @@
     }
 
     @Override
-    public void getSelectedText(int flags, AndroidFuture future /* T=CharSequence */) {
+    public void getSelectedText(InputConnectionCommandHeader header, int flags,
+            AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getSelectedText", future, () -> {
             final InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -278,8 +279,8 @@
     }
 
     @Override
-    public void getSurroundingText(int beforeLength, int afterLength, int flags,
-            AndroidFuture future /* T=SurroundingText */) {
+    public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
+            int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
         dispatchWithTracing("getSurroundingText", future, () -> {
             final InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -302,7 +303,8 @@
     }
 
     @Override
-    public void getCursorCapsMode(int reqModes, AndroidFuture future /* T=Integer */) {
+    public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
+            AndroidFuture future /* T=Integer */) {
         dispatchWithTracing("getCursorCapsMode", future, () -> {
             final InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -314,8 +316,8 @@
     }
 
     @Override
-    public void getExtractedText(ExtractedTextRequest request, int flags,
-            AndroidFuture future /* T=ExtractedText */) {
+    public void getExtractedText(InputConnectionCommandHeader header, ExtractedTextRequest request,
+            int flags, AndroidFuture future /* T=ExtractedText */) {
         dispatchWithTracing("getExtractedText", future, () -> {
             final InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -327,7 +329,8 @@
     }
 
     @Override
-    public void commitText(CharSequence text, int newCursorPosition) {
+    public void commitText(InputConnectionCommandHeader header, CharSequence text,
+            int newCursorPosition) {
         dispatchWithTracing("commitText", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -339,7 +342,7 @@
     }
 
     @Override
-    public void commitCompletion(CompletionInfo text) {
+    public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
         dispatchWithTracing("commitCompletion", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -351,7 +354,7 @@
     }
 
     @Override
-    public void commitCorrection(CorrectionInfo info) {
+    public void commitCorrection(InputConnectionCommandHeader header, CorrectionInfo info) {
         dispatchWithTracing("commitCorrection", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -367,7 +370,7 @@
     }
 
     @Override
-    public void setSelection(int start, int end) {
+    public void setSelection(InputConnectionCommandHeader header, int start, int end) {
         dispatchWithTracing("setSelection", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -379,7 +382,7 @@
     }
 
     @Override
-    public void performEditorAction(int id) {
+    public void performEditorAction(InputConnectionCommandHeader header, int id) {
         dispatchWithTracing("performEditorAction", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -391,7 +394,7 @@
     }
 
     @Override
-    public void performContextMenuAction(int id) {
+    public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
         dispatchWithTracing("performContextMenuAction", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -403,7 +406,7 @@
     }
 
     @Override
-    public void setComposingRegion(int start, int end) {
+    public void setComposingRegion(InputConnectionCommandHeader header, int start, int end) {
         dispatchWithTracing("setComposingRegion", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -419,7 +422,8 @@
     }
 
     @Override
-    public void setComposingText(CharSequence text, int newCursorPosition) {
+    public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
+            int newCursorPosition) {
         dispatchWithTracing("setComposingText", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -430,8 +434,36 @@
         });
     }
 
+    /**
+     * Dispatches {@link InputConnection#finishComposingText()}.
+     *
+     * <p>This method is intended to be called only from {@link InputMethodManager}.</p>
+     */
+    public void finishComposingTextFromImm() {
+        dispatchWithTracing("finishComposingTextFromImm", () -> {
+            if (isFinished()) {
+                // In this case, #finishComposingText() is guaranteed to be called already.
+                // There should be no negative impact if we ignore this call silently.
+                if (DEBUG) {
+                    Log.w(TAG, "Bug 35301295: Redundant finishComposingTextFromImm.");
+                }
+                return;
+            }
+            InputConnection ic = getInputConnection();
+            // Note we do NOT check isActive() here, because this is safe
+            // for an IME to call at any time, and we need to allow it
+            // through to clean up our state after the IME has switched to
+            // another client.
+            if (ic == null) {
+                Log.w(TAG, "finishComposingTextFromImm on inactive InputConnection");
+                return;
+            }
+            ic.finishComposingText();
+        });
+    }
+
     @Override
-    public void finishComposingText() {
+    public void finishComposingText(InputConnectionCommandHeader header) {
         dispatchWithTracing("finishComposingText", () -> {
             if (isFinished()) {
                 // In this case, #finishComposingText() is guaranteed to be called already.
@@ -455,7 +487,7 @@
     }
 
     @Override
-    public void sendKeyEvent(KeyEvent event) {
+    public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
         dispatchWithTracing("sendKeyEvent", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -467,7 +499,7 @@
     }
 
     @Override
-    public void clearMetaKeyStates(int states) {
+    public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
         dispatchWithTracing("clearMetaKeyStates", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -479,7 +511,8 @@
     }
 
     @Override
-    public void deleteSurroundingText(int beforeLength, int afterLength) {
+    public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
+            int afterLength) {
         dispatchWithTracing("deleteSurroundingText", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -491,7 +524,8 @@
     }
 
     @Override
-    public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+    public void deleteSurroundingTextInCodePoints(InputConnectionCommandHeader header,
+            int beforeLength, int afterLength) {
         dispatchWithTracing("deleteSurroundingTextInCodePoints", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -507,7 +541,7 @@
     }
 
     @Override
-    public void beginBatchEdit() {
+    public void beginBatchEdit(InputConnectionCommandHeader header) {
         dispatchWithTracing("beginBatchEdit", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -519,7 +553,7 @@
     }
 
     @Override
-    public void endBatchEdit() {
+    public void endBatchEdit(InputConnectionCommandHeader header) {
         dispatchWithTracing("endBatchEdit", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -531,7 +565,7 @@
     }
 
     @Override
-    public void performSpellCheck() {
+    public void performSpellCheck(InputConnectionCommandHeader header) {
         dispatchWithTracing("performSpellCheck", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -543,7 +577,8 @@
     }
 
     @Override
-    public void performPrivateCommand(String action, Bundle data) {
+    public void performPrivateCommand(InputConnectionCommandHeader header, String action,
+            Bundle data) {
         dispatchWithTracing("performPrivateCommand", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -555,8 +590,8 @@
     }
 
     @Override
-    public void requestCursorUpdates(int cursorUpdateMode, int imeDisplayId,
-            AndroidFuture future /* T=Boolean */) {
+    public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
+            int imeDisplayId, AndroidFuture future /* T=Boolean */) {
         dispatchWithTracing("requestCursorUpdates", future, () -> {
             final InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
@@ -577,7 +612,8 @@
     }
 
     @Override
-    public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
+    public void commitContent(InputConnectionCommandHeader header,
+            InputContentInfo inputContentInfo, int flags, Bundle opts,
             AndroidFuture future /* T=Boolean */) {
         dispatchWithTracing("commitContent", future, () -> {
             final InputConnection ic = getInputConnection();
@@ -599,7 +635,7 @@
     }
 
     @Override
-    public void setImeConsumesInput(boolean imeConsumesInput) {
+    public void setImeConsumesInput(InputConnectionCommandHeader header, boolean imeConsumesInput) {
         dispatchWithTracing("setImeConsumesInput", () -> {
             InputConnection ic = getInputConnection();
             if (ic == null || !isActive()) {
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 93baa19..9443070 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -31,12 +31,15 @@
  * Estimates power consumed by the ambient display
  */
 public class AmbientDisplayPowerCalculator extends PowerCalculator {
-    private final UsageBasedPowerEstimator mPowerEstimator;
+    private final UsageBasedPowerEstimator[] mPowerEstimators;
 
     public AmbientDisplayPowerCalculator(PowerProfile powerProfile) {
-        // TODO(b/200239964): update to support multidisplay.
-        mPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0));
+        final int numDisplays = powerProfile.getNumDisplays();
+        mPowerEstimators = new UsageBasedPowerEstimator[numDisplays];
+        for (int display = 0; display < numDisplays; display++) {
+            mPowerEstimators[display] = new UsageBasedPowerEstimator(
+                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, display));
+        }
     }
 
     /**
@@ -50,8 +53,8 @@
         final int powerModel = getPowerModel(measuredEnergyUC, query);
         final long durationMs = calculateDuration(batteryStats, rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED);
-        final double powerMah = getMeasuredOrEstimatedPower(powerModel,
-                measuredEnergyUC, mPowerEstimator, durationMs);
+        final double powerMah = calculateTotalPower(powerModel, batteryStats, rawRealtimeUs,
+                measuredEnergyUC);
         builder.getAggregateBatteryConsumerBuilder(
                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY, durationMs)
@@ -71,9 +74,8 @@
         final long measuredEnergyUC = batteryStats.getScreenDozeMeasuredBatteryConsumptionUC();
         final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType);
         final int powerModel = getPowerModel(measuredEnergyUC);
-        final double powerMah = getMeasuredOrEstimatedPower(powerModel,
-                batteryStats.getScreenDozeMeasuredBatteryConsumptionUC(),
-                mPowerEstimator, durationMs);
+        final double powerMah = calculateTotalPower(powerModel, batteryStats, rawRealtimeUs,
+                measuredEnergyUC);
         if (powerMah > 0) {
             BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0);
             bs.usagePowerMah = powerMah;
@@ -86,4 +88,26 @@
     private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
         return batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000;
     }
+
+    private double calculateTotalPower(@BatteryConsumer.PowerModel int powerModel,
+            BatteryStats batteryStats, long rawRealtimeUs, long consumptionUC) {
+        switch (powerModel) {
+            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
+                return uCtoMah(consumptionUC);
+            case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
+            default:
+                return calculateEstimatedPower(batteryStats, rawRealtimeUs);
+        }
+    }
+
+    private double calculateEstimatedPower(BatteryStats batteryStats, long rawRealtimeUs) {
+        final int numDisplays = mPowerEstimators.length;
+        double power = 0;
+        for (int display = 0; display < numDisplays; display++) {
+            final long dozeTime = batteryStats.getDisplayScreenDozeTime(display, rawRealtimeUs)
+                    / 1000;
+            power += mPowerEstimators[display].calculatePower(dozeTime);
+        }
+        return power;
+    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3aa63a0..985331c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -896,6 +896,11 @@
          */
         public StopwatchTimer[] screenBrightnessTimers =
                 new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
+        /**
+         * Per display screen state the last time {@link #updateDisplayMeasuredEnergyStatsLocked}
+         * was called.
+         */
+        public int screenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN;
 
         DisplayBatteryStats(Clock clock, TimeBase timeBase) {
             screenOnTimer = new StopwatchTimer(clock, null, -1, null,
@@ -922,6 +927,8 @@
 
     DisplayBatteryStats[] mPerDisplayBatteryStats;
 
+    private int mDisplayMismatchWtfCount = 0;
+
     boolean mInteractive;
     StopwatchTimer mInteractiveTimer;
 
@@ -1066,8 +1073,6 @@
     @GuardedBy("this")
     @VisibleForTesting
     protected @Nullable MeasuredEnergyStats mGlobalMeasuredEnergyStats;
-    /** Last known screen state. Needed for apportioning display energy. */
-    int mScreenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN;
     /** Bluetooth Power calculator for attributing measured bluetooth charge consumption to uids */
     @Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null;
     /** Cpu Power calculator for attributing measured cpu charge consumption to uids */
@@ -13112,22 +13117,43 @@
      * is always 0 when the screen is not "ON" and whenever the rail energy is 0 (if supported).
      * To the extent that those assumptions are violated, the algorithm will err.
      *
-     * @param chargeUC amount of charge (microcoulombs) used by Display since this was last called.
-     * @param screenState screen state at the time this data collection was scheduled
+     * @param chargesUC amount of charge (microcoulombs) used by each Display since this was last
+     *                 called.
+     * @param screenStates each screen state at the time this data collection was scheduled
      */
     @GuardedBy("this")
-    public void updateDisplayMeasuredEnergyStatsLocked(long chargeUC, int screenState,
+    public void updateDisplayMeasuredEnergyStatsLocked(long[] chargesUC, int[] screenStates,
             long elapsedRealtimeMs) {
-        if (DEBUG_ENERGY) Slog.d(TAG, "Updating display stats: " + chargeUC);
+        if (DEBUG_ENERGY) Slog.d(TAG, "Updating display stats: " + Arrays.toString(chargesUC));
         if (mGlobalMeasuredEnergyStats == null) {
             return;
         }
 
-        final @StandardPowerBucket int powerBucket =
-                MeasuredEnergyStats.getDisplayPowerBucket(mScreenStateAtLastEnergyMeasurement);
-        mScreenStateAtLastEnergyMeasurement = screenState;
+        final int numDisplays;
+        if (mPerDisplayBatteryStats.length == screenStates.length) {
+            numDisplays = screenStates.length;
+        } else {
+            // if this point is reached, it will be reached every display state change.
+            // Rate limit the wtf logging to once every 100 display updates.
+            if (mDisplayMismatchWtfCount++ % 100 == 0) {
+                Slog.wtf(TAG, "Mismatch between PowerProfile reported display count ("
+                        + mPerDisplayBatteryStats.length
+                        + ") and PowerStatsHal reported display count (" + screenStates.length
+                        + ")");
+            }
+            // Keep the show going, use the shorter of the two.
+            numDisplays = mPerDisplayBatteryStats.length < screenStates.length
+                    ? mPerDisplayBatteryStats.length : screenStates.length;
+        }
 
-        if (!mOnBatteryInternal || chargeUC <= 0) {
+        final int[] oldScreenStates = new int[numDisplays];
+        for (int i = 0; i < numDisplays; i++) {
+            final int screenState = screenStates[i];
+            oldScreenStates[i] = mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement;
+            mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
+        }
+
+        if (!mOnBatteryInternal) {
             // There's nothing further to update.
             return;
         }
@@ -13142,17 +13168,31 @@
             return;
         }
 
-        mGlobalMeasuredEnergyStats.updateStandardBucket(powerBucket, chargeUC);
+        long totalScreenOnChargeUC = 0;
+        for (int i = 0; i < numDisplays; i++) {
+            final long chargeUC = chargesUC[i];
+            if (chargeUC <= 0) {
+                // There's nothing further to update.
+                continue;
+            }
+
+            final @StandardPowerBucket int powerBucket =
+                    MeasuredEnergyStats.getDisplayPowerBucket(oldScreenStates[i]);
+            mGlobalMeasuredEnergyStats.updateStandardBucket(powerBucket, chargeUC);
+            if (powerBucket == MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON) {
+                totalScreenOnChargeUC += chargeUC;
+            }
+        }
 
         // Now we blame individual apps, but only if the display was ON.
-        if (powerBucket != MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON) {
+        if (totalScreenOnChargeUC <= 0) {
             return;
         }
         // TODO(b/175726779): Consider unifying the code with the non-rail display power blaming.
 
         // NOTE: fg time is NOT pooled. If two uids are both somehow in fg, then that time is
         // 'double counted' and will simply exceed the realtime that elapsed.
-        // If multidisplay becomes a reality, this is probably more reasonable than pooling.
+        // TODO(b/175726779): collect per display uid visibility for display power attribution.
 
         // Collect total time since mark so that we can normalize power.
         final SparseDoubleArray fgTimeUsArray = new SparseDoubleArray();
@@ -13165,7 +13205,8 @@
             if (fgTimeUs == 0) continue;
             fgTimeUsArray.put(uid.getUid(), (double) fgTimeUs);
         }
-        distributeEnergyToUidsLocked(powerBucket, chargeUC, fgTimeUsArray, 0);
+        distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON,
+                totalScreenOnChargeUC, fgTimeUsArray, 0);
     }
 
     /**
@@ -15122,7 +15163,12 @@
     public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
             String[] customBucketNames) {
         boolean supportedBucketMismatch = false;
-        mScreenStateAtLastEnergyMeasurement = mScreenState;
+
+        final int numDisplays = mPerDisplayBatteryStats.length;
+        for (int i = 0; i < numDisplays; i++) {
+            final int screenState = mPerDisplayBatteryStats[i].screenState;
+            mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
+        }
 
         if (supportedStandardBuckets == null) {
             if (mGlobalMeasuredEnergyStats != null) {
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
index 4979ecb..93d562c 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -133,32 +133,6 @@
     }
 
     /**
-     * Returns either the measured energy converted to mAh or a usage-based estimate.
-     */
-    protected static double getMeasuredOrEstimatedPower(@BatteryConsumer.PowerModel int powerModel,
-            long measuredEnergyUC, UsageBasedPowerEstimator powerEstimator, long durationMs) {
-        switch (powerModel) {
-            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
-                return uCtoMah(measuredEnergyUC);
-            case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
-            default:
-                return powerEstimator.calculatePower(durationMs);
-        }
-    }
-
-    /**
-     * Returns either the measured energy converted to mAh or a usage-based estimate.
-     */
-    protected static double getMeasuredOrEstimatedPower(
-            long measuredEnergyUC, UsageBasedPowerEstimator powerEstimator, long durationMs) {
-        if (measuredEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE) {
-            return uCtoMah(measuredEnergyUC);
-        } else {
-            return powerEstimator.calculatePower(durationMs);
-        }
-    }
-
-    /**
      * Prints formatted amount of power in milli-amp-hours.
      */
     public static void printPowerMah(PrintWriter pw, double powerMah) {
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 72ad4e7..2b63459 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -44,8 +44,8 @@
     // Minimum amount of time the screen should be on to start smearing drain to apps
     public static final long MIN_ACTIVE_TIME_FOR_SMEARING = 10 * DateUtils.MINUTE_IN_MILLIS;
 
-    private final UsageBasedPowerEstimator mScreenOnPowerEstimator;
-    private final UsageBasedPowerEstimator mScreenFullPowerEstimator;
+    private final UsageBasedPowerEstimator[] mScreenOnPowerEstimators;
+    private final UsageBasedPowerEstimator[] mScreenFullPowerEstimators;
 
     private static class PowerAndDuration {
         public long durationMs;
@@ -53,11 +53,16 @@
     }
 
     public ScreenPowerCalculator(PowerProfile powerProfile) {
-        // TODO(b/200239964): update to support multidisplay.
-        mScreenOnPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0));
-        mScreenFullPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0));
+        final int numDisplays = powerProfile.getNumDisplays();
+        mScreenOnPowerEstimators = new UsageBasedPowerEstimator[numDisplays];
+        mScreenFullPowerEstimators = new UsageBasedPowerEstimator[numDisplays];
+        for (int display = 0; display < numDisplays; display++) {
+            mScreenOnPowerEstimators[display] = new UsageBasedPowerEstimator(
+                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, display));
+            mScreenFullPowerEstimators[display] = new UsageBasedPowerEstimator(
+                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL,
+                            display));
+        }
     }
 
     @Override
@@ -172,7 +177,7 @@
             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
             default:
                 totalPowerAndDuration.powerMah = calculateTotalPowerFromBrightness(batteryStats,
-                        rawRealtimeUs, statsType, totalPowerAndDuration.durationMs);
+                        rawRealtimeUs);
         }
     }
 
@@ -194,19 +199,25 @@
         return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000;
     }
 
-    private double calculateTotalPowerFromBrightness(BatteryStats batteryStats, long rawRealtimeUs,
-            int statsType, long durationMs) {
-        double power = mScreenOnPowerEstimator.calculatePower(durationMs);
-        for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
-            final long brightnessTime =
-                    batteryStats.getScreenBrightnessTime(i, rawRealtimeUs, statsType) / 1000;
-            final double binPowerMah = mScreenFullPowerEstimator.calculatePower(brightnessTime)
-                    * (i + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
-            if (DEBUG && binPowerMah != 0) {
-                Slog.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
-                        + " power=" + formatCharge(binPowerMah));
+    private double calculateTotalPowerFromBrightness(BatteryStats batteryStats,
+            long rawRealtimeUs) {
+        final int numDisplays = mScreenOnPowerEstimators.length;
+        double power = 0;
+        for (int display = 0; display < numDisplays; display++) {
+            final long displayTime = batteryStats.getDisplayScreenOnTime(display, rawRealtimeUs)
+                    / 1000;
+            power += mScreenOnPowerEstimators[display].calculatePower(displayTime);
+            for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) {
+                final long brightnessTime = batteryStats.getDisplayScreenBrightnessTime(display,
+                        bin, rawRealtimeUs) / 1000;
+                final double binPowerMah = mScreenFullPowerEstimators[display].calculatePower(
+                        brightnessTime) * (bin + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
+                if (DEBUG && binPowerMah != 0) {
+                    Slog.d(TAG, "Screen bin #" + bin + ": time=" + brightnessTime
+                            + " power=" + formatCharge(binPowerMah));
+                }
+                power += binPowerMah;
             }
-            power += binPowerMah;
         }
         return power;
     }
diff --git a/core/java/com/android/internal/policy/SystemBarUtils.java b/core/java/com/android/internal/policy/SystemBarUtils.java
new file mode 100644
index 0000000..6bf1333
--- /dev/null
+++ b/core/java/com/android/internal/policy/SystemBarUtils.java
@@ -0,0 +1,94 @@
+/*
+ * 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.internal.policy;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Insets;
+import android.util.RotationUtils;
+import android.view.DisplayCutout;
+import android.view.Surface;
+
+import com.android.internal.R;
+
+/**
+ * Utility functions for system bars used by both window manager and System UI.
+ *
+ * @hide
+ */
+public final class SystemBarUtils {
+
+    /**
+     * Gets the status bar height.
+     */
+    public static int getStatusBarHeight(Context context) {
+        return getStatusBarHeight(context.getResources(), context.getDisplay().getCutout());
+    }
+
+    /**
+     * Gets the status bar height with a specific display cutout.
+     */
+    public static int getStatusBarHeight(Resources res, DisplayCutout cutout) {
+        final int defaultSize = res.getDimensionPixelSize(R.dimen.status_bar_height);
+        final int safeInsetTop = cutout == null ? 0 : cutout.getSafeInsetTop();
+        final int waterfallInsetTop = cutout == null ? 0 : cutout.getWaterfallInsets().top;
+        // The status bar height should be:
+        // Max(top cutout size, (status bar default height + waterfall top size))
+        return Math.max(safeInsetTop, defaultSize + waterfallInsetTop);
+    }
+
+    /**
+     * Gets the status bar height for a specific rotation.
+     */
+    public static int getStatusBarHeightForRotation(
+            Context context, @Surface.Rotation int targetRot) {
+        final int rotation = context.getDisplay().getRotation();
+        final DisplayCutout cutout = context.getDisplay().getCutout();
+
+        Insets insets = cutout == null ? Insets.NONE : Insets.of(cutout.getSafeInsets());
+        Insets waterfallInsets = cutout == null ? Insets.NONE : cutout.getWaterfallInsets();
+        // rotate insets to target rotation if needed.
+        if (rotation != targetRot) {
+            if (!insets.equals(Insets.NONE)) {
+                insets = RotationUtils.rotateInsets(
+                        insets, RotationUtils.deltaRotation(rotation, targetRot));
+            }
+            if (!waterfallInsets.equals(Insets.NONE)) {
+                waterfallInsets = RotationUtils.rotateInsets(
+                        waterfallInsets, RotationUtils.deltaRotation(rotation, targetRot));
+            }
+        }
+        final int defaultSize =
+                context.getResources().getDimensionPixelSize(R.dimen.status_bar_height);
+        // The status bar height should be:
+        // Max(top cutout size, (status bar default height + waterfall top size))
+        return Math.max(insets.top, defaultSize + waterfallInsets.top);
+    }
+
+    /**
+     * Gets the height of area above QQS where battery/time go in notification panel. The height
+     * equals to status bar height if status bar height is bigger than the
+     * {@link R.dimen#quick_qs_offset_height}.
+     */
+    public static int getQuickQsOffsetHeight(Context context) {
+        final int defaultSize = context.getResources().getDimensionPixelSize(
+                R.dimen.quick_qs_offset_height);
+        final int statusBarHeight = getStatusBarHeight(context);
+        // Equals to status bar height if status bar height is bigger.
+        return Math.max(defaultSize, statusBarHeight);
+    }
+}
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 2e6f9e5..df55beb 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -24,6 +24,7 @@
 import android.view.inputmethod.InputContentInfo;
 
 import com.android.internal.infra.AndroidFuture;
+import com.android.internal.inputmethod.InputConnectionCommandHeader;
 
 /**
  * Interface from an input method to the application, allowing it to perform
@@ -31,58 +32,67 @@
  * {@hide}
  */
  oneway interface IInputContext {
-    void getTextBeforeCursor(int length, int flags, in AndroidFuture future /* T=CharSequence */);
+    void getTextBeforeCursor(in InputConnectionCommandHeader header, int length, int flags,
+            in AndroidFuture future /* T=CharSequence */);
 
-    void getTextAfterCursor(int length, int flags, in AndroidFuture future /* T=CharSequence */);
+    void getTextAfterCursor(in InputConnectionCommandHeader header, int length, int flags,
+            in AndroidFuture future /* T=CharSequence */);
 
-    void getCursorCapsMode(int reqModes, in AndroidFuture future /* T=Integer */);
+    void getCursorCapsMode(in InputConnectionCommandHeader header, int reqModes,
+            in AndroidFuture future /* T=Integer */);
 
-    void getExtractedText(in ExtractedTextRequest request, int flags,
-            in AndroidFuture future /* T=ExtractedText */);
+    void getExtractedText(in InputConnectionCommandHeader header, in ExtractedTextRequest request,
+            int flags, in AndroidFuture future /* T=ExtractedText */);
 
-    void deleteSurroundingText(int beforeLength, int afterLength);
-    void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
+    void deleteSurroundingText(in InputConnectionCommandHeader header, int beforeLength,
+            int afterLength);
+    void deleteSurroundingTextInCodePoints(in InputConnectionCommandHeader header, int beforeLength,
+            int afterLength);
 
-    void setComposingText(CharSequence text, int newCursorPosition);
+    void setComposingText(in InputConnectionCommandHeader header, CharSequence text,
+            int newCursorPosition);
 
-    void finishComposingText();
+    void finishComposingText(in InputConnectionCommandHeader header);
 
-    void commitText(CharSequence text, int newCursorPosition);
+    void commitText(in InputConnectionCommandHeader header, CharSequence text,
+            int newCursorPosition);
 
-    void commitCompletion(in CompletionInfo completion);
+    void commitCompletion(in InputConnectionCommandHeader header, in CompletionInfo completion);
 
-    void commitCorrection(in CorrectionInfo correction);
+    void commitCorrection(in InputConnectionCommandHeader header, in CorrectionInfo correction);
 
-    void setSelection(int start, int end);
+    void setSelection(in InputConnectionCommandHeader header, int start, int end);
 
-    void performEditorAction(int actionCode);
+    void performEditorAction(in InputConnectionCommandHeader header, int actionCode);
 
-    void performContextMenuAction(int id);
+    void performContextMenuAction(in InputConnectionCommandHeader header, int id);
 
-    void beginBatchEdit();
+    void beginBatchEdit(in InputConnectionCommandHeader header);
 
-    void endBatchEdit();
+    void endBatchEdit(in InputConnectionCommandHeader header);
 
-    void sendKeyEvent(in KeyEvent event);
+    void sendKeyEvent(in InputConnectionCommandHeader header, in KeyEvent event);
 
-    void clearMetaKeyStates(int states);
+    void clearMetaKeyStates(in InputConnectionCommandHeader header, int states);
 
-    void performSpellCheck();
+    void performSpellCheck(in InputConnectionCommandHeader header);
 
-    void performPrivateCommand(String action, in Bundle data);
+    void performPrivateCommand(in InputConnectionCommandHeader header, String action,
+            in Bundle data);
 
-    void setComposingRegion(int start, int end);
+    void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
 
-    void getSelectedText(int flags, in AndroidFuture future /* T=CharSequence */);
+    void getSelectedText(in InputConnectionCommandHeader header, int flags,
+            in AndroidFuture future /* T=CharSequence */);
 
-    void requestCursorUpdates(int cursorUpdateMode, int imeDisplayId,
-            in AndroidFuture future /* T=Boolean */);
+    void requestCursorUpdates(in InputConnectionCommandHeader header, int cursorUpdateMode,
+            int imeDisplayId, in AndroidFuture future /* T=Boolean */);
 
-    void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
-            in AndroidFuture future /* T=Boolean */);
+    void commitContent(in InputConnectionCommandHeader header, in InputContentInfo inputContentInfo,
+            int flags, in Bundle opts, in AndroidFuture future /* T=Boolean */);
 
-    void getSurroundingText(int beforeLength, int afterLength, int flags,
-            in AndroidFuture future /* T=SurroundingText */);
+    void getSurroundingText(in InputConnectionCommandHeader header, int beforeLength,
+            int afterLength, int flags, in AndroidFuture future /* T=SurroundingText */);
 
-    void setImeConsumesInput(boolean imeConsumesInput);
+    void setImeConsumesInput(in InputConnectionCommandHeader header, boolean imeConsumesInput);
 }
diff --git a/core/java/com/android/internal/view/ListViewCaptureHelper.java b/core/java/com/android/internal/view/ListViewCaptureHelper.java
index f4a5b71..11ed820 100644
--- a/core/java/com/android/internal/view/ListViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/ListViewCaptureHelper.java
@@ -23,10 +23,13 @@
 
 import android.annotation.NonNull;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.util.Log;
 import android.view.View;
 import android.widget.ListView;
 
+import java.util.function.Consumer;
+
 /**
  * Scroll capture support for ListView.
  *
@@ -56,8 +59,8 @@
     }
 
     @Override
-    public ScrollResult onScrollRequested(@NonNull ListView listView, Rect scrollBounds,
-            Rect requestRect) {
+    public void onScrollRequested(@NonNull ListView listView, Rect scrollBounds,
+            Rect requestRect, CancellationSignal signal, Consumer<ScrollResult> resultConsumer) {
         Log.d(TAG, "-----------------------------------------------------------");
         Log.d(TAG, "onScrollRequested(scrollBounds=" + scrollBounds + ", "
                 + "requestRect=" + requestRect + ")");
@@ -69,7 +72,8 @@
 
         if (!listView.isVisibleToUser() || listView.getChildCount() == 0) {
             Log.w(TAG, "listView is empty or not visible, cannot continue");
-            return result; // result.availableArea == empty Rect
+            resultConsumer.accept(result);  // result.availableArea == empty Rect
+            return;
         }
 
         // Make requestRect relative to RecyclerView (from scrollBounds)
@@ -117,7 +121,7 @@
                     mScrollDelta, scrollBounds, requestedContainerBounds);
         }
         Log.d(TAG, "-----------------------------------------------------------");
-        return result;
+        resultConsumer.accept(result);
     }
 
     @Override
diff --git a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
index 64622f0..8192ffd 100644
--- a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
@@ -18,11 +18,14 @@
 
 import android.annotation.NonNull;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 
+import java.util.function.Consumer;
+
 /**
  * ScrollCapture for RecyclerView and <i>RecyclerView-like</i> ViewGroups.
  * <p>
@@ -61,8 +64,8 @@
     }
 
     @Override
-    public ScrollResult onScrollRequested(@NonNull ViewGroup recyclerView, Rect scrollBounds,
-            Rect requestRect) {
+    public void onScrollRequested(@NonNull ViewGroup recyclerView, Rect scrollBounds,
+            Rect requestRect, CancellationSignal signal, Consumer<ScrollResult> resultConsumer) {
         ScrollResult result = new ScrollResult();
         result.requestedArea = new Rect(requestRect);
         result.scrollDelta = mScrollDelta;
@@ -70,7 +73,8 @@
 
         if (!recyclerView.isVisibleToUser() || recyclerView.getChildCount() == 0) {
             Log.w(TAG, "recyclerView is empty or not visible, cannot continue");
-            return result; // result.availableArea == empty Rect
+            resultConsumer.accept(result); // result.availableArea == empty Rect
+            return;
         }
 
         // move from scrollBounds-relative to parent-local coordinates
@@ -83,7 +87,8 @@
         View anchor = findChildNearestTarget(recyclerView, requestedContainerBounds);
         if (anchor == null) {
             Log.w(TAG, "Failed to locate anchor view");
-            return result; // result.availableArea == empty rect
+            resultConsumer.accept(result); // result.availableArea == empty rect
+            return;
         }
 
         Rect requestedContentBounds = new Rect(requestedContainerBounds);
@@ -113,13 +118,14 @@
 
         if (!requestedContainerBounds.intersect(recyclerLocalVisible)) {
             // Requested area is still not visible
-            return result;
+            resultConsumer.accept(result);
+            return;
         }
         Rect available = new Rect(requestedContainerBounds);
         available.offset(-scrollBounds.left, -scrollBounds.top);
         available.offset(0, mScrollDelta);
         result.availableArea = available;
-        return result;
+        resultConsumer.accept(result);
     }
 
     /**
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
index baf725d..347ab1f 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
@@ -18,9 +18,12 @@
 
 import android.annotation.NonNull;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.view.View;
 import android.view.ViewGroup;
 
+import java.util.function.Consumer;
+
 /**
  * Provides view-specific handling to ScrollCaptureViewSupport.
  *
@@ -98,21 +101,22 @@
     /**
      * Map the request onto the screen.
      * <p>
-     * Given a  rect describing the area to capture, relative to scrollBounds, take actions
+     * Given a rect describing the area to capture, relative to scrollBounds, take actions
      * necessary to bring the content within the rectangle into the visible area of the view if
      * needed and return the resulting rectangle describing the position and bounds of the area
      * which is visible.
-     *
-     * @param view the view being captured
+     *  @param view the view being captured
      * @param scrollBounds the area in which scrolling content moves, local to the {@code containing
      *                     view}
      * @param requestRect  the area relative to {@code scrollBounds} which describes the location of
-     *                     content to capture for the request
-     * @return the result of the request as a {@link ScrollResult}
+ *                     content to capture for the request
+     * @param cancellationSignal allows for the request to be cancelled by the caller
+     * @param resultConsumer accepts the result of the request as a {@link ScrollResult}
      */
     @NonNull
-    ScrollResult onScrollRequested(@NonNull V view, @NonNull Rect scrollBounds,
-            @NonNull Rect requestRect);
+    void onScrollRequested(@NonNull V view, @NonNull Rect scrollBounds,
+            @NonNull Rect requestRect, CancellationSignal cancellationSignal,
+            Consumer<ScrollResult> resultConsumer);
 
     /**
      * Restore the target after capture.
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 2f25d60..94a8ae5 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -249,46 +249,38 @@
         }
 
         // Ask the view to scroll as needed to bring this area into view.
-        ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
-                requestRect);
+        mViewHelper.onScrollRequested(view, session.getScrollBounds(), requestRect, signal,
+                (result) -> onScrollResult(result, view, signal, onComplete));
+    }
+
+    private void onScrollResult(ScrollResult scrollResult, V view, CancellationSignal signal,
+            Consumer<Rect> onComplete) {
+
+        if (signal.isCanceled()) {
+            Log.w(TAG, "onScrollCaptureImageRequest: cancelled! skipping render.");
+            return;
+        }
 
         if (scrollResult.availableArea.isEmpty()) {
             onComplete.accept(scrollResult.availableArea);
             return;
         }
 
-        // For image capture, shift back by scrollDelta to arrive at the location within the view
-        // where the requested content will be drawn
+        // For image capture, shift back by scrollDelta to arrive at the location
+        // within the view where the requested content will be drawn
         Rect viewCaptureArea = new Rect(scrollResult.availableArea);
         viewCaptureArea.offset(0, -scrollResult.scrollDelta);
 
-        Runnable captureAction = () -> {
-            if (signal.isCanceled()) {
-                Log.w(TAG, "onScrollCaptureImageRequest: cancelled! skipping render.");
-            } else {
-                int result = mRenderer.renderView(view, viewCaptureArea);
-                switch (result) {
-                    case HardwareRenderer.SYNC_OK:
-                    case HardwareRenderer.SYNC_REDRAW_REQUESTED:
-                        /* Frame synced, buffer will be produced... notify client. */
-                        onComplete.accept(new Rect(scrollResult.availableArea));
-                        return;
-                    case HardwareRenderer.SYNC_FRAME_DROPPED:
-                        Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !");
-                        break;
-                    case HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND:
-                        Log.e(TAG, "syncAndDraw(): SYNC_LOST_SURFACE !");
-                        break;
-                    case HardwareRenderer.SYNC_CONTEXT_IS_STOPPED:
-                        Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !");
-                        break;
-                }
-                // No buffer will be produced.
-                onComplete.accept(new Rect(/* empty */));
-            }
-        };
-
-        view.postOnAnimationDelayed(captureAction, mPostScrollDelayMillis);
+        int result = mRenderer.renderView(view, viewCaptureArea);
+        if (result == HardwareRenderer.SYNC_OK
+                || result == HardwareRenderer.SYNC_REDRAW_REQUESTED) {
+            /* Frame synced, buffer will be produced... notify client. */
+            onComplete.accept(new Rect(scrollResult.availableArea));
+        } else {
+            // No buffer will be produced.
+            Log.e(TAG, "syncAndDraw(): SyncAndDrawResult = " + result);
+            onComplete.accept(new Rect(/* empty */));
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
index db7881f..c8ff283 100644
--- a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
@@ -19,10 +19,13 @@
 import android.annotation.NonNull;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 
+import java.util.function.Consumer;
+
 /**
  * ScrollCapture for ScrollView and <i>ScrollView-like</i> ViewGroups.
  * <p>
@@ -60,8 +63,8 @@
         }
     }
 
-    public ScrollResult onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds,
-            Rect requestRect) {
+    public void onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds,
+            Rect requestRect, CancellationSignal signal, Consumer<ScrollResult> resultConsumer) {
         /*
                +---------+ <----+ Content [25,25 - 275,1025] (w=250,h=1000)
                |         |
@@ -105,7 +108,8 @@
         final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
         if (contentView == null) {
             // No child view? Cannot continue.
-            return result;
+            resultConsumer.accept(result);
+            return;
         }
 
         //  1) Translate request rect to make it relative to container view
@@ -155,7 +159,8 @@
         if (!view.getChildVisibleRect(contentView, available, offset)) {
             available.setEmpty();
             result.availableArea = available;
-            return result;
+            resultConsumer.accept(result);
+            return;
         }
         // Transform back from global to content-view local
         available.offset(-offset.x, -offset.y);
@@ -174,7 +179,7 @@
         available.offset(0, scrollDelta);
 
         result.availableArea = new Rect(available);
-        return result;
+        resultConsumer.accept(result);
     }
 
     public void onPrepareForEnd(@NonNull ViewGroup view) {
diff --git a/core/java/com/android/internal/view/WebViewCaptureHelper.java b/core/java/com/android/internal/view/WebViewCaptureHelper.java
index 37ce782..086e00c 100644
--- a/core/java/com/android/internal/view/WebViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/WebViewCaptureHelper.java
@@ -23,8 +23,11 @@
 
 import android.annotation.NonNull;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.webkit.WebView;
 
+import java.util.function.Consumer;
+
 /**
  * ScrollCapture for WebView.
  */
@@ -51,8 +54,9 @@
 
     @NonNull
     @Override
-    public ScrollResult onScrollRequested(@NonNull WebView view, @NonNull Rect scrollBounds,
-            @NonNull Rect requestRect) {
+    public void onScrollRequested(@NonNull WebView view, @NonNull Rect scrollBounds,
+            @NonNull Rect requestRect, CancellationSignal cancellationSignal,
+            Consumer<ScrollResult> resultConsumer) {
 
         int scrollDelta = view.getScrollY() - mOriginScrollY;
 
@@ -64,7 +68,7 @@
         mWebViewBounds.set(0, 0, view.getWidth(), view.getHeight());
 
         if (!view.isVisibleToUser()) {
-            return result;
+            resultConsumer.accept(result);
         }
 
         // Map the request into local coordinates
@@ -88,7 +92,7 @@
             result.availableArea = new Rect(mRequestWebViewLocal);
             result.availableArea.offset(0, result.scrollDelta);
         }
-        return result;
+        resultConsumer.accept(result);
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 514e0b8..a0bf9b5 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -16,62 +16,18 @@
 
 package com.android.internal.widget;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Size;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.MeasureSpec;
 import android.view.View.OnLayoutChangeListener;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.Window;
-import android.view.WindowManager;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.Transformation;
-import android.widget.ArrayAdapter;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
 import android.widget.PopupWindow;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Comparator;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -90,7 +46,6 @@
     private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
             item -> false;
 
-    private final Context mContext;
     private final Window mWindow;
     private final FloatingToolbarPopup mPopup;
 
@@ -158,9 +113,8 @@
     public FloatingToolbar(Window window) {
         // TODO(b/65172902): Pass context in constructor when DecorView (and other callers)
         // supports multi-display.
-        mContext = applyDefaultTheme(window.getContext());
         mWindow = Objects.requireNonNull(window);
-        mPopup = new FloatingToolbarPopup(mContext, window.getDecorView());
+        mPopup = new FloatingToolbarPopup(window.getContext(), window.getDecorView());
     }
 
     /**
@@ -321,1544 +275,4 @@
     private void unregisterOrientationHandler() {
         mWindow.getDecorView().removeOnLayoutChangeListener(mOrientationChangeHandler);
     }
-
-
-    /**
-     * A popup window used by the floating toolbar.
-     *
-     * This class is responsible for the rendering/animation of the floating toolbar.
-     * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
-     * to transition between panels.
-     */
-    private static final class FloatingToolbarPopup {
-
-        /* Minimum and maximum number of items allowed in the overflow. */
-        private static final int MIN_OVERFLOW_SIZE = 2;
-        private static final int MAX_OVERFLOW_SIZE = 4;
-
-        private final Context mContext;
-        private final View mParent;  // Parent for the popup window.
-        private final PopupWindow mPopupWindow;
-
-        /* Margins between the popup window and it's content. */
-        private final int mMarginHorizontal;
-        private final int mMarginVertical;
-
-        /* View components */
-        private final ViewGroup mContentContainer;  // holds all contents.
-        private final ViewGroup mMainPanel;  // holds menu items that are initially displayed.
-        private final OverflowPanel mOverflowPanel;  // holds menu items hidden in the overflow.
-        private final ImageButton mOverflowButton;  // opens/closes the overflow.
-        /* overflow button drawables. */
-        private final Drawable mArrow;
-        private final Drawable mOverflow;
-        private final AnimatedVectorDrawable mToArrow;
-        private final AnimatedVectorDrawable mToOverflow;
-
-        private final OverflowPanelViewHelper mOverflowPanelViewHelper;
-
-        /* Animation interpolators. */
-        private final Interpolator mLogAccelerateInterpolator;
-        private final Interpolator mFastOutSlowInInterpolator;
-        private final Interpolator mLinearOutSlowInInterpolator;
-        private final Interpolator mFastOutLinearInInterpolator;
-
-        /* Animations. */
-        private final AnimatorSet mShowAnimation;
-        private final AnimatorSet mDismissAnimation;
-        private final AnimatorSet mHideAnimation;
-        private final AnimationSet mOpenOverflowAnimation;
-        private final AnimationSet mCloseOverflowAnimation;
-        private final Animation.AnimationListener mOverflowAnimationListener;
-
-        private final Rect mViewPortOnScreen = new Rect();  // portion of screen we can draw in.
-        private final Point mCoordsOnWindow = new Point();  // popup window coordinates.
-        /* Temporary data holders. Reset values before using. */
-        private final int[] mTmpCoords = new int[2];
-
-        private final Region mTouchableRegion = new Region();
-        private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
-                info -> {
-                    info.contentInsets.setEmpty();
-                    info.visibleInsets.setEmpty();
-                    info.touchableRegion.set(mTouchableRegion);
-                    info.setTouchableInsets(
-                            ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-                };
-
-        private final int mLineHeight;
-        private final int mIconTextSpacing;
-
-        /**
-         * @see OverflowPanelViewHelper#preparePopupContent().
-         */
-        private final Runnable mPreparePopupContentRTLHelper = new Runnable() {
-            @Override
-            public void run() {
-                setPanelsStatesAtRestingPosition();
-                setContentAreaAsTouchableSurface();
-                mContentContainer.setAlpha(1);
-            }
-        };
-
-        private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
-        private boolean mHidden; // tracks whether this popup is hidden or hiding.
-
-        /* Calculated sizes for panels and overflow button. */
-        private final Size mOverflowButtonSize;
-        private Size mOverflowPanelSize;  // Should be null when there is no overflow.
-        private Size mMainPanelSize;
-
-        /* Menu items and click listeners */
-        private final Map<MenuItemRepr, MenuItem> mMenuItems = new LinkedHashMap<>();
-        private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
-        private final View.OnClickListener mMenuItemButtonOnClickListener =
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        if (mOnMenuItemClickListener == null) {
-                            return;
-                        }
-                        final Object tag = v.getTag();
-                        if (!(tag instanceof MenuItemRepr)) {
-                            return;
-                        }
-                        final MenuItem menuItem = mMenuItems.get((MenuItemRepr) tag);
-                        if (menuItem == null) {
-                            return;
-                        }
-                        mOnMenuItemClickListener.onMenuItemClick(menuItem);
-                    }
-                };
-
-        private boolean mOpenOverflowUpwards;  // Whether the overflow opens upwards or downwards.
-        private boolean mIsOverflowOpen;
-
-        private int mTransitionDurationScale;  // Used to scale the toolbar transition duration.
-
-        /**
-         * Initializes a new floating toolbar popup.
-         *
-         * @param parent  A parent view to get the {@link android.view.View#getWindowToken()} token
-         *      from.
-         */
-        public FloatingToolbarPopup(Context context, View parent) {
-            mParent = Objects.requireNonNull(parent);
-            mContext = Objects.requireNonNull(context);
-            mContentContainer = createContentContainer(context);
-            mPopupWindow = createPopupWindow(mContentContainer);
-            mMarginHorizontal = parent.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
-            mMarginVertical = parent.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
-            mLineHeight = context.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_height);
-            mIconTextSpacing = context.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
-
-            // Interpolators
-            mLogAccelerateInterpolator = new LogAccelerateInterpolator();
-            mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
-                    mContext, android.R.interpolator.fast_out_slow_in);
-            mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
-                    mContext, android.R.interpolator.linear_out_slow_in);
-            mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
-                    mContext, android.R.interpolator.fast_out_linear_in);
-
-            // Drawables. Needed for views.
-            mArrow = mContext.getResources()
-                    .getDrawable(R.drawable.ft_avd_tooverflow, mContext.getTheme());
-            mArrow.setAutoMirrored(true);
-            mOverflow = mContext.getResources()
-                    .getDrawable(R.drawable.ft_avd_toarrow, mContext.getTheme());
-            mOverflow.setAutoMirrored(true);
-            mToArrow = (AnimatedVectorDrawable) mContext.getResources()
-                    .getDrawable(R.drawable.ft_avd_toarrow_animation, mContext.getTheme());
-            mToArrow.setAutoMirrored(true);
-            mToOverflow = (AnimatedVectorDrawable) mContext.getResources()
-                    .getDrawable(R.drawable.ft_avd_tooverflow_animation, mContext.getTheme());
-            mToOverflow.setAutoMirrored(true);
-
-            // Views
-            mOverflowButton = createOverflowButton();
-            mOverflowButtonSize = measure(mOverflowButton);
-            mMainPanel = createMainPanel();
-            mOverflowPanelViewHelper = new OverflowPanelViewHelper(mContext, mIconTextSpacing);
-            mOverflowPanel = createOverflowPanel();
-
-            // Animation. Need views.
-            mOverflowAnimationListener = createOverflowAnimationListener();
-            mOpenOverflowAnimation = new AnimationSet(true);
-            mOpenOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
-            mCloseOverflowAnimation = new AnimationSet(true);
-            mCloseOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
-            mShowAnimation = createEnterAnimation(mContentContainer);
-            mDismissAnimation = createExitAnimation(
-                    mContentContainer,
-                    150,  // startDelay
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            mPopupWindow.dismiss();
-                            mContentContainer.removeAllViews();
-                        }
-                    });
-            mHideAnimation = createExitAnimation(
-                    mContentContainer,
-                    0,  // startDelay
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            mPopupWindow.dismiss();
-                        }
-                    });
-        }
-
-        /**
-         * Makes this toolbar "outside touchable" and sets the onDismissListener.
-         *
-         * @param outsideTouchable if true, the popup will be made "outside touchable" and
-         *      "non focusable". The reverse will happen if false.
-         * @param onDismiss
-         *
-         * @return true if the "outsideTouchable" setting was modified. Otherwise returns false
-         *
-         * @see PopupWindow#setOutsideTouchable(boolean)
-         * @see PopupWindow#setFocusable(boolean)
-         * @see PopupWindow.OnDismissListener
-         */
-        public boolean setOutsideTouchable(
-                boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
-            boolean ret = false;
-            if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
-                mPopupWindow.setOutsideTouchable(outsideTouchable);
-                mPopupWindow.setFocusable(!outsideTouchable);
-                mPopupWindow.update();
-                ret = true;
-            }
-            mPopupWindow.setOnDismissListener(onDismiss);
-            return ret;
-        }
-
-        /**
-         * Lays out buttons for the specified menu items.
-         * Requires a subsequent call to {@link #show()} to show the items.
-         */
-        public void layoutMenuItems(
-                List<MenuItem> menuItems,
-                MenuItem.OnMenuItemClickListener menuItemClickListener,
-                int suggestedWidth) {
-            cancelOverflowAnimations();
-            clearPanels();
-            updateMenuItems(menuItems, menuItemClickListener);
-            menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth));
-            if (!menuItems.isEmpty()) {
-                // Add remaining items to the overflow.
-                layoutOverflowPanelItems(menuItems);
-            }
-            updatePopupSize();
-        }
-
-        /**
-         * Updates the popup's menu items without rebuilding the widget.
-         * Use in place of layoutMenuItems() when the popup's views need not be reconstructed.
-         *
-         * @see isLayoutRequired(List<MenuItem>)
-         */
-        public void updateMenuItems(
-                List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener) {
-            mMenuItems.clear();
-            for (MenuItem menuItem : menuItems) {
-                mMenuItems.put(MenuItemRepr.of(menuItem), menuItem);
-            }
-            mOnMenuItemClickListener = menuItemClickListener;
-        }
-
-        /**
-         * Returns true if this popup needs a relayout to properly render the specified menu items.
-         */
-        public boolean isLayoutRequired(List<MenuItem> menuItems) {
-            return !MenuItemRepr.reprEquals(menuItems, mMenuItems.values());
-        }
-
-        /**
-         * Shows this popup at the specified coordinates.
-         * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
-         */
-        public void show(Rect contentRectOnScreen) {
-            Objects.requireNonNull(contentRectOnScreen);
-
-            if (isShowing()) {
-                return;
-            }
-
-            mHidden = false;
-            mDismissed = false;
-            cancelDismissAndHideAnimations();
-            cancelOverflowAnimations();
-
-            refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
-            preparePopupContent();
-            // We need to specify the position in window coordinates.
-            // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
-            // specify the popup position in screen coordinates.
-            mPopupWindow.showAtLocation(
-                    mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x, mCoordsOnWindow.y);
-            setTouchableSurfaceInsetsComputer();
-            runShowAnimation();
-        }
-
-        /**
-         * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
-         */
-        public void dismiss() {
-            if (mDismissed) {
-                return;
-            }
-
-            mHidden = false;
-            mDismissed = true;
-            mHideAnimation.cancel();
-
-            runDismissAnimation();
-            setZeroTouchableSurface();
-        }
-
-        /**
-         * Hides this popup. This is a no-op if this popup is not showing.
-         * Use {@link #isHidden()} to distinguish between a hidden and a dismissed popup.
-         */
-        public void hide() {
-            if (!isShowing()) {
-                return;
-            }
-
-            mHidden = true;
-            runHideAnimation();
-            setZeroTouchableSurface();
-        }
-
-        /**
-         * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
-         */
-        public boolean isShowing() {
-            return !mDismissed && !mHidden;
-        }
-
-        /**
-         * Returns {@code true} if this popup is currently hidden. {@code false} otherwise.
-         */
-        public boolean isHidden() {
-            return mHidden;
-        }
-
-        /**
-         * Updates the coordinates of this popup.
-         * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
-         * This is a no-op if this popup is not showing.
-         */
-        public void updateCoordinates(Rect contentRectOnScreen) {
-            Objects.requireNonNull(contentRectOnScreen);
-
-            if (!isShowing() || !mPopupWindow.isShowing()) {
-                return;
-            }
-
-            cancelOverflowAnimations();
-            refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
-            preparePopupContent();
-            // We need to specify the position in window coordinates.
-            // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
-            // specify the popup position in screen coordinates.
-            mPopupWindow.update(
-                    mCoordsOnWindow.x, mCoordsOnWindow.y,
-                    mPopupWindow.getWidth(), mPopupWindow.getHeight());
-        }
-
-        private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
-            refreshViewPort();
-
-            // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
-            // landscape.
-            final int x = Math.min(
-                    contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
-                    mViewPortOnScreen.right - mPopupWindow.getWidth());
-
-            final int y;
-
-            final int availableHeightAboveContent =
-                    contentRectOnScreen.top - mViewPortOnScreen.top;
-            final int availableHeightBelowContent =
-                    mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
-
-            final int margin = 2 * mMarginVertical;
-            final int toolbarHeightWithVerticalMargin = mLineHeight + margin;
-
-            if (!hasOverflow()) {
-                if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin) {
-                    // There is enough space at the top of the content.
-                    y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
-                } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin) {
-                    // There is enough space at the bottom of the content.
-                    y = contentRectOnScreen.bottom;
-                } else if (availableHeightBelowContent >= mLineHeight) {
-                    // Just enough space to fit the toolbar with no vertical margins.
-                    y = contentRectOnScreen.bottom - mMarginVertical;
-                } else {
-                    // Not enough space. Prefer to position as high as possible.
-                    y = Math.max(
-                            mViewPortOnScreen.top,
-                            contentRectOnScreen.top - toolbarHeightWithVerticalMargin);
-                }
-            } else {
-                // Has an overflow.
-                final int minimumOverflowHeightWithMargin =
-                        calculateOverflowHeight(MIN_OVERFLOW_SIZE) + margin;
-                final int availableHeightThroughContentDown = mViewPortOnScreen.bottom -
-                        contentRectOnScreen.top + toolbarHeightWithVerticalMargin;
-                final int availableHeightThroughContentUp = contentRectOnScreen.bottom -
-                        mViewPortOnScreen.top + toolbarHeightWithVerticalMargin;
-
-                if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) {
-                    // There is enough space at the top of the content rect for the overflow.
-                    // Position above and open upwards.
-                    updateOverflowHeight(availableHeightAboveContent - margin);
-                    y = contentRectOnScreen.top - mPopupWindow.getHeight();
-                    mOpenOverflowUpwards = true;
-                } else if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin
-                        && availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
-                    // There is enough space at the top of the content rect for the main panel
-                    // but not the overflow.
-                    // Position above but open downwards.
-                    updateOverflowHeight(availableHeightThroughContentDown - margin);
-                    y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
-                    mOpenOverflowUpwards = false;
-                } else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) {
-                    // There is enough space at the bottom of the content rect for the overflow.
-                    // Position below and open downwards.
-                    updateOverflowHeight(availableHeightBelowContent - margin);
-                    y = contentRectOnScreen.bottom;
-                    mOpenOverflowUpwards = false;
-                } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin
-                        && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) {
-                    // There is enough space at the bottom of the content rect for the main panel
-                    // but not the overflow.
-                    // Position below but open upwards.
-                    updateOverflowHeight(availableHeightThroughContentUp - margin);
-                    y = contentRectOnScreen.bottom + toolbarHeightWithVerticalMargin -
-                            mPopupWindow.getHeight();
-                    mOpenOverflowUpwards = true;
-                } else {
-                    // Not enough space.
-                    // Position at the top of the view port and open downwards.
-                    updateOverflowHeight(mViewPortOnScreen.height() - margin);
-                    y = mViewPortOnScreen.top;
-                    mOpenOverflowUpwards = false;
-                }
-            }
-
-            // We later specify the location of PopupWindow relative to the attached window.
-            // The idea here is that 1) we can get the location of a View in both window coordinates
-            // and screen coordiantes, where the offset between them should be equal to the window
-            // origin, and 2) we can use an arbitrary for this calculation while calculating the
-            // location of the rootview is supposed to be least expensive.
-            // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can avoid
-            // the following calculation.
-            mParent.getRootView().getLocationOnScreen(mTmpCoords);
-            int rootViewLeftOnScreen = mTmpCoords[0];
-            int rootViewTopOnScreen = mTmpCoords[1];
-            mParent.getRootView().getLocationInWindow(mTmpCoords);
-            int rootViewLeftOnWindow = mTmpCoords[0];
-            int rootViewTopOnWindow = mTmpCoords[1];
-            int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
-            int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
-            mCoordsOnWindow.set(
-                    Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
-        }
-
-        /**
-         * Performs the "show" animation on the floating popup.
-         */
-        private void runShowAnimation() {
-            mShowAnimation.start();
-        }
-
-        /**
-         * Performs the "dismiss" animation on the floating popup.
-         */
-        private void runDismissAnimation() {
-            mDismissAnimation.start();
-        }
-
-        /**
-         * Performs the "hide" animation on the floating popup.
-         */
-        private void runHideAnimation() {
-            mHideAnimation.start();
-        }
-
-        private void cancelDismissAndHideAnimations() {
-            mDismissAnimation.cancel();
-            mHideAnimation.cancel();
-        }
-
-        private void cancelOverflowAnimations() {
-            mContentContainer.clearAnimation();
-            mMainPanel.animate().cancel();
-            mOverflowPanel.animate().cancel();
-            mToArrow.stop();
-            mToOverflow.stop();
-        }
-
-        private void openOverflow() {
-            final int targetWidth = mOverflowPanelSize.getWidth();
-            final int targetHeight = mOverflowPanelSize.getHeight();
-            final int startWidth = mContentContainer.getWidth();
-            final int startHeight = mContentContainer.getHeight();
-            final float startY = mContentContainer.getY();
-            final float left = mContentContainer.getX();
-            final float right = left + mContentContainer.getWidth();
-            Animation widthAnimation = new Animation() {
-                @Override
-                protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
-                    setWidth(mContentContainer, startWidth + deltaWidth);
-                    if (isInRTLMode()) {
-                        mContentContainer.setX(left);
-
-                        // Lock the panels in place.
-                        mMainPanel.setX(0);
-                        mOverflowPanel.setX(0);
-                    } else {
-                        mContentContainer.setX(right - mContentContainer.getWidth());
-
-                        // Offset the panels' positions so they look like they're locked in place
-                        // on the screen.
-                        mMainPanel.setX(mContentContainer.getWidth() - startWidth);
-                        mOverflowPanel.setX(mContentContainer.getWidth() - targetWidth);
-                    }
-                }
-            };
-            Animation heightAnimation = new Animation() {
-                @Override
-                protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
-                    setHeight(mContentContainer, startHeight + deltaHeight);
-                    if (mOpenOverflowUpwards) {
-                        mContentContainer.setY(
-                                startY - (mContentContainer.getHeight() - startHeight));
-                        positionContentYCoordinatesIfOpeningOverflowUpwards();
-                    }
-                }
-            };
-            final float overflowButtonStartX = mOverflowButton.getX();
-            final float overflowButtonTargetX = isInRTLMode() ?
-                    overflowButtonStartX + targetWidth - mOverflowButton.getWidth() :
-                    overflowButtonStartX - targetWidth + mOverflowButton.getWidth();
-            Animation overflowButtonAnimation = new Animation() {
-                @Override
-                protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    float overflowButtonX = overflowButtonStartX
-                            + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
-                    float deltaContainerWidth = isInRTLMode() ?
-                            0 :
-                            mContentContainer.getWidth() - startWidth;
-                    float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
-                    mOverflowButton.setX(actualOverflowButtonX);
-                }
-            };
-            widthAnimation.setInterpolator(mLogAccelerateInterpolator);
-            widthAnimation.setDuration(getAdjustedDuration(250));
-            heightAnimation.setInterpolator(mFastOutSlowInInterpolator);
-            heightAnimation.setDuration(getAdjustedDuration(250));
-            overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
-            overflowButtonAnimation.setDuration(getAdjustedDuration(250));
-            mOpenOverflowAnimation.getAnimations().clear();
-            mOpenOverflowAnimation.getAnimations().clear();
-            mOpenOverflowAnimation.addAnimation(widthAnimation);
-            mOpenOverflowAnimation.addAnimation(heightAnimation);
-            mOpenOverflowAnimation.addAnimation(overflowButtonAnimation);
-            mContentContainer.startAnimation(mOpenOverflowAnimation);
-            mIsOverflowOpen = true;
-            mMainPanel.animate()
-                    .alpha(0).withLayer()
-                    .setInterpolator(mLinearOutSlowInInterpolator)
-                    .setDuration(250)
-                    .start();
-            mOverflowPanel.setAlpha(1); // fadeIn in 0ms.
-        }
-
-        private void closeOverflow() {
-            final int targetWidth = mMainPanelSize.getWidth();
-            final int startWidth = mContentContainer.getWidth();
-            final float left = mContentContainer.getX();
-            final float right = left + mContentContainer.getWidth();
-            Animation widthAnimation = new Animation() {
-                @Override
-                protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
-                    setWidth(mContentContainer, startWidth + deltaWidth);
-                    if (isInRTLMode()) {
-                        mContentContainer.setX(left);
-
-                        // Lock the panels in place.
-                        mMainPanel.setX(0);
-                        mOverflowPanel.setX(0);
-                    } else {
-                        mContentContainer.setX(right - mContentContainer.getWidth());
-
-                        // Offset the panels' positions so they look like they're locked in place
-                        // on the screen.
-                        mMainPanel.setX(mContentContainer.getWidth() - targetWidth);
-                        mOverflowPanel.setX(mContentContainer.getWidth() - startWidth);
-                    }
-                }
-            };
-            final int targetHeight = mMainPanelSize.getHeight();
-            final int startHeight = mContentContainer.getHeight();
-            final float bottom = mContentContainer.getY() + mContentContainer.getHeight();
-            Animation heightAnimation = new Animation() {
-                @Override
-                protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
-                    setHeight(mContentContainer, startHeight + deltaHeight);
-                    if (mOpenOverflowUpwards) {
-                        mContentContainer.setY(bottom - mContentContainer.getHeight());
-                        positionContentYCoordinatesIfOpeningOverflowUpwards();
-                    }
-                }
-            };
-            final float overflowButtonStartX = mOverflowButton.getX();
-            final float overflowButtonTargetX = isInRTLMode() ?
-                    overflowButtonStartX - startWidth + mOverflowButton.getWidth() :
-                    overflowButtonStartX + startWidth - mOverflowButton.getWidth();
-            Animation overflowButtonAnimation = new Animation() {
-                @Override
-                protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    float overflowButtonX = overflowButtonStartX
-                            + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
-                    float deltaContainerWidth = isInRTLMode() ?
-                            0 :
-                            mContentContainer.getWidth() - startWidth;
-                    float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
-                    mOverflowButton.setX(actualOverflowButtonX);
-                }
-            };
-            widthAnimation.setInterpolator(mFastOutSlowInInterpolator);
-            widthAnimation.setDuration(getAdjustedDuration(250));
-            heightAnimation.setInterpolator(mLogAccelerateInterpolator);
-            heightAnimation.setDuration(getAdjustedDuration(250));
-            overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
-            overflowButtonAnimation.setDuration(getAdjustedDuration(250));
-            mCloseOverflowAnimation.getAnimations().clear();
-            mCloseOverflowAnimation.addAnimation(widthAnimation);
-            mCloseOverflowAnimation.addAnimation(heightAnimation);
-            mCloseOverflowAnimation.addAnimation(overflowButtonAnimation);
-            mContentContainer.startAnimation(mCloseOverflowAnimation);
-            mIsOverflowOpen = false;
-            mMainPanel.animate()
-                    .alpha(1).withLayer()
-                    .setInterpolator(mFastOutLinearInInterpolator)
-                    .setDuration(100)
-                    .start();
-            mOverflowPanel.animate()
-                    .alpha(0).withLayer()
-                    .setInterpolator(mLinearOutSlowInInterpolator)
-                    .setDuration(150)
-                    .start();
-        }
-
-        /**
-         * Defines the position of the floating toolbar popup panels when transition animation has
-         * stopped.
-         */
-        private void setPanelsStatesAtRestingPosition() {
-            mOverflowButton.setEnabled(true);
-            mOverflowPanel.awakenScrollBars();
-
-            if (mIsOverflowOpen) {
-                // Set open state.
-                final Size containerSize = mOverflowPanelSize;
-                setSize(mContentContainer, containerSize);
-                mMainPanel.setAlpha(0);
-                mMainPanel.setVisibility(View.INVISIBLE);
-                mOverflowPanel.setAlpha(1);
-                mOverflowPanel.setVisibility(View.VISIBLE);
-                mOverflowButton.setImageDrawable(mArrow);
-                mOverflowButton.setContentDescription(mContext.getString(
-                        R.string.floating_toolbar_close_overflow_description));
-
-                // Update x-coordinates depending on RTL state.
-                if (isInRTLMode()) {
-                    mContentContainer.setX(mMarginHorizontal);  // align left
-                    mMainPanel.setX(0);  // align left
-                    mOverflowButton.setX(  // align right
-                            containerSize.getWidth() - mOverflowButtonSize.getWidth());
-                    mOverflowPanel.setX(0);  // align left
-                } else {
-                    mContentContainer.setX(  // align right
-                            mPopupWindow.getWidth() -
-                                    containerSize.getWidth() - mMarginHorizontal);
-                    mMainPanel.setX(-mContentContainer.getX());  // align right
-                    mOverflowButton.setX(0);  // align left
-                    mOverflowPanel.setX(0);  // align left
-                }
-
-                // Update y-coordinates depending on overflow's open direction.
-                if (mOpenOverflowUpwards) {
-                    mContentContainer.setY(mMarginVertical);  // align top
-                    mMainPanel.setY(  // align bottom
-                            containerSize.getHeight() - mContentContainer.getHeight());
-                    mOverflowButton.setY(  // align bottom
-                            containerSize.getHeight() - mOverflowButtonSize.getHeight());
-                    mOverflowPanel.setY(0);  // align top
-                } else {
-                    // opens downwards.
-                    mContentContainer.setY(mMarginVertical);  // align top
-                    mMainPanel.setY(0);  // align top
-                    mOverflowButton.setY(0);  // align top
-                    mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
-                }
-            } else {
-                // Overflow not open. Set closed state.
-                final Size containerSize = mMainPanelSize;
-                setSize(mContentContainer, containerSize);
-                mMainPanel.setAlpha(1);
-                mMainPanel.setVisibility(View.VISIBLE);
-                mOverflowPanel.setAlpha(0);
-                mOverflowPanel.setVisibility(View.INVISIBLE);
-                mOverflowButton.setImageDrawable(mOverflow);
-                mOverflowButton.setContentDescription(mContext.getString(
-                        R.string.floating_toolbar_open_overflow_description));
-
-                if (hasOverflow()) {
-                    // Update x-coordinates depending on RTL state.
-                    if (isInRTLMode()) {
-                        mContentContainer.setX(mMarginHorizontal);  // align left
-                        mMainPanel.setX(0);  // align left
-                        mOverflowButton.setX(0);  // align left
-                        mOverflowPanel.setX(0);  // align left
-                    } else {
-                        mContentContainer.setX(  // align right
-                                mPopupWindow.getWidth() -
-                                        containerSize.getWidth() - mMarginHorizontal);
-                        mMainPanel.setX(0);  // align left
-                        mOverflowButton.setX(  // align right
-                                containerSize.getWidth() - mOverflowButtonSize.getWidth());
-                        mOverflowPanel.setX(  // align right
-                                containerSize.getWidth() - mOverflowPanelSize.getWidth());
-                    }
-
-                    // Update y-coordinates depending on overflow's open direction.
-                    if (mOpenOverflowUpwards) {
-                        mContentContainer.setY(  // align bottom
-                                mMarginVertical +
-                                        mOverflowPanelSize.getHeight() - containerSize.getHeight());
-                        mMainPanel.setY(0);  // align top
-                        mOverflowButton.setY(0);  // align top
-                        mOverflowPanel.setY(  // align bottom
-                                containerSize.getHeight() - mOverflowPanelSize.getHeight());
-                    } else {
-                        // opens downwards.
-                        mContentContainer.setY(mMarginVertical);  // align top
-                        mMainPanel.setY(0);  // align top
-                        mOverflowButton.setY(0);  // align top
-                        mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
-                    }
-                } else {
-                    // No overflow.
-                    mContentContainer.setX(mMarginHorizontal);  // align left
-                    mContentContainer.setY(mMarginVertical);  // align top
-                    mMainPanel.setX(0);  // align left
-                    mMainPanel.setY(0);  // align top
-                }
-            }
-        }
-
-        private void updateOverflowHeight(int suggestedHeight) {
-            if (hasOverflow()) {
-                final int maxItemSize = (suggestedHeight - mOverflowButtonSize.getHeight()) /
-                        mLineHeight;
-                final int newHeight = calculateOverflowHeight(maxItemSize);
-                if (mOverflowPanelSize.getHeight() != newHeight) {
-                    mOverflowPanelSize = new Size(mOverflowPanelSize.getWidth(), newHeight);
-                }
-                setSize(mOverflowPanel, mOverflowPanelSize);
-                if (mIsOverflowOpen) {
-                    setSize(mContentContainer, mOverflowPanelSize);
-                    if (mOpenOverflowUpwards) {
-                        final int deltaHeight = mOverflowPanelSize.getHeight() - newHeight;
-                        mContentContainer.setY(mContentContainer.getY() + deltaHeight);
-                        mOverflowButton.setY(mOverflowButton.getY() - deltaHeight);
-                    }
-                } else {
-                    setSize(mContentContainer, mMainPanelSize);
-                }
-                updatePopupSize();
-            }
-        }
-
-        private void updatePopupSize() {
-            int width = 0;
-            int height = 0;
-            if (mMainPanelSize != null) {
-                width = Math.max(width, mMainPanelSize.getWidth());
-                height = Math.max(height, mMainPanelSize.getHeight());
-            }
-            if (mOverflowPanelSize != null) {
-                width = Math.max(width, mOverflowPanelSize.getWidth());
-                height = Math.max(height, mOverflowPanelSize.getHeight());
-            }
-            mPopupWindow.setWidth(width + mMarginHorizontal * 2);
-            mPopupWindow.setHeight(height + mMarginVertical * 2);
-            maybeComputeTransitionDurationScale();
-        }
-
-        private void refreshViewPort() {
-            mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen);
-        }
-
-        private int getAdjustedToolbarWidth(int suggestedWidth) {
-            int width = suggestedWidth;
-            refreshViewPort();
-            int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
-            if (width <= 0) {
-                width = mParent.getResources()
-                        .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
-            }
-            return Math.min(width, maximumWidth);
-        }
-
-        /**
-         * Sets the touchable region of this popup to be zero. This means that all touch events on
-         * this popup will go through to the surface behind it.
-         */
-        private void setZeroTouchableSurface() {
-            mTouchableRegion.setEmpty();
-        }
-
-        /**
-         * Sets the touchable region of this popup to be the area occupied by its content.
-         */
-        private void setContentAreaAsTouchableSurface() {
-            Objects.requireNonNull(mMainPanelSize);
-            final int width;
-            final int height;
-            if (mIsOverflowOpen) {
-                Objects.requireNonNull(mOverflowPanelSize);
-                width = mOverflowPanelSize.getWidth();
-                height = mOverflowPanelSize.getHeight();
-            } else {
-                width = mMainPanelSize.getWidth();
-                height = mMainPanelSize.getHeight();
-            }
-            mTouchableRegion.set(
-                    (int) mContentContainer.getX(),
-                    (int) mContentContainer.getY(),
-                    (int) mContentContainer.getX() + width,
-                    (int) mContentContainer.getY() + height);
-        }
-
-        /**
-         * Make the touchable area of this popup be the area specified by mTouchableRegion.
-         * This should be called after the popup window has been dismissed (dismiss/hide)
-         * and is probably being re-shown with a new content root view.
-         */
-        private void setTouchableSurfaceInsetsComputer() {
-            ViewTreeObserver viewTreeObserver = mPopupWindow.getContentView()
-                    .getRootView()
-                    .getViewTreeObserver();
-            viewTreeObserver.removeOnComputeInternalInsetsListener(mInsetsComputer);
-            viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer);
-        }
-
-        private boolean isInRTLMode() {
-            return mContext.getApplicationInfo().hasRtlSupport()
-                    && mContext.getResources().getConfiguration().getLayoutDirection()
-                            == View.LAYOUT_DIRECTION_RTL;
-        }
-
-        private boolean hasOverflow() {
-            return mOverflowPanelSize != null;
-        }
-
-        /**
-         * Fits as many menu items in the main panel and returns a list of the menu items that
-         * were not fit in.
-         *
-         * @return The menu items that are not included in this main panel.
-         */
-        public List<MenuItem> layoutMainPanelItems(
-                List<MenuItem> menuItems, final int toolbarWidth) {
-            Objects.requireNonNull(menuItems);
-
-            int availableWidth = toolbarWidth;
-
-            final LinkedList<MenuItem> remainingMenuItems = new LinkedList<>();
-            // add the overflow menu items to the end of the remainingMenuItems list.
-            final LinkedList<MenuItem> overflowMenuItems = new LinkedList();
-            for (MenuItem menuItem : menuItems) {
-                if (menuItem.getItemId() != android.R.id.textAssist
-                        && menuItem.requiresOverflow()) {
-                    overflowMenuItems.add(menuItem);
-                } else {
-                    remainingMenuItems.add(menuItem);
-                }
-            }
-            remainingMenuItems.addAll(overflowMenuItems);
-
-            mMainPanel.removeAllViews();
-            mMainPanel.setPaddingRelative(0, 0, 0, 0);
-
-            int lastGroupId = -1;
-            boolean isFirstItem = true;
-            while (!remainingMenuItems.isEmpty()) {
-                final MenuItem menuItem = remainingMenuItems.peek();
-
-                // if this is the first item, regardless of requiresOverflow(), it should be
-                // displayed on the main panel. Otherwise all items including this one will be
-                // overflow items, and should be displayed in overflow panel.
-                if(!isFirstItem && menuItem.requiresOverflow()) {
-                    break;
-                }
-
-                final boolean showIcon = isFirstItem && menuItem.getItemId() == R.id.textAssist;
-                final View menuItemButton = createMenuItemButton(
-                        mContext, menuItem, mIconTextSpacing, showIcon);
-                if (!showIcon && menuItemButton instanceof LinearLayout) {
-                    ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
-                }
-
-                // Adding additional start padding for the first button to even out button spacing.
-                if (isFirstItem) {
-                    menuItemButton.setPaddingRelative(
-                            (int) (1.5 * menuItemButton.getPaddingStart()),
-                            menuItemButton.getPaddingTop(),
-                            menuItemButton.getPaddingEnd(),
-                            menuItemButton.getPaddingBottom());
-                }
-
-                // Adding additional end padding for the last button to even out button spacing.
-                boolean isLastItem = remainingMenuItems.size() == 1;
-                if (isLastItem) {
-                    menuItemButton.setPaddingRelative(
-                            menuItemButton.getPaddingStart(),
-                            menuItemButton.getPaddingTop(),
-                            (int) (1.5 * menuItemButton.getPaddingEnd()),
-                            menuItemButton.getPaddingBottom());
-                }
-
-                menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-                final int menuItemButtonWidth = Math.min(
-                        menuItemButton.getMeasuredWidth(), toolbarWidth);
-
-                // Check if we can fit an item while reserving space for the overflowButton.
-                final boolean canFitWithOverflow =
-                        menuItemButtonWidth <=
-                                availableWidth - mOverflowButtonSize.getWidth();
-                final boolean canFitNoOverflow =
-                        isLastItem && menuItemButtonWidth <= availableWidth;
-                if (canFitWithOverflow || canFitNoOverflow) {
-                    setButtonTagAndClickListener(menuItemButton, menuItem);
-                    // Set tooltips for main panel items, but not overflow items (b/35726766).
-                    menuItemButton.setTooltipText(menuItem.getTooltipText());
-                    mMainPanel.addView(menuItemButton);
-                    final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
-                    params.width = menuItemButtonWidth;
-                    menuItemButton.setLayoutParams(params);
-                    availableWidth -= menuItemButtonWidth;
-                    remainingMenuItems.pop();
-                } else {
-                    break;
-                }
-                lastGroupId = menuItem.getGroupId();
-                isFirstItem = false;
-            }
-
-            if (!remainingMenuItems.isEmpty()) {
-                // Reserve space for overflowButton.
-                mMainPanel.setPaddingRelative(0, 0, mOverflowButtonSize.getWidth(), 0);
-            }
-
-            mMainPanelSize = measure(mMainPanel);
-            return remainingMenuItems;
-        }
-
-        private void layoutOverflowPanelItems(List<MenuItem> menuItems) {
-            ArrayAdapter<MenuItem> overflowPanelAdapter =
-                    (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
-            overflowPanelAdapter.clear();
-            final int size = menuItems.size();
-            for (int i = 0; i < size; i++) {
-                overflowPanelAdapter.add(menuItems.get(i));
-            }
-            mOverflowPanel.setAdapter(overflowPanelAdapter);
-            if (mOpenOverflowUpwards) {
-                mOverflowPanel.setY(0);
-            } else {
-                mOverflowPanel.setY(mOverflowButtonSize.getHeight());
-            }
-
-            int width = Math.max(getOverflowWidth(), mOverflowButtonSize.getWidth());
-            int height = calculateOverflowHeight(MAX_OVERFLOW_SIZE);
-            mOverflowPanelSize = new Size(width, height);
-            setSize(mOverflowPanel, mOverflowPanelSize);
-        }
-
-        /**
-         * Resets the content container and appropriately position it's panels.
-         */
-        private void preparePopupContent() {
-            mContentContainer.removeAllViews();
-
-            // Add views in the specified order so they stack up as expected.
-            // Order: overflowPanel, mainPanel, overflowButton.
-            if (hasOverflow()) {
-                mContentContainer.addView(mOverflowPanel);
-            }
-            mContentContainer.addView(mMainPanel);
-            if (hasOverflow()) {
-                mContentContainer.addView(mOverflowButton);
-            }
-            setPanelsStatesAtRestingPosition();
-            setContentAreaAsTouchableSurface();
-
-            // The positioning of contents in RTL is wrong when the view is first rendered.
-            // Hide the view and post a runnable to recalculate positions and render the view.
-            // TODO: Investigate why this happens and fix.
-            if (isInRTLMode()) {
-                mContentContainer.setAlpha(0);
-                mContentContainer.post(mPreparePopupContentRTLHelper);
-            }
-        }
-
-        /**
-         * Clears out the panels and their container. Resets their calculated sizes.
-         */
-        private void clearPanels() {
-            mOverflowPanelSize = null;
-            mMainPanelSize = null;
-            mIsOverflowOpen = false;
-            mMainPanel.removeAllViews();
-            ArrayAdapter<MenuItem> overflowPanelAdapter =
-                    (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
-            overflowPanelAdapter.clear();
-            mOverflowPanel.setAdapter(overflowPanelAdapter);
-            mContentContainer.removeAllViews();
-        }
-
-        private void positionContentYCoordinatesIfOpeningOverflowUpwards() {
-            if (mOpenOverflowUpwards) {
-                mMainPanel.setY(mContentContainer.getHeight() - mMainPanelSize.getHeight());
-                mOverflowButton.setY(mContentContainer.getHeight() - mOverflowButton.getHeight());
-                mOverflowPanel.setY(mContentContainer.getHeight() - mOverflowPanelSize.getHeight());
-            }
-        }
-
-        private int getOverflowWidth() {
-            int overflowWidth = 0;
-            final int count = mOverflowPanel.getAdapter().getCount();
-            for (int i = 0; i < count; i++) {
-                MenuItem menuItem = (MenuItem) mOverflowPanel.getAdapter().getItem(i);
-                overflowWidth =
-                        Math.max(mOverflowPanelViewHelper.calculateWidth(menuItem), overflowWidth);
-            }
-            return overflowWidth;
-        }
-
-        private int calculateOverflowHeight(int maxItemSize) {
-            // Maximum of 4 items, minimum of 2 if the overflow has to scroll.
-            int actualSize = Math.min(
-                    MAX_OVERFLOW_SIZE,
-                    Math.min(
-                            Math.max(MIN_OVERFLOW_SIZE, maxItemSize),
-                            mOverflowPanel.getCount()));
-            int extension = 0;
-            if (actualSize < mOverflowPanel.getCount()) {
-                // The overflow will require scrolling to get to all the items.
-                // Extend the height so that part of the hidden items is displayed.
-                extension = (int) (mLineHeight * 0.5f);
-            }
-            return actualSize * mLineHeight
-                    + mOverflowButtonSize.getHeight()
-                    + extension;
-        }
-
-        private void setButtonTagAndClickListener(View menuItemButton, MenuItem menuItem) {
-            menuItemButton.setTag(MenuItemRepr.of(menuItem));
-            menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
-        }
-
-        /**
-         * NOTE: Use only in android.view.animation.* animations. Do not use in android.animation.*
-         * animations. See comment about this in the code.
-         */
-        private int getAdjustedDuration(int originalDuration) {
-            if (mTransitionDurationScale < 150) {
-                // For smaller transition, decrease the time.
-                return Math.max(originalDuration - 50, 0);
-            } else if (mTransitionDurationScale > 300) {
-                // For bigger transition, increase the time.
-                return originalDuration + 50;
-            }
-
-            // Scale the animation duration with getDurationScale(). This allows
-            // android.view.animation.* animations to scale just like android.animation.* animations
-            // when  animator duration scale is adjusted in "Developer Options".
-            // For this reason, do not use this method for android.animation.* animations.
-            return (int) (originalDuration * ValueAnimator.getDurationScale());
-        }
-
-        private void maybeComputeTransitionDurationScale() {
-            if (mMainPanelSize != null && mOverflowPanelSize != null) {
-                int w = mMainPanelSize.getWidth() - mOverflowPanelSize.getWidth();
-                int h = mOverflowPanelSize.getHeight() - mMainPanelSize.getHeight();
-                mTransitionDurationScale = (int) (Math.sqrt(w * w + h * h) /
-                        mContentContainer.getContext().getResources().getDisplayMetrics().density);
-            }
-        }
-
-        private ViewGroup createMainPanel() {
-            ViewGroup mainPanel = new LinearLayout(mContext) {
-                @Override
-                protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-                    if (isOverflowAnimating()) {
-                        // Update widthMeasureSpec to make sure that this view is not clipped
-                        // as we offset it's coordinates with respect to it's parent.
-                        widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                                mMainPanelSize.getWidth(),
-                                MeasureSpec.EXACTLY);
-                    }
-                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-                }
-
-                @Override
-                public boolean onInterceptTouchEvent(MotionEvent ev) {
-                    // Intercept the touch event while the overflow is animating.
-                    return isOverflowAnimating();
-                }
-            };
-            return mainPanel;
-        }
-
-        private ImageButton createOverflowButton() {
-            final ImageButton overflowButton = (ImageButton) LayoutInflater.from(mContext)
-                    .inflate(R.layout.floating_popup_overflow_button, null);
-            overflowButton.setImageDrawable(mOverflow);
-            overflowButton.setOnClickListener(v -> {
-                if (mIsOverflowOpen) {
-                    overflowButton.setImageDrawable(mToOverflow);
-                    mToOverflow.start();
-                    closeOverflow();
-                } else {
-                    overflowButton.setImageDrawable(mToArrow);
-                    mToArrow.start();
-                    openOverflow();
-                }
-            });
-            return overflowButton;
-        }
-
-        private OverflowPanel createOverflowPanel() {
-            final OverflowPanel overflowPanel = new OverflowPanel(this);
-            overflowPanel.setLayoutParams(new ViewGroup.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-            overflowPanel.setDivider(null);
-            overflowPanel.setDividerHeight(0);
-
-            final ArrayAdapter adapter =
-                    new ArrayAdapter<MenuItem>(mContext, 0) {
-                        @Override
-                        public View getView(int position, View convertView, ViewGroup parent) {
-                            return mOverflowPanelViewHelper.getView(
-                                    getItem(position), mOverflowPanelSize.getWidth(), convertView);
-                        }
-                    };
-            overflowPanel.setAdapter(adapter);
-
-            overflowPanel.setOnItemClickListener((parent, view, position, id) -> {
-                MenuItem menuItem = (MenuItem) overflowPanel.getAdapter().getItem(position);
-                if (mOnMenuItemClickListener != null) {
-                    mOnMenuItemClickListener.onMenuItemClick(menuItem);
-                }
-            });
-
-            return overflowPanel;
-        }
-
-        private boolean isOverflowAnimating() {
-            final boolean overflowOpening = mOpenOverflowAnimation.hasStarted()
-                    && !mOpenOverflowAnimation.hasEnded();
-            final boolean overflowClosing = mCloseOverflowAnimation.hasStarted()
-                    && !mCloseOverflowAnimation.hasEnded();
-            return overflowOpening || overflowClosing;
-        }
-
-        private Animation.AnimationListener createOverflowAnimationListener() {
-            Animation.AnimationListener listener = new Animation.AnimationListener() {
-                @Override
-                public void onAnimationStart(Animation animation) {
-                    // Disable the overflow button while it's animating.
-                    // It will be re-enabled when the animation stops.
-                    mOverflowButton.setEnabled(false);
-                    // Ensure both panels have visibility turned on when the overflow animation
-                    // starts.
-                    mMainPanel.setVisibility(View.VISIBLE);
-                    mOverflowPanel.setVisibility(View.VISIBLE);
-                }
-
-                @Override
-                public void onAnimationEnd(Animation animation) {
-                    // Posting this because it seems like this is called before the animation
-                    // actually ends.
-                    mContentContainer.post(() -> {
-                        setPanelsStatesAtRestingPosition();
-                        setContentAreaAsTouchableSurface();
-                    });
-                }
-
-                @Override
-                public void onAnimationRepeat(Animation animation) {
-                }
-            };
-            return listener;
-        }
-
-        private static Size measure(View view) {
-            Preconditions.checkState(view.getParent() == null);
-            view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-            return new Size(view.getMeasuredWidth(), view.getMeasuredHeight());
-        }
-
-        private static void setSize(View view, int width, int height) {
-            view.setMinimumWidth(width);
-            view.setMinimumHeight(height);
-            ViewGroup.LayoutParams params = view.getLayoutParams();
-            params = (params == null) ? new ViewGroup.LayoutParams(0, 0) : params;
-            params.width = width;
-            params.height = height;
-            view.setLayoutParams(params);
-        }
-
-        private static void setSize(View view, Size size) {
-            setSize(view, size.getWidth(), size.getHeight());
-        }
-
-        private static void setWidth(View view, int width) {
-            ViewGroup.LayoutParams params = view.getLayoutParams();
-            setSize(view, width, params.height);
-        }
-
-        private static void setHeight(View view, int height) {
-            ViewGroup.LayoutParams params = view.getLayoutParams();
-            setSize(view, params.width, height);
-        }
-
-        /**
-         * A custom ListView for the overflow panel.
-         */
-        private static final class OverflowPanel extends ListView {
-
-            private final FloatingToolbarPopup mPopup;
-
-            OverflowPanel(FloatingToolbarPopup popup) {
-                super(Objects.requireNonNull(popup).mContext);
-                this.mPopup = popup;
-                setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
-                setScrollIndicators(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
-            }
-
-            @Override
-            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-                // Update heightMeasureSpec to make sure that this view is not clipped
-                // as we offset it's coordinates with respect to it's parent.
-                int height = mPopup.mOverflowPanelSize.getHeight()
-                        - mPopup.mOverflowButtonSize.getHeight();
-                heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
-                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            }
-
-            @Override
-            public boolean dispatchTouchEvent(MotionEvent ev) {
-                if (mPopup.isOverflowAnimating()) {
-                    // Eat the touch event.
-                    return true;
-                }
-                return super.dispatchTouchEvent(ev);
-            }
-
-            @Override
-            protected boolean awakenScrollBars() {
-                return super.awakenScrollBars();
-            }
-        }
-
-        /**
-         * A custom interpolator used for various floating toolbar animations.
-         */
-        private static final class LogAccelerateInterpolator implements Interpolator {
-
-            private static final int BASE = 100;
-            private static final float LOGS_SCALE = 1f / computeLog(1, BASE);
-
-            private static float computeLog(float t, int base) {
-                return (float) (1 - Math.pow(base, -t));
-            }
-
-            @Override
-            public float getInterpolation(float t) {
-                return 1 - computeLog(1 - t, BASE) * LOGS_SCALE;
-            }
-        }
-
-        /**
-         * A helper for generating views for the overflow panel.
-         */
-        private static final class OverflowPanelViewHelper {
-
-            private final View mCalculator;
-            private final int mIconTextSpacing;
-            private final int mSidePadding;
-
-            private final Context mContext;
-
-            public OverflowPanelViewHelper(Context context, int iconTextSpacing) {
-                mContext = Objects.requireNonNull(context);
-                mIconTextSpacing = iconTextSpacing;
-                mSidePadding = context.getResources()
-                        .getDimensionPixelSize(R.dimen.floating_toolbar_overflow_side_padding);
-                mCalculator = createMenuButton(null);
-            }
-
-            public View getView(MenuItem menuItem, int minimumWidth, View convertView) {
-                Objects.requireNonNull(menuItem);
-                if (convertView != null) {
-                    updateMenuItemButton(
-                            convertView, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
-                } else {
-                    convertView = createMenuButton(menuItem);
-                }
-                convertView.setMinimumWidth(minimumWidth);
-                return convertView;
-            }
-
-            public int calculateWidth(MenuItem menuItem) {
-                updateMenuItemButton(
-                        mCalculator, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
-                mCalculator.measure(
-                        View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-                return mCalculator.getMeasuredWidth();
-            }
-
-            private View createMenuButton(MenuItem menuItem) {
-                View button = createMenuItemButton(
-                        mContext, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
-                button.setPadding(mSidePadding, 0, mSidePadding, 0);
-                return button;
-            }
-
-            private boolean shouldShowIcon(MenuItem menuItem) {
-                if (menuItem != null) {
-                    return menuItem.getGroupId() == android.R.id.textAssist;
-                }
-                return false;
-            }
-        }
-    }
-
-    /**
-     * Represents the identity of a MenuItem that is rendered in a FloatingToolbarPopup.
-     */
-    @VisibleForTesting
-    public static final class MenuItemRepr {
-
-        public final int itemId;
-        public final int groupId;
-        @Nullable public final String title;
-        @Nullable private final Drawable mIcon;
-
-        private MenuItemRepr(
-                int itemId, int groupId, @Nullable CharSequence title, @Nullable Drawable icon) {
-            this.itemId = itemId;
-            this.groupId = groupId;
-            this.title = (title == null) ? null : title.toString();
-            mIcon = icon;
-        }
-
-        /**
-         * Creates an instance of MenuItemRepr for the specified menu item.
-         */
-        public static MenuItemRepr of(MenuItem menuItem) {
-            return new MenuItemRepr(
-                    menuItem.getItemId(),
-                    menuItem.getGroupId(),
-                    menuItem.getTitle(),
-                    menuItem.getIcon());
-        }
-
-        /**
-         * Returns this object's hashcode.
-         */
-        @Override
-        public int hashCode() {
-            return Objects.hash(itemId, groupId, title, mIcon);
-        }
-
-        /**
-         * Returns true if this object is the same as the specified object.
-         */
-        @Override
-        public boolean equals(Object o) {
-            if (o == this) {
-                return true;
-            }
-            if (!(o instanceof MenuItemRepr)) {
-                return false;
-            }
-            final MenuItemRepr other = (MenuItemRepr) o;
-            return itemId == other.itemId
-                    && groupId == other.groupId
-                    && TextUtils.equals(title, other.title)
-                    // Many Drawables (icons) do not implement equals(). Using equals() here instead
-                    // of reference comparisons in case a Drawable subclass implements equals().
-                    && Objects.equals(mIcon, other.mIcon);
-        }
-
-        /**
-         * Returns true if the two menu item collections are the same based on MenuItemRepr.
-         */
-        public static boolean reprEquals(
-                Collection<MenuItem> menuItems1, Collection<MenuItem> menuItems2) {
-            if (menuItems1.size() != menuItems2.size()) {
-                return false;
-            }
-
-            final Iterator<MenuItem> menuItems2Iter = menuItems2.iterator();
-            for (MenuItem menuItem1 : menuItems1) {
-                final MenuItem menuItem2 = menuItems2Iter.next();
-                if (!MenuItemRepr.of(menuItem1).equals(MenuItemRepr.of(menuItem2))) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-    }
-
-    /**
-     * Creates and returns a menu button for the specified menu item.
-     */
-    private static View createMenuItemButton(
-            Context context, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
-        final View menuItemButton = LayoutInflater.from(context)
-                .inflate(R.layout.floating_popup_menu_button, null);
-        if (menuItem != null) {
-            updateMenuItemButton(menuItemButton, menuItem, iconTextSpacing, showIcon);
-        }
-        return menuItemButton;
-    }
-
-    /**
-     * Updates the specified menu item button with the specified menu item data.
-     */
-    private static void updateMenuItemButton(
-            View menuItemButton, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
-        final TextView buttonText = menuItemButton.findViewById(
-                R.id.floating_toolbar_menu_item_text);
-        buttonText.setEllipsize(null);
-        if (TextUtils.isEmpty(menuItem.getTitle())) {
-            buttonText.setVisibility(View.GONE);
-        } else {
-            buttonText.setVisibility(View.VISIBLE);
-            buttonText.setText(menuItem.getTitle());
-        }
-        final ImageView buttonIcon = menuItemButton.findViewById(
-                R.id.floating_toolbar_menu_item_image);
-        if (menuItem.getIcon() == null || !showIcon) {
-            buttonIcon.setVisibility(View.GONE);
-            if (buttonText != null) {
-                buttonText.setPaddingRelative(0, 0, 0, 0);
-            }
-        } else {
-            buttonIcon.setVisibility(View.VISIBLE);
-            buttonIcon.setImageDrawable(menuItem.getIcon());
-            if (buttonText != null) {
-                buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
-            }
-        }
-        final CharSequence contentDescription = menuItem.getContentDescription();
-        if (TextUtils.isEmpty(contentDescription)) {
-            menuItemButton.setContentDescription(menuItem.getTitle());
-        } else {
-            menuItemButton.setContentDescription(contentDescription);
-        }
-    }
-
-    private static ViewGroup createContentContainer(Context context) {
-        ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
-                .inflate(R.layout.floating_popup_container, null);
-        contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-        contentContainer.setTag(FLOATING_TOOLBAR_TAG);
-        contentContainer.setClipToOutline(true);
-        return contentContainer;
-    }
-
-    private static PopupWindow createPopupWindow(ViewGroup content) {
-        ViewGroup popupContentHolder = new LinearLayout(content.getContext());
-        PopupWindow popupWindow = new PopupWindow(popupContentHolder);
-        // TODO: Use .setIsLaidOutInScreen(true) instead of .setClippingEnabled(false)
-        // unless FLAG_LAYOUT_IN_SCREEN has any unintentional side-effects.
-        popupWindow.setClippingEnabled(false);
-        popupWindow.setWindowLayoutType(
-                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
-        popupWindow.setAnimationStyle(0);
-        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-        content.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-        popupContentHolder.addView(content);
-        return popupWindow;
-    }
-
-    /**
-     * Creates an "appear" animation for the specified view.
-     *
-     * @param view  The view to animate
-     */
-    private static AnimatorSet createEnterAnimation(View view) {
-        AnimatorSet animation = new AnimatorSet();
-        animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150));
-        return animation;
-    }
-
-    /**
-     * Creates a "disappear" animation for the specified view.
-     *
-     * @param view  The view to animate
-     * @param startDelay  The start delay of the animation
-     * @param listener  The animation listener
-     */
-    private static AnimatorSet createExitAnimation(
-            View view, int startDelay, Animator.AnimatorListener listener) {
-        AnimatorSet animation =  new AnimatorSet();
-        animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
-        animation.setStartDelay(startDelay);
-        animation.addListener(listener);
-        return animation;
-    }
-
-    /**
-     * Returns a re-themed context with controlled look and feel for views.
-     */
-    private static Context applyDefaultTheme(Context originalContext) {
-        TypedArray a = originalContext.obtainStyledAttributes(new int[]{R.attr.isLightTheme});
-        boolean isLightTheme = a.getBoolean(0, true);
-        int themeId
-                = isLightTheme ? R.style.Theme_DeviceDefault_Light : R.style.Theme_DeviceDefault;
-        a.recycle();
-        return new ContextThemeWrapper(originalContext, themeId);
-    }
 }
diff --git a/core/java/com/android/internal/widget/FloatingToolbarPopup.java b/core/java/com/android/internal/widget/FloatingToolbarPopup.java
new file mode 100644
index 0000000..e0388f6
--- /dev/null
+++ b/core/java/com/android/internal/widget/FloatingToolbarPopup.java
@@ -0,0 +1,1610 @@
+/*
+ * 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.internal.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Size;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.Transformation;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A popup window used by the floating toolbar.
+ *
+ * This class is responsible for the rendering/animation of the floating toolbar.
+ * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
+ * to transition between panels.
+ */
+public final class FloatingToolbarPopup {
+
+    /* Minimum and maximum number of items allowed in the overflow. */
+    private static final int MIN_OVERFLOW_SIZE = 2;
+    private static final int MAX_OVERFLOW_SIZE = 4;
+
+    private final Context mContext;
+    private final View mParent;  // Parent for the popup window.
+    private final PopupWindow mPopupWindow;
+
+    /* Margins between the popup window and its content. */
+    private final int mMarginHorizontal;
+    private final int mMarginVertical;
+
+    /* View components */
+    private final ViewGroup mContentContainer;  // holds all contents.
+    private final ViewGroup mMainPanel;  // holds menu items that are initially displayed.
+    // holds menu items hidden in the overflow.
+    private final FloatingToolbarPopup.OverflowPanel mOverflowPanel;
+    private final ImageButton mOverflowButton;  // opens/closes the overflow.
+    /* overflow button drawables. */
+    private final Drawable mArrow;
+    private final Drawable mOverflow;
+    private final AnimatedVectorDrawable mToArrow;
+    private final AnimatedVectorDrawable mToOverflow;
+
+    private final FloatingToolbarPopup.OverflowPanelViewHelper
+            mOverflowPanelViewHelper;
+
+    /* Animation interpolators. */
+    private final Interpolator mLogAccelerateInterpolator;
+    private final Interpolator mFastOutSlowInInterpolator;
+    private final Interpolator mLinearOutSlowInInterpolator;
+    private final Interpolator mFastOutLinearInInterpolator;
+
+    /* Animations. */
+    private final AnimatorSet mShowAnimation;
+    private final AnimatorSet mDismissAnimation;
+    private final AnimatorSet mHideAnimation;
+    private final AnimationSet mOpenOverflowAnimation;
+    private final AnimationSet mCloseOverflowAnimation;
+    private final Animation.AnimationListener mOverflowAnimationListener;
+
+    private final Rect mViewPortOnScreen = new Rect();  // portion of screen we can draw in.
+    private final Point mCoordsOnWindow = new Point();  // popup window coordinates.
+    /* Temporary data holders. Reset values before using. */
+    private final int[] mTmpCoords = new int[2];
+
+    private final Region mTouchableRegion = new Region();
+    private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
+            info -> {
+                info.contentInsets.setEmpty();
+                info.visibleInsets.setEmpty();
+                info.touchableRegion.set(mTouchableRegion);
+                info.setTouchableInsets(
+                        ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            };
+
+    private final int mLineHeight;
+    private final int mIconTextSpacing;
+
+    /**
+     * @see FloatingToolbarPopup.OverflowPanelViewHelper#preparePopupContent().
+     */
+    private final Runnable mPreparePopupContentRTLHelper = new Runnable() {
+        @Override
+        public void run() {
+            setPanelsStatesAtRestingPosition();
+            setContentAreaAsTouchableSurface();
+            mContentContainer.setAlpha(1);
+        }
+    };
+
+    private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
+    private boolean mHidden; // tracks whether this popup is hidden or hiding.
+
+    /* Calculated sizes for panels and overflow button. */
+    private final Size mOverflowButtonSize;
+    private Size mOverflowPanelSize;  // Should be null when there is no overflow.
+    private Size mMainPanelSize;
+
+    /* Menu items and click listeners */
+    private final Map<MenuItemRepr, MenuItem> mMenuItems = new LinkedHashMap<>();
+    private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
+    private final View.OnClickListener mMenuItemButtonOnClickListener =
+            new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mOnMenuItemClickListener == null) {
+                        return;
+                    }
+                    final Object tag = v.getTag();
+                    if (!(tag instanceof MenuItemRepr)) {
+                        return;
+                    }
+                    final MenuItem menuItem = mMenuItems.get((MenuItemRepr) tag);
+                    if (menuItem == null) {
+                        return;
+                    }
+                    mOnMenuItemClickListener.onMenuItemClick(menuItem);
+                }
+            };
+
+    private boolean mOpenOverflowUpwards;  // Whether the overflow opens upwards or downwards.
+    private boolean mIsOverflowOpen;
+
+    private int mTransitionDurationScale;  // Used to scale the toolbar transition duration.
+
+    /**
+     * Initializes a new floating toolbar popup.
+     *
+     * @param parent  A parent view to get the {@link android.view.View#getWindowToken()} token
+     *      from.
+     */
+    public FloatingToolbarPopup(Context context, View parent) {
+        mParent = Objects.requireNonNull(parent);
+        mContext = applyDefaultTheme(context);
+        mContentContainer = createContentContainer(context);
+        mPopupWindow = createPopupWindow(mContentContainer);
+        mMarginHorizontal = parent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+        mMarginVertical = parent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
+        mLineHeight = context.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_height);
+        mIconTextSpacing = context.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
+
+        // Interpolators
+        mLogAccelerateInterpolator = new FloatingToolbarPopup.LogAccelerateInterpolator();
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_slow_in);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.linear_out_slow_in);
+        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_linear_in);
+
+        // Drawables. Needed for views.
+        mArrow = mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_tooverflow, mContext.getTheme());
+        mArrow.setAutoMirrored(true);
+        mOverflow = mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_toarrow, mContext.getTheme());
+        mOverflow.setAutoMirrored(true);
+        mToArrow = (AnimatedVectorDrawable) mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_toarrow_animation, mContext.getTheme());
+        mToArrow.setAutoMirrored(true);
+        mToOverflow = (AnimatedVectorDrawable) mContext.getResources()
+                .getDrawable(R.drawable.ft_avd_tooverflow_animation, mContext.getTheme());
+        mToOverflow.setAutoMirrored(true);
+
+        // Views
+        mOverflowButton = createOverflowButton();
+        mOverflowButtonSize = measure(mOverflowButton);
+        mMainPanel = createMainPanel();
+        mOverflowPanelViewHelper =
+                new FloatingToolbarPopup.OverflowPanelViewHelper(mContext, mIconTextSpacing);
+        mOverflowPanel = createOverflowPanel();
+
+        // Animation. Need views.
+        mOverflowAnimationListener = createOverflowAnimationListener();
+        mOpenOverflowAnimation = new AnimationSet(true);
+        mOpenOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
+        mCloseOverflowAnimation = new AnimationSet(true);
+        mCloseOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
+        mShowAnimation = createEnterAnimation(mContentContainer);
+        mDismissAnimation = createExitAnimation(
+                mContentContainer,
+                150,  // startDelay
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPopupWindow.dismiss();
+                        mContentContainer.removeAllViews();
+                    }
+                });
+        mHideAnimation = createExitAnimation(
+                mContentContainer,
+                0,  // startDelay
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPopupWindow.dismiss();
+                    }
+                });
+    }
+
+    /**
+     * Makes this toolbar "outside touchable" and sets the onDismissListener.
+     *
+     * @param outsideTouchable if true, the popup will be made "outside touchable" and
+     *      "non focusable". The reverse will happen if false.
+     * @param onDismiss
+     *
+     * @return true if the "outsideTouchable" setting was modified. Otherwise returns false
+     *
+     * @see PopupWindow#setOutsideTouchable(boolean)
+     * @see PopupWindow#setFocusable(boolean)
+     * @see PopupWindow.OnDismissListener
+     */
+    public boolean setOutsideTouchable(
+            boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
+        boolean ret = false;
+        if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
+            mPopupWindow.setOutsideTouchable(outsideTouchable);
+            mPopupWindow.setFocusable(!outsideTouchable);
+            mPopupWindow.update();
+            ret = true;
+        }
+        mPopupWindow.setOnDismissListener(onDismiss);
+        return ret;
+    }
+
+    /**
+     * Lays out buttons for the specified menu items.
+     * Requires a subsequent call to {@link FloatingToolbar#show()} to show the items.
+     */
+    public void layoutMenuItems(
+            List<MenuItem> menuItems,
+            MenuItem.OnMenuItemClickListener menuItemClickListener,
+            int suggestedWidth) {
+        cancelOverflowAnimations();
+        clearPanels();
+        updateMenuItems(menuItems, menuItemClickListener);
+        menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth));
+        if (!menuItems.isEmpty()) {
+            // Add remaining items to the overflow.
+            layoutOverflowPanelItems(menuItems);
+        }
+        updatePopupSize();
+    }
+
+    /**
+     * Updates the popup's menu items without rebuilding the widget.
+     * Use in place of layoutMenuItems() when the popup's views need not be reconstructed.
+     *
+     * @see #isLayoutRequired(List<MenuItem>)
+     */
+    public void updateMenuItems(
+            List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener) {
+        mMenuItems.clear();
+        for (MenuItem menuItem : menuItems) {
+            mMenuItems.put(MenuItemRepr.of(menuItem), menuItem);
+        }
+        mOnMenuItemClickListener = menuItemClickListener;
+    }
+
+    /**
+     * Returns true if this popup needs a relayout to properly render the specified menu items.
+     */
+    public boolean isLayoutRequired(List<MenuItem> menuItems) {
+        return !MenuItemRepr.reprEquals(menuItems, mMenuItems.values());
+    }
+
+    /**
+     * Shows this popup at the specified coordinates.
+     * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+     */
+    public void show(Rect contentRectOnScreen) {
+        Objects.requireNonNull(contentRectOnScreen);
+
+        if (isShowing()) {
+            return;
+        }
+
+        mHidden = false;
+        mDismissed = false;
+        cancelDismissAndHideAnimations();
+        cancelOverflowAnimations();
+
+        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
+        preparePopupContent();
+        // We need to specify the position in window coordinates.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
+        // specify the popup position in screen coordinates.
+        mPopupWindow.showAtLocation(
+                mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x, mCoordsOnWindow.y);
+        setTouchableSurfaceInsetsComputer();
+        runShowAnimation();
+    }
+
+    /**
+     * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
+     */
+    public void dismiss() {
+        if (mDismissed) {
+            return;
+        }
+
+        mHidden = false;
+        mDismissed = true;
+        mHideAnimation.cancel();
+
+        runDismissAnimation();
+        setZeroTouchableSurface();
+    }
+
+    /**
+     * Hides this popup. This is a no-op if this popup is not showing.
+     * Use {@link #isHidden()} to distinguish between a hidden and a dismissed popup.
+     */
+    public void hide() {
+        if (!isShowing()) {
+            return;
+        }
+
+        mHidden = true;
+        runHideAnimation();
+        setZeroTouchableSurface();
+    }
+
+    /**
+     * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+     */
+    public boolean isShowing() {
+        return !mDismissed && !mHidden;
+    }
+
+    /**
+     * Returns {@code true} if this popup is currently hidden. {@code false} otherwise.
+     */
+    public boolean isHidden() {
+        return mHidden;
+    }
+
+    /**
+     * Updates the coordinates of this popup.
+     * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+     * This is a no-op if this popup is not showing.
+     */
+    public void updateCoordinates(Rect contentRectOnScreen) {
+        Objects.requireNonNull(contentRectOnScreen);
+
+        if (!isShowing() || !mPopupWindow.isShowing()) {
+            return;
+        }
+
+        cancelOverflowAnimations();
+        refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
+        preparePopupContent();
+        // We need to specify the position in window coordinates.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can
+        // specify the popup position in screen coordinates.
+        mPopupWindow.update(
+                mCoordsOnWindow.x, mCoordsOnWindow.y,
+                mPopupWindow.getWidth(), mPopupWindow.getHeight());
+    }
+
+    private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
+        refreshViewPort();
+
+        // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
+        // landscape.
+        final int x = Math.min(
+                contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
+                mViewPortOnScreen.right - mPopupWindow.getWidth());
+
+        final int y;
+
+        final int availableHeightAboveContent =
+                contentRectOnScreen.top - mViewPortOnScreen.top;
+        final int availableHeightBelowContent =
+                mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
+
+        final int margin = 2 * mMarginVertical;
+        final int toolbarHeightWithVerticalMargin = mLineHeight + margin;
+
+        if (!hasOverflow()) {
+            if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin) {
+                // There is enough space at the top of the content.
+                y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
+            } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin) {
+                // There is enough space at the bottom of the content.
+                y = contentRectOnScreen.bottom;
+            } else if (availableHeightBelowContent >= mLineHeight) {
+                // Just enough space to fit the toolbar with no vertical margins.
+                y = contentRectOnScreen.bottom - mMarginVertical;
+            } else {
+                // Not enough space. Prefer to position as high as possible.
+                y = Math.max(
+                        mViewPortOnScreen.top,
+                        contentRectOnScreen.top - toolbarHeightWithVerticalMargin);
+            }
+        } else {
+            // Has an overflow.
+            final int minimumOverflowHeightWithMargin =
+                    calculateOverflowHeight(MIN_OVERFLOW_SIZE) + margin;
+            final int availableHeightThroughContentDown =
+                    mViewPortOnScreen.bottom - contentRectOnScreen.top
+                            + toolbarHeightWithVerticalMargin;
+            final int availableHeightThroughContentUp =
+                    contentRectOnScreen.bottom - mViewPortOnScreen.top
+                            + toolbarHeightWithVerticalMargin;
+
+            if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the top of the content rect for the overflow.
+                // Position above and open upwards.
+                updateOverflowHeight(availableHeightAboveContent - margin);
+                y = contentRectOnScreen.top - mPopupWindow.getHeight();
+                mOpenOverflowUpwards = true;
+            } else if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin
+                    && availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the top of the content rect for the main panel
+                // but not the overflow.
+                // Position above but open downwards.
+                updateOverflowHeight(availableHeightThroughContentDown - margin);
+                y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
+                mOpenOverflowUpwards = false;
+            } else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the bottom of the content rect for the overflow.
+                // Position below and open downwards.
+                updateOverflowHeight(availableHeightBelowContent - margin);
+                y = contentRectOnScreen.bottom;
+                mOpenOverflowUpwards = false;
+            } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin
+                    && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) {
+                // There is enough space at the bottom of the content rect for the main panel
+                // but not the overflow.
+                // Position below but open upwards.
+                updateOverflowHeight(availableHeightThroughContentUp - margin);
+                y = contentRectOnScreen.bottom + toolbarHeightWithVerticalMargin
+                        - mPopupWindow.getHeight();
+                mOpenOverflowUpwards = true;
+            } else {
+                // Not enough space.
+                // Position at the top of the view port and open downwards.
+                updateOverflowHeight(mViewPortOnScreen.height() - margin);
+                y = mViewPortOnScreen.top;
+                mOpenOverflowUpwards = false;
+            }
+        }
+
+        // We later specify the location of PopupWindow relative to the attached window.
+        // The idea here is that 1) we can get the location of a View in both window coordinates
+        // and screen coordinates, where the offset between them should be equal to the window
+        // origin, and 2) we can use an arbitrary for this calculation while calculating the
+        // location of the rootview is supposed to be least expensive.
+        // TODO: Consider to use PopupWindow.setIsLaidOutInScreen(true) so that we can avoid
+        // the following calculation.
+        mParent.getRootView().getLocationOnScreen(mTmpCoords);
+        int rootViewLeftOnScreen = mTmpCoords[0];
+        int rootViewTopOnScreen = mTmpCoords[1];
+        mParent.getRootView().getLocationInWindow(mTmpCoords);
+        int rootViewLeftOnWindow = mTmpCoords[0];
+        int rootViewTopOnWindow = mTmpCoords[1];
+        int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
+        int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
+        mCoordsOnWindow.set(
+                Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
+    }
+
+    /**
+     * Performs the "show" animation on the floating popup.
+     */
+    private void runShowAnimation() {
+        mShowAnimation.start();
+    }
+
+    /**
+     * Performs the "dismiss" animation on the floating popup.
+     */
+    private void runDismissAnimation() {
+        mDismissAnimation.start();
+    }
+
+    /**
+     * Performs the "hide" animation on the floating popup.
+     */
+    private void runHideAnimation() {
+        mHideAnimation.start();
+    }
+
+    private void cancelDismissAndHideAnimations() {
+        mDismissAnimation.cancel();
+        mHideAnimation.cancel();
+    }
+
+    private void cancelOverflowAnimations() {
+        mContentContainer.clearAnimation();
+        mMainPanel.animate().cancel();
+        mOverflowPanel.animate().cancel();
+        mToArrow.stop();
+        mToOverflow.stop();
+    }
+
+    private void openOverflow() {
+        final int targetWidth = mOverflowPanelSize.getWidth();
+        final int targetHeight = mOverflowPanelSize.getHeight();
+        final int startWidth = mContentContainer.getWidth();
+        final int startHeight = mContentContainer.getHeight();
+        final float startY = mContentContainer.getY();
+        final float left = mContentContainer.getX();
+        final float right = left + mContentContainer.getWidth();
+        Animation widthAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
+                setWidth(mContentContainer, startWidth + deltaWidth);
+                if (isInRTLMode()) {
+                    mContentContainer.setX(left);
+
+                    // Lock the panels in place.
+                    mMainPanel.setX(0);
+                    mOverflowPanel.setX(0);
+                } else {
+                    mContentContainer.setX(right - mContentContainer.getWidth());
+
+                    // Offset the panels' positions so they look like they're locked in place
+                    // on the screen.
+                    mMainPanel.setX(mContentContainer.getWidth() - startWidth);
+                    mOverflowPanel.setX(mContentContainer.getWidth() - targetWidth);
+                }
+            }
+        };
+        Animation heightAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
+                setHeight(mContentContainer, startHeight + deltaHeight);
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(
+                            startY - (mContentContainer.getHeight() - startHeight));
+                    positionContentYCoordinatesIfOpeningOverflowUpwards();
+                }
+            }
+        };
+        final float overflowButtonStartX = mOverflowButton.getX();
+        final float overflowButtonTargetX =
+                isInRTLMode() ? overflowButtonStartX + targetWidth - mOverflowButton.getWidth()
+                        : overflowButtonStartX - targetWidth + mOverflowButton.getWidth();
+        Animation overflowButtonAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                float overflowButtonX = overflowButtonStartX
+                        + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
+                float deltaContainerWidth =
+                        isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
+                float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
+                mOverflowButton.setX(actualOverflowButtonX);
+            }
+        };
+        widthAnimation.setInterpolator(mLogAccelerateInterpolator);
+        widthAnimation.setDuration(getAdjustedDuration(250));
+        heightAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        heightAnimation.setDuration(getAdjustedDuration(250));
+        overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
+        mOpenOverflowAnimation.getAnimations().clear();
+        mOpenOverflowAnimation.getAnimations().clear();
+        mOpenOverflowAnimation.addAnimation(widthAnimation);
+        mOpenOverflowAnimation.addAnimation(heightAnimation);
+        mOpenOverflowAnimation.addAnimation(overflowButtonAnimation);
+        mContentContainer.startAnimation(mOpenOverflowAnimation);
+        mIsOverflowOpen = true;
+        mMainPanel.animate()
+                .alpha(0).withLayer()
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setDuration(250)
+                .start();
+        mOverflowPanel.setAlpha(1); // fadeIn in 0ms.
+    }
+
+    private void closeOverflow() {
+        final int targetWidth = mMainPanelSize.getWidth();
+        final int startWidth = mContentContainer.getWidth();
+        final float left = mContentContainer.getX();
+        final float right = left + mContentContainer.getWidth();
+        Animation widthAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
+                setWidth(mContentContainer, startWidth + deltaWidth);
+                if (isInRTLMode()) {
+                    mContentContainer.setX(left);
+
+                    // Lock the panels in place.
+                    mMainPanel.setX(0);
+                    mOverflowPanel.setX(0);
+                } else {
+                    mContentContainer.setX(right - mContentContainer.getWidth());
+
+                    // Offset the panels' positions so they look like they're locked in place
+                    // on the screen.
+                    mMainPanel.setX(mContentContainer.getWidth() - targetWidth);
+                    mOverflowPanel.setX(mContentContainer.getWidth() - startWidth);
+                }
+            }
+        };
+        final int targetHeight = mMainPanelSize.getHeight();
+        final int startHeight = mContentContainer.getHeight();
+        final float bottom = mContentContainer.getY() + mContentContainer.getHeight();
+        Animation heightAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
+                setHeight(mContentContainer, startHeight + deltaHeight);
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(bottom - mContentContainer.getHeight());
+                    positionContentYCoordinatesIfOpeningOverflowUpwards();
+                }
+            }
+        };
+        final float overflowButtonStartX = mOverflowButton.getX();
+        final float overflowButtonTargetX =
+                isInRTLMode() ? overflowButtonStartX - startWidth + mOverflowButton.getWidth()
+                        : overflowButtonStartX + startWidth - mOverflowButton.getWidth();
+        Animation overflowButtonAnimation = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                float overflowButtonX = overflowButtonStartX
+                        + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
+                float deltaContainerWidth =
+                        isInRTLMode() ? 0 : mContentContainer.getWidth() - startWidth;
+                float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
+                mOverflowButton.setX(actualOverflowButtonX);
+            }
+        };
+        widthAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        widthAnimation.setDuration(getAdjustedDuration(250));
+        heightAnimation.setInterpolator(mLogAccelerateInterpolator);
+        heightAnimation.setDuration(getAdjustedDuration(250));
+        overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
+        overflowButtonAnimation.setDuration(getAdjustedDuration(250));
+        mCloseOverflowAnimation.getAnimations().clear();
+        mCloseOverflowAnimation.addAnimation(widthAnimation);
+        mCloseOverflowAnimation.addAnimation(heightAnimation);
+        mCloseOverflowAnimation.addAnimation(overflowButtonAnimation);
+        mContentContainer.startAnimation(mCloseOverflowAnimation);
+        mIsOverflowOpen = false;
+        mMainPanel.animate()
+                .alpha(1).withLayer()
+                .setInterpolator(mFastOutLinearInInterpolator)
+                .setDuration(100)
+                .start();
+        mOverflowPanel.animate()
+                .alpha(0).withLayer()
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setDuration(150)
+                .start();
+    }
+
+    /**
+     * Defines the position of the floating toolbar popup panels when transition animation has
+     * stopped.
+     */
+    private void setPanelsStatesAtRestingPosition() {
+        mOverflowButton.setEnabled(true);
+        mOverflowPanel.awakenScrollBars();
+
+        if (mIsOverflowOpen) {
+            // Set open state.
+            final Size containerSize = mOverflowPanelSize;
+            setSize(mContentContainer, containerSize);
+            mMainPanel.setAlpha(0);
+            mMainPanel.setVisibility(View.INVISIBLE);
+            mOverflowPanel.setAlpha(1);
+            mOverflowPanel.setVisibility(View.VISIBLE);
+            mOverflowButton.setImageDrawable(mArrow);
+            mOverflowButton.setContentDescription(mContext.getString(
+                    R.string.floating_toolbar_close_overflow_description));
+
+            // Update x-coordinates depending on RTL state.
+            if (isInRTLMode()) {
+                mContentContainer.setX(mMarginHorizontal);  // align left
+                mMainPanel.setX(0);  // align left
+                mOverflowButton.setX(// align right
+                        containerSize.getWidth() - mOverflowButtonSize.getWidth());
+                mOverflowPanel.setX(0);  // align left
+            } else {
+                mContentContainer.setX(// align right
+                        mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
+                mMainPanel.setX(-mContentContainer.getX());  // align right
+                mOverflowButton.setX(0);  // align left
+                mOverflowPanel.setX(0);  // align left
+            }
+
+            // Update y-coordinates depending on overflow's open direction.
+            if (mOpenOverflowUpwards) {
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setY(// align bottom
+                        containerSize.getHeight() - mContentContainer.getHeight());
+                mOverflowButton.setY(// align bottom
+                        containerSize.getHeight() - mOverflowButtonSize.getHeight());
+                mOverflowPanel.setY(0);  // align top
+            } else {
+                // opens downwards.
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setY(0);  // align top
+                mOverflowButton.setY(0);  // align top
+                mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
+            }
+        } else {
+            // Overflow not open. Set closed state.
+            final Size containerSize = mMainPanelSize;
+            setSize(mContentContainer, containerSize);
+            mMainPanel.setAlpha(1);
+            mMainPanel.setVisibility(View.VISIBLE);
+            mOverflowPanel.setAlpha(0);
+            mOverflowPanel.setVisibility(View.INVISIBLE);
+            mOverflowButton.setImageDrawable(mOverflow);
+            mOverflowButton.setContentDescription(mContext.getString(
+                    R.string.floating_toolbar_open_overflow_description));
+
+            if (hasOverflow()) {
+                // Update x-coordinates depending on RTL state.
+                if (isInRTLMode()) {
+                    mContentContainer.setX(mMarginHorizontal);  // align left
+                    mMainPanel.setX(0);  // align left
+                    mOverflowButton.setX(0);  // align left
+                    mOverflowPanel.setX(0);  // align left
+                } else {
+                    mContentContainer.setX(// align right
+                            mPopupWindow.getWidth() - containerSize.getWidth() - mMarginHorizontal);
+                    mMainPanel.setX(0);  // align left
+                    mOverflowButton.setX(// align right
+                            containerSize.getWidth() - mOverflowButtonSize.getWidth());
+                    mOverflowPanel.setX(// align right
+                            containerSize.getWidth() - mOverflowPanelSize.getWidth());
+                }
+
+                // Update y-coordinates depending on overflow's open direction.
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(// align bottom
+                            mMarginVertical + mOverflowPanelSize.getHeight()
+                                    - containerSize.getHeight());
+                    mMainPanel.setY(0);  // align top
+                    mOverflowButton.setY(0);  // align top
+                    mOverflowPanel.setY(// align bottom
+                            containerSize.getHeight() - mOverflowPanelSize.getHeight());
+                } else {
+                    // opens downwards.
+                    mContentContainer.setY(mMarginVertical);  // align top
+                    mMainPanel.setY(0);  // align top
+                    mOverflowButton.setY(0);  // align top
+                    mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
+                }
+            } else {
+                // No overflow.
+                mContentContainer.setX(mMarginHorizontal);  // align left
+                mContentContainer.setY(mMarginVertical);  // align top
+                mMainPanel.setX(0);  // align left
+                mMainPanel.setY(0);  // align top
+            }
+        }
+    }
+
+    private void updateOverflowHeight(int suggestedHeight) {
+        if (hasOverflow()) {
+            final int maxItemSize =
+                    (suggestedHeight - mOverflowButtonSize.getHeight()) / mLineHeight;
+            final int newHeight = calculateOverflowHeight(maxItemSize);
+            if (mOverflowPanelSize.getHeight() != newHeight) {
+                mOverflowPanelSize = new Size(mOverflowPanelSize.getWidth(), newHeight);
+            }
+            setSize(mOverflowPanel, mOverflowPanelSize);
+            if (mIsOverflowOpen) {
+                setSize(mContentContainer, mOverflowPanelSize);
+                if (mOpenOverflowUpwards) {
+                    final int deltaHeight = mOverflowPanelSize.getHeight() - newHeight;
+                    mContentContainer.setY(mContentContainer.getY() + deltaHeight);
+                    mOverflowButton.setY(mOverflowButton.getY() - deltaHeight);
+                }
+            } else {
+                setSize(mContentContainer, mMainPanelSize);
+            }
+            updatePopupSize();
+        }
+    }
+
+    private void updatePopupSize() {
+        int width = 0;
+        int height = 0;
+        if (mMainPanelSize != null) {
+            width = Math.max(width, mMainPanelSize.getWidth());
+            height = Math.max(height, mMainPanelSize.getHeight());
+        }
+        if (mOverflowPanelSize != null) {
+            width = Math.max(width, mOverflowPanelSize.getWidth());
+            height = Math.max(height, mOverflowPanelSize.getHeight());
+        }
+        mPopupWindow.setWidth(width + mMarginHorizontal * 2);
+        mPopupWindow.setHeight(height + mMarginVertical * 2);
+        maybeComputeTransitionDurationScale();
+    }
+
+    private void refreshViewPort() {
+        mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen);
+    }
+
+    private int getAdjustedToolbarWidth(int suggestedWidth) {
+        int width = suggestedWidth;
+        refreshViewPort();
+        int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+        if (width <= 0) {
+            width = mParent.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
+        }
+        return Math.min(width, maximumWidth);
+    }
+
+    /**
+     * Sets the touchable region of this popup to be zero. This means that all touch events on
+     * this popup will go through to the surface behind it.
+     */
+    private void setZeroTouchableSurface() {
+        mTouchableRegion.setEmpty();
+    }
+
+    /**
+     * Sets the touchable region of this popup to be the area occupied by its content.
+     */
+    private void setContentAreaAsTouchableSurface() {
+        Objects.requireNonNull(mMainPanelSize);
+        final int width;
+        final int height;
+        if (mIsOverflowOpen) {
+            Objects.requireNonNull(mOverflowPanelSize);
+            width = mOverflowPanelSize.getWidth();
+            height = mOverflowPanelSize.getHeight();
+        } else {
+            width = mMainPanelSize.getWidth();
+            height = mMainPanelSize.getHeight();
+        }
+        mTouchableRegion.set(
+                (int) mContentContainer.getX(),
+                (int) mContentContainer.getY(),
+                (int) mContentContainer.getX() + width,
+                (int) mContentContainer.getY() + height);
+    }
+
+    /**
+     * Make the touchable area of this popup be the area specified by mTouchableRegion.
+     * This should be called after the popup window has been dismissed (dismiss/hide)
+     * and is probably being re-shown with a new content root view.
+     */
+    private void setTouchableSurfaceInsetsComputer() {
+        ViewTreeObserver viewTreeObserver = mPopupWindow.getContentView()
+                .getRootView()
+                .getViewTreeObserver();
+        viewTreeObserver.removeOnComputeInternalInsetsListener(mInsetsComputer);
+        viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer);
+    }
+
+    private boolean isInRTLMode() {
+        return mContext.getApplicationInfo().hasRtlSupport()
+                && mContext.getResources().getConfiguration().getLayoutDirection()
+                == View.LAYOUT_DIRECTION_RTL;
+    }
+
+    private boolean hasOverflow() {
+        return mOverflowPanelSize != null;
+    }
+
+    /**
+     * Fits as many menu items in the main panel and returns a list of the menu items that
+     * were not fit in.
+     *
+     * @return The menu items that are not included in this main panel.
+     */
+    public List<MenuItem> layoutMainPanelItems(
+            List<MenuItem> menuItems, final int toolbarWidth) {
+        Objects.requireNonNull(menuItems);
+
+        int availableWidth = toolbarWidth;
+
+        final LinkedList<MenuItem> remainingMenuItems = new LinkedList<>();
+        // add the overflow menu items to the end of the remainingMenuItems list.
+        final LinkedList<MenuItem> overflowMenuItems = new LinkedList();
+        for (MenuItem menuItem : menuItems) {
+            if (menuItem.getItemId() != android.R.id.textAssist
+                    && menuItem.requiresOverflow()) {
+                overflowMenuItems.add(menuItem);
+            } else {
+                remainingMenuItems.add(menuItem);
+            }
+        }
+        remainingMenuItems.addAll(overflowMenuItems);
+
+        mMainPanel.removeAllViews();
+        mMainPanel.setPaddingRelative(0, 0, 0, 0);
+
+        int lastGroupId = -1;
+        boolean isFirstItem = true;
+        while (!remainingMenuItems.isEmpty()) {
+            final MenuItem menuItem = remainingMenuItems.peek();
+
+            // if this is the first item, regardless of requiresOverflow(), it should be
+            // displayed on the main panel. Otherwise all items including this one will be
+            // overflow items, and should be displayed in overflow panel.
+            if (!isFirstItem && menuItem.requiresOverflow()) {
+                break;
+            }
+
+            final boolean showIcon = isFirstItem && menuItem.getItemId() == R.id.textAssist;
+            final View menuItemButton = createMenuItemButton(
+                    mContext, menuItem, mIconTextSpacing, showIcon);
+            if (!showIcon && menuItemButton instanceof LinearLayout) {
+                ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
+            }
+
+            // Adding additional start padding for the first button to even out button spacing.
+            if (isFirstItem) {
+                menuItemButton.setPaddingRelative(
+                        (int) (1.5 * menuItemButton.getPaddingStart()),
+                        menuItemButton.getPaddingTop(),
+                        menuItemButton.getPaddingEnd(),
+                        menuItemButton.getPaddingBottom());
+            }
+
+            // Adding additional end padding for the last button to even out button spacing.
+            boolean isLastItem = remainingMenuItems.size() == 1;
+            if (isLastItem) {
+                menuItemButton.setPaddingRelative(
+                        menuItemButton.getPaddingStart(),
+                        menuItemButton.getPaddingTop(),
+                        (int) (1.5 * menuItemButton.getPaddingEnd()),
+                        menuItemButton.getPaddingBottom());
+            }
+
+            menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            final int menuItemButtonWidth = Math.min(
+                    menuItemButton.getMeasuredWidth(), toolbarWidth);
+
+            // Check if we can fit an item while reserving space for the overflowButton.
+            final boolean canFitWithOverflow =
+                    menuItemButtonWidth <= availableWidth - mOverflowButtonSize.getWidth();
+            final boolean canFitNoOverflow =
+                    isLastItem && menuItemButtonWidth <= availableWidth;
+            if (canFitWithOverflow || canFitNoOverflow) {
+                setButtonTagAndClickListener(menuItemButton, menuItem);
+                // Set tooltips for main panel items, but not overflow items (b/35726766).
+                menuItemButton.setTooltipText(menuItem.getTooltipText());
+                mMainPanel.addView(menuItemButton);
+                final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
+                params.width = menuItemButtonWidth;
+                menuItemButton.setLayoutParams(params);
+                availableWidth -= menuItemButtonWidth;
+                remainingMenuItems.pop();
+            } else {
+                break;
+            }
+            lastGroupId = menuItem.getGroupId();
+            isFirstItem = false;
+        }
+
+        if (!remainingMenuItems.isEmpty()) {
+            // Reserve space for overflowButton.
+            mMainPanel.setPaddingRelative(0, 0, mOverflowButtonSize.getWidth(), 0);
+        }
+
+        mMainPanelSize = measure(mMainPanel);
+        return remainingMenuItems;
+    }
+
+    private void layoutOverflowPanelItems(List<MenuItem> menuItems) {
+        ArrayAdapter<MenuItem> overflowPanelAdapter =
+                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+        overflowPanelAdapter.clear();
+        final int size = menuItems.size();
+        for (int i = 0; i < size; i++) {
+            overflowPanelAdapter.add(menuItems.get(i));
+        }
+        mOverflowPanel.setAdapter(overflowPanelAdapter);
+        if (mOpenOverflowUpwards) {
+            mOverflowPanel.setY(0);
+        } else {
+            mOverflowPanel.setY(mOverflowButtonSize.getHeight());
+        }
+
+        int width = Math.max(getOverflowWidth(), mOverflowButtonSize.getWidth());
+        int height = calculateOverflowHeight(MAX_OVERFLOW_SIZE);
+        mOverflowPanelSize = new Size(width, height);
+        setSize(mOverflowPanel, mOverflowPanelSize);
+    }
+
+    /**
+     * Resets the content container and appropriately position it's panels.
+     */
+    private void preparePopupContent() {
+        mContentContainer.removeAllViews();
+
+        // Add views in the specified order so they stack up as expected.
+        // Order: overflowPanel, mainPanel, overflowButton.
+        if (hasOverflow()) {
+            mContentContainer.addView(mOverflowPanel);
+        }
+        mContentContainer.addView(mMainPanel);
+        if (hasOverflow()) {
+            mContentContainer.addView(mOverflowButton);
+        }
+        setPanelsStatesAtRestingPosition();
+        setContentAreaAsTouchableSurface();
+
+        // The positioning of contents in RTL is wrong when the view is first rendered.
+        // Hide the view and post a runnable to recalculate positions and render the view.
+        // TODO: Investigate why this happens and fix.
+        if (isInRTLMode()) {
+            mContentContainer.setAlpha(0);
+            mContentContainer.post(mPreparePopupContentRTLHelper);
+        }
+    }
+
+    /**
+     * Clears out the panels and their container. Resets their calculated sizes.
+     */
+    private void clearPanels() {
+        mOverflowPanelSize = null;
+        mMainPanelSize = null;
+        mIsOverflowOpen = false;
+        mMainPanel.removeAllViews();
+        ArrayAdapter<MenuItem> overflowPanelAdapter =
+                (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+        overflowPanelAdapter.clear();
+        mOverflowPanel.setAdapter(overflowPanelAdapter);
+        mContentContainer.removeAllViews();
+    }
+
+    private void positionContentYCoordinatesIfOpeningOverflowUpwards() {
+        if (mOpenOverflowUpwards) {
+            mMainPanel.setY(mContentContainer.getHeight() - mMainPanelSize.getHeight());
+            mOverflowButton.setY(mContentContainer.getHeight() - mOverflowButton.getHeight());
+            mOverflowPanel.setY(mContentContainer.getHeight() - mOverflowPanelSize.getHeight());
+        }
+    }
+
+    private int getOverflowWidth() {
+        int overflowWidth = 0;
+        final int count = mOverflowPanel.getAdapter().getCount();
+        for (int i = 0; i < count; i++) {
+            MenuItem menuItem = (MenuItem) mOverflowPanel.getAdapter().getItem(i);
+            overflowWidth =
+                    Math.max(mOverflowPanelViewHelper.calculateWidth(menuItem), overflowWidth);
+        }
+        return overflowWidth;
+    }
+
+    private int calculateOverflowHeight(int maxItemSize) {
+        // Maximum of 4 items, minimum of 2 if the overflow has to scroll.
+        int actualSize = Math.min(
+                MAX_OVERFLOW_SIZE,
+                Math.min(
+                        Math.max(MIN_OVERFLOW_SIZE, maxItemSize),
+                        mOverflowPanel.getCount()));
+        int extension = 0;
+        if (actualSize < mOverflowPanel.getCount()) {
+            // The overflow will require scrolling to get to all the items.
+            // Extend the height so that part of the hidden items is displayed.
+            extension = (int) (mLineHeight * 0.5f);
+        }
+        return actualSize * mLineHeight
+                + mOverflowButtonSize.getHeight()
+                + extension;
+    }
+
+    private void setButtonTagAndClickListener(View menuItemButton, MenuItem menuItem) {
+        menuItemButton.setTag(MenuItemRepr.of(menuItem));
+        menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
+    }
+
+    /**
+     * NOTE: Use only in android.view.animation.* animations. Do not use in android.animation.*
+     * animations. See comment about this in the code.
+     */
+    private int getAdjustedDuration(int originalDuration) {
+        if (mTransitionDurationScale < 150) {
+            // For smaller transition, decrease the time.
+            return Math.max(originalDuration - 50, 0);
+        } else if (mTransitionDurationScale > 300) {
+            // For bigger transition, increase the time.
+            return originalDuration + 50;
+        }
+
+        // Scale the animation duration with getDurationScale(). This allows
+        // android.view.animation.* animations to scale just like android.animation.* animations
+        // when  animator duration scale is adjusted in "Developer Options".
+        // For this reason, do not use this method for android.animation.* animations.
+        return (int) (originalDuration * ValueAnimator.getDurationScale());
+    }
+
+    private void maybeComputeTransitionDurationScale() {
+        if (mMainPanelSize != null && mOverflowPanelSize != null) {
+            int w = mMainPanelSize.getWidth() - mOverflowPanelSize.getWidth();
+            int h = mOverflowPanelSize.getHeight() - mMainPanelSize.getHeight();
+            mTransitionDurationScale = (int) (Math.sqrt(w * w + h * h)
+                    / mContentContainer.getContext().getResources().getDisplayMetrics().density);
+        }
+    }
+
+    private ViewGroup createMainPanel() {
+        ViewGroup mainPanel = new LinearLayout(mContext) {
+            @Override
+            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+                if (isOverflowAnimating()) {
+                    // Update widthMeasureSpec to make sure that this view is not clipped
+                    // as we offset its coordinates with respect to its parent.
+                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            mMainPanelSize.getWidth(),
+                            MeasureSpec.EXACTLY);
+                }
+                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+
+            @Override
+            public boolean onInterceptTouchEvent(MotionEvent ev) {
+                // Intercept the touch event while the overflow is animating.
+                return isOverflowAnimating();
+            }
+        };
+        return mainPanel;
+    }
+
+    private ImageButton createOverflowButton() {
+        final ImageButton overflowButton = (ImageButton) LayoutInflater.from(mContext)
+                .inflate(R.layout.floating_popup_overflow_button, null);
+        overflowButton.setImageDrawable(mOverflow);
+        overflowButton.setOnClickListener(v -> {
+            if (mIsOverflowOpen) {
+                overflowButton.setImageDrawable(mToOverflow);
+                mToOverflow.start();
+                closeOverflow();
+            } else {
+                overflowButton.setImageDrawable(mToArrow);
+                mToArrow.start();
+                openOverflow();
+            }
+        });
+        return overflowButton;
+    }
+
+    private FloatingToolbarPopup.OverflowPanel createOverflowPanel() {
+        final FloatingToolbarPopup.OverflowPanel
+                overflowPanel = new FloatingToolbarPopup.OverflowPanel(this);
+        overflowPanel.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+        overflowPanel.setDivider(null);
+        overflowPanel.setDividerHeight(0);
+
+        final ArrayAdapter adapter =
+                new ArrayAdapter<MenuItem>(mContext, 0) {
+                    @Override
+                    public View getView(int position, View convertView, ViewGroup parent) {
+                        return mOverflowPanelViewHelper.getView(
+                                getItem(position), mOverflowPanelSize.getWidth(), convertView);
+                    }
+                };
+        overflowPanel.setAdapter(adapter);
+
+        overflowPanel.setOnItemClickListener((parent, view, position, id) -> {
+            MenuItem menuItem = (MenuItem) overflowPanel.getAdapter().getItem(position);
+            if (mOnMenuItemClickListener != null) {
+                mOnMenuItemClickListener.onMenuItemClick(menuItem);
+            }
+        });
+
+        return overflowPanel;
+    }
+
+    private boolean isOverflowAnimating() {
+        final boolean overflowOpening = mOpenOverflowAnimation.hasStarted()
+                && !mOpenOverflowAnimation.hasEnded();
+        final boolean overflowClosing = mCloseOverflowAnimation.hasStarted()
+                && !mCloseOverflowAnimation.hasEnded();
+        return overflowOpening || overflowClosing;
+    }
+
+    private Animation.AnimationListener createOverflowAnimationListener() {
+        Animation.AnimationListener listener = new Animation.AnimationListener() {
+            @Override
+            public void onAnimationStart(Animation animation) {
+                // Disable the overflow button while it's animating.
+                // It will be re-enabled when the animation stops.
+                mOverflowButton.setEnabled(false);
+                // Ensure both panels have visibility turned on when the overflow animation
+                // starts.
+                mMainPanel.setVisibility(View.VISIBLE);
+                mOverflowPanel.setVisibility(View.VISIBLE);
+            }
+
+            @Override
+            public void onAnimationEnd(Animation animation) {
+                // Posting this because it seems like this is called before the animation
+                // actually ends.
+                mContentContainer.post(() -> {
+                    setPanelsStatesAtRestingPosition();
+                    setContentAreaAsTouchableSurface();
+                });
+            }
+
+            @Override
+            public void onAnimationRepeat(Animation animation) {
+            }
+        };
+        return listener;
+    }
+
+    private static Size measure(View view) {
+        Preconditions.checkState(view.getParent() == null);
+        view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        return new Size(view.getMeasuredWidth(), view.getMeasuredHeight());
+    }
+
+    private static void setSize(View view, int width, int height) {
+        view.setMinimumWidth(width);
+        view.setMinimumHeight(height);
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        params = (params == null) ? new ViewGroup.LayoutParams(0, 0) : params;
+        params.width = width;
+        params.height = height;
+        view.setLayoutParams(params);
+    }
+
+    private static void setSize(View view, Size size) {
+        setSize(view, size.getWidth(), size.getHeight());
+    }
+
+    private static void setWidth(View view, int width) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        setSize(view, width, params.height);
+    }
+
+    private static void setHeight(View view, int height) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        setSize(view, params.width, height);
+    }
+
+    /**
+     * A custom ListView for the overflow panel.
+     */
+    private static final class OverflowPanel extends ListView {
+
+        private final FloatingToolbarPopup mPopup;
+
+        OverflowPanel(FloatingToolbarPopup popup) {
+            super(Objects.requireNonNull(popup).mContext);
+            this.mPopup = popup;
+            setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
+            setScrollIndicators(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            // Update heightMeasureSpec to make sure that this view is not clipped
+            // as we offset it's coordinates with respect to its parent.
+            int height = mPopup.mOverflowPanelSize.getHeight()
+                    - mPopup.mOverflowButtonSize.getHeight();
+            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            if (mPopup.isOverflowAnimating()) {
+                // Eat the touch event.
+                return true;
+            }
+            return super.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        protected boolean awakenScrollBars() {
+            return super.awakenScrollBars();
+        }
+    }
+
+    /**
+     * A custom interpolator used for various floating toolbar animations.
+     */
+    private static final class LogAccelerateInterpolator implements Interpolator {
+
+        private static final int BASE = 100;
+        private static final float LOGS_SCALE = 1f / computeLog(1, BASE);
+
+        private static float computeLog(float t, int base) {
+            return (float) (1 - Math.pow(base, -t));
+        }
+
+        @Override
+        public float getInterpolation(float t) {
+            return 1 - computeLog(1 - t, BASE) * LOGS_SCALE;
+        }
+    }
+
+    /**
+     * A helper for generating views for the overflow panel.
+     */
+    private static final class OverflowPanelViewHelper {
+
+        private final View mCalculator;
+        private final int mIconTextSpacing;
+        private final int mSidePadding;
+
+        private final Context mContext;
+
+        OverflowPanelViewHelper(Context context, int iconTextSpacing) {
+            mContext = Objects.requireNonNull(context);
+            mIconTextSpacing = iconTextSpacing;
+            mSidePadding = context.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_overflow_side_padding);
+            mCalculator = createMenuButton(null);
+        }
+
+        public View getView(MenuItem menuItem, int minimumWidth, View convertView) {
+            Objects.requireNonNull(menuItem);
+            if (convertView != null) {
+                updateMenuItemButton(
+                        convertView, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            } else {
+                convertView = createMenuButton(menuItem);
+            }
+            convertView.setMinimumWidth(minimumWidth);
+            return convertView;
+        }
+
+        public int calculateWidth(MenuItem menuItem) {
+            updateMenuItemButton(
+                    mCalculator, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            mCalculator.measure(
+                    View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+            return mCalculator.getMeasuredWidth();
+        }
+
+        private View createMenuButton(MenuItem menuItem) {
+            View button = createMenuItemButton(
+                    mContext, menuItem, mIconTextSpacing, shouldShowIcon(menuItem));
+            button.setPadding(mSidePadding, 0, mSidePadding, 0);
+            return button;
+        }
+
+        private boolean shouldShowIcon(MenuItem menuItem) {
+            if (menuItem != null) {
+                return menuItem.getGroupId() == android.R.id.textAssist;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Creates and returns a menu button for the specified menu item.
+     */
+    private static View createMenuItemButton(
+            Context context, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
+        final View menuItemButton = LayoutInflater.from(context)
+                .inflate(R.layout.floating_popup_menu_button, null);
+        if (menuItem != null) {
+            updateMenuItemButton(menuItemButton, menuItem, iconTextSpacing, showIcon);
+        }
+        return menuItemButton;
+    }
+
+    /**
+     * Updates the specified menu item button with the specified menu item data.
+     */
+    private static void updateMenuItemButton(
+            View menuItemButton, MenuItem menuItem, int iconTextSpacing, boolean showIcon) {
+        final TextView buttonText = menuItemButton.findViewById(
+                R.id.floating_toolbar_menu_item_text);
+        buttonText.setEllipsize(null);
+        if (TextUtils.isEmpty(menuItem.getTitle())) {
+            buttonText.setVisibility(View.GONE);
+        } else {
+            buttonText.setVisibility(View.VISIBLE);
+            buttonText.setText(menuItem.getTitle());
+        }
+        final ImageView buttonIcon = menuItemButton.findViewById(
+                R.id.floating_toolbar_menu_item_image);
+        if (menuItem.getIcon() == null || !showIcon) {
+            buttonIcon.setVisibility(View.GONE);
+            if (buttonText != null) {
+                buttonText.setPaddingRelative(0, 0, 0, 0);
+            }
+        } else {
+            buttonIcon.setVisibility(View.VISIBLE);
+            buttonIcon.setImageDrawable(menuItem.getIcon());
+            if (buttonText != null) {
+                buttonText.setPaddingRelative(iconTextSpacing, 0, 0, 0);
+            }
+        }
+        final CharSequence contentDescription = menuItem.getContentDescription();
+        if (TextUtils.isEmpty(contentDescription)) {
+            menuItemButton.setContentDescription(menuItem.getTitle());
+        } else {
+            menuItemButton.setContentDescription(contentDescription);
+        }
+    }
+
+    private static ViewGroup createContentContainer(Context context) {
+        ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
+                .inflate(R.layout.floating_popup_container, null);
+        contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
+        contentContainer.setClipToOutline(true);
+        return contentContainer;
+    }
+
+    private static PopupWindow createPopupWindow(ViewGroup content) {
+        ViewGroup popupContentHolder = new LinearLayout(content.getContext());
+        PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+        // TODO: Use .setIsLaidOutInScreen(true) instead of .setClippingEnabled(false)
+        // unless FLAG_LAYOUT_IN_SCREEN has any unintentional side-effects.
+        popupWindow.setClippingEnabled(false);
+        popupWindow.setWindowLayoutType(
+                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
+        popupWindow.setAnimationStyle(0);
+        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        content.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        popupContentHolder.addView(content);
+        return popupWindow;
+    }
+
+    /**
+     * Creates an "appear" animation for the specified view.
+     *
+     * @param view  The view to animate
+     */
+    private static AnimatorSet createEnterAnimation(View view) {
+        AnimatorSet animation = new AnimatorSet();
+        animation.playTogether(
+                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150));
+        return animation;
+    }
+
+    /**
+     * Creates a "disappear" animation for the specified view.
+     *
+     * @param view  The view to animate
+     * @param startDelay  The start delay of the animation
+     * @param listener  The animation listener
+     */
+    private static AnimatorSet createExitAnimation(
+            View view, int startDelay, Animator.AnimatorListener listener) {
+        AnimatorSet animation =  new AnimatorSet();
+        animation.playTogether(
+                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
+        animation.setStartDelay(startDelay);
+        animation.addListener(listener);
+        return animation;
+    }
+
+    /**
+     * Returns a re-themed context with controlled look and feel for views.
+     */
+    private static Context applyDefaultTheme(Context originalContext) {
+        TypedArray a = originalContext.obtainStyledAttributes(new int[]{R.attr.isLightTheme});
+        boolean isLightTheme = a.getBoolean(0, true);
+        int themeId =
+                isLightTheme ? R.style.Theme_DeviceDefault_Light : R.style.Theme_DeviceDefault;
+        a.recycle();
+        return new ContextThemeWrapper(originalContext, themeId);
+    }
+
+    /**
+     * Represents the identity of a MenuItem that is rendered in a FloatingToolbarPopup.
+     */
+    @VisibleForTesting
+    public static final class MenuItemRepr {
+
+        public final int itemId;
+        public final int groupId;
+        @Nullable public final String title;
+        @Nullable private final Drawable mIcon;
+
+        private MenuItemRepr(
+                int itemId, int groupId, @Nullable CharSequence title, @Nullable Drawable icon) {
+            this.itemId = itemId;
+            this.groupId = groupId;
+            this.title = (title == null) ? null : title.toString();
+            mIcon = icon;
+        }
+
+        /**
+         * Creates an instance of MenuItemRepr for the specified menu item.
+         */
+        public static MenuItemRepr of(MenuItem menuItem) {
+            return new MenuItemRepr(
+                    menuItem.getItemId(),
+                    menuItem.getGroupId(),
+                    menuItem.getTitle(),
+                    menuItem.getIcon());
+        }
+
+        /**
+         * Returns this object's hashcode.
+         */
+        @Override
+        public int hashCode() {
+            return Objects.hash(itemId, groupId, title, mIcon);
+        }
+
+        /**
+         * Returns true if this object is the same as the specified object.
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (!(o instanceof MenuItemRepr)) {
+                return false;
+            }
+            final MenuItemRepr other = (MenuItemRepr) o;
+            return itemId == other.itemId
+                    && groupId == other.groupId
+                    && TextUtils.equals(title, other.title)
+                    // Many Drawables (icons) do not implement equals(). Using equals() here instead
+                    // of reference comparisons in case a Drawable subclass implements equals().
+                    && Objects.equals(mIcon, other.mIcon);
+        }
+
+        /**
+         * Returns true if the two menu item collections are the same based on MenuItemRepr.
+         */
+        public static boolean reprEquals(
+                Collection<MenuItem> menuItems1, Collection<MenuItem> menuItems2) {
+            if (menuItems1.size() != menuItems2.size()) {
+                return false;
+            }
+
+            final Iterator<MenuItem> menuItems2Iter = menuItems2.iterator();
+            for (MenuItem menuItem1 : menuItems1) {
+                final MenuItem menuItem2 = menuItems2Iter.next();
+                if (!MenuItemRepr.of(menuItem1).equals(MenuItemRepr.of(menuItem2))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index d4fb3e3..a7362ab 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -30,43 +30,6 @@
 
 namespace android {
 
-struct {
-    jmethodID onTransactionComplete;
-} gTransactionCompleteCallback;
-
-class TransactionCompleteCallbackWrapper : public LightRefBase<TransactionCompleteCallbackWrapper> {
-public:
-    explicit TransactionCompleteCallbackWrapper(JNIEnv* env, jobject jobject) {
-        env->GetJavaVM(&mVm);
-        mTransactionCompleteObject = env->NewGlobalRef(jobject);
-        LOG_ALWAYS_FATAL_IF(!mTransactionCompleteObject, "Failed to make global ref");
-    }
-
-    ~TransactionCompleteCallbackWrapper() {
-        if (mTransactionCompleteObject) {
-            getenv()->DeleteGlobalRef(mTransactionCompleteObject);
-            mTransactionCompleteObject = nullptr;
-        }
-    }
-
-    void onTransactionComplete(int64_t frameNr) {
-        if (mTransactionCompleteObject) {
-            getenv()->CallVoidMethod(mTransactionCompleteObject,
-                                     gTransactionCompleteCallback.onTransactionComplete, frameNr);
-        }
-    }
-
-private:
-    JavaVM* mVm;
-    jobject mTransactionCompleteObject;
-
-    JNIEnv* getenv() {
-        JNIEnv* env;
-        mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
-        return env;
-    }
-};
-
 static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
                           jlong width, jlong height, jint format) {
     String8 str8;
@@ -119,21 +82,6 @@
     queue->mergeWithNextTransaction(transaction, framenumber);
 }
 
-static void nativeSetTransactionCompleteCallback(JNIEnv* env, jclass clazz, jlong ptr,
-                                                 jlong frameNumber,
-                                                 jobject transactionCompleteCallback) {
-    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
-    if (transactionCompleteCallback == nullptr) {
-        queue->setTransactionCompleteCallback(frameNumber, nullptr);
-    } else {
-        sp<TransactionCompleteCallbackWrapper> wrapper =
-                new TransactionCompleteCallbackWrapper{env, transactionCompleteCallback};
-        queue->setTransactionCompleteCallback(frameNumber, [wrapper](int64_t frameNr) {
-            wrapper->onTransactionComplete(frameNr);
-        });
-    }
-}
-
 static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) {
     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
     return queue->getLastAcquiredFrameNum();
@@ -153,9 +101,6 @@
         {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
         {"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
         {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
-        {"nativeSetTransactionCompleteCallback",
-                "(JJLandroid/graphics/BLASTBufferQueue$TransactionCompleteCallback;)V",
-                (void*)nativeSetTransactionCompleteCallback},
         {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
         {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
         // clang-format on
@@ -165,11 +110,6 @@
     int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
             gMethods, NELEM(gMethods));
     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
-
-    jclass transactionCompleteClass =
-            FindClassOrDie(env, "android/graphics/BLASTBufferQueue$TransactionCompleteCallback");
-    gTransactionCompleteCallback.onTransactionComplete =
-            GetMethodIDOrDie(env, transactionCompleteClass, "onTransactionComplete", "(J)V");
     return 0;
 }
 
diff --git a/core/jni/android_graphics_SurfaceTexture.cpp b/core/jni/android_graphics_SurfaceTexture.cpp
index 0909ce7..2f12289 100644
--- a/core/jni/android_graphics_SurfaceTexture.cpp
+++ b/core/jni/android_graphics_SurfaceTexture.cpp
@@ -147,8 +147,7 @@
     virtual void onFrameAvailable(const BufferItem& item);
 
 private:
-    static JNIEnv* getJNIEnv(bool* needsDetach);
-    static void detachJNI();
+    static JNIEnv* getJNIEnv();
 
     jobject mWeakThiz;
     jclass mClazz;
@@ -160,58 +159,40 @@
     mClazz((jclass)env->NewGlobalRef(clazz))
 {}
 
-JNIEnv* JNISurfaceTextureContext::getJNIEnv(bool* needsDetach) {
-    *needsDetach = false;
+JNIEnv* JNISurfaceTextureContext::getJNIEnv() {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     if (env == NULL) {
         JavaVMAttachArgs args = {
             JNI_VERSION_1_4, "JNISurfaceTextureContext", NULL };
         JavaVM* vm = AndroidRuntime::getJavaVM();
-        int result = vm->AttachCurrentThread(&env, (void*) &args);
+        int result = vm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
         if (result != JNI_OK) {
             ALOGE("thread attach failed: %#x", result);
             return NULL;
         }
-        *needsDetach = true;
     }
     return env;
 }
 
-void JNISurfaceTextureContext::detachJNI() {
-    JavaVM* vm = AndroidRuntime::getJavaVM();
-    int result = vm->DetachCurrentThread();
-    if (result != JNI_OK) {
-        ALOGE("thread detach failed: %#x", result);
-    }
-}
-
 JNISurfaceTextureContext::~JNISurfaceTextureContext()
 {
-    bool needsDetach = false;
-    JNIEnv* env = getJNIEnv(&needsDetach);
+    JNIEnv* env = getJNIEnv();
     if (env != NULL) {
         env->DeleteGlobalRef(mWeakThiz);
         env->DeleteGlobalRef(mClazz);
     } else {
         ALOGW("leaking JNI object references");
     }
-    if (needsDetach) {
-        detachJNI();
-    }
 }
 
 void JNISurfaceTextureContext::onFrameAvailable(const BufferItem& /* item */)
 {
-    bool needsDetach = false;
-    JNIEnv* env = getJNIEnv(&needsDetach);
+    JNIEnv* env = getJNIEnv();
     if (env != NULL) {
         env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
     } else {
         ALOGW("onFrameAvailable event will not posted");
     }
-    if (needsDetach) {
-        detachJNI();
-    }
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 0d33807..be9aaaf 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -641,7 +641,7 @@
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel != NULL) {
         bool result;
-        status_t err = parcel->hasFileDescriptorsInRange(offset, length, result);
+        status_t err = parcel->hasFileDescriptorsInRange(offset, length, &result);
         if (err != NO_ERROR) {
             signalExceptionForError(env, clazz, err);
             return JNI_FALSE;
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 6f5cc53..40f6e4f 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -138,6 +138,14 @@
         return true;
     }
 
+    // Allow Runtime Resource Overlays inside APEXes.
+    static const char* kOverlayPathSuffix = "/overlay";
+    if (android::base::StartsWith(path, kApexPrefix) &&
+        android::base::EndsWith(android::base::Dirname(path), kOverlayPathSuffix) &&
+        android::base::EndsWith(path, kApkSuffix) && path.find("/../") == std::string::npos) {
+        return true;
+    }
+
     static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
     static const char* kOverlayIdmapSuffix = ".apk@idmap";
     if (android::base::StartsWith(path, kOverlayIdmapPrefix) &&
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index bc6a090..40e3f60 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -366,6 +366,7 @@
     optional bool translucent = 30;
     optional bool pip_auto_enter_enabled = 31;
     optional bool in_size_compat_mode = 32;
+    optional float min_aspect_ratio = 33;
 }
 
 /* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5a53374..329f02a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -256,6 +256,9 @@
         android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONF_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.action.TETHERING_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" />
@@ -2976,6 +2979,17 @@
     <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH"
                 android:protectionLevel="normal" />
 
+    <!-- Allows application to request to be associated with a virtual display capable of streaming
+         Android applications
+         ({@link android.companion.AssociationRequest#DEVICE_PROFILE_APP_STREAMING})
+         by {@link android.companion.CompanionDeviceManager}.
+        <p>Not for use by third-party applications.
+         @hide
+         @SystemApi
+    -->
+    <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Allows a companion app to associate to Wi-Fi.
          <p>Only for use by a single pre-approved app.
          @hide
@@ -4926,15 +4940,15 @@
                 android:protectionLevel="signature|privileged" />
 
     <!-- An application needs this permission for
-         {@link android.provider.Settings#ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK} to show its
-         {@link android.app.Activity} in 2-pane of Settings app. -->
-    <permission android:name="android.permission.LAUNCH_TWO_PANE_SETTINGS_DEEP_LINK"
+         {@link android.provider.Settings#ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY} to show its
+         {@link android.app.Activity} embedded in Settings app. -->
+    <permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK"
                 android:protectionLevel="signature|preinstalled" />
 
     <!-- @SystemApi {@link android.app.Activity} should require this permission to ensure that only
-         the settings app can embed it in a 2-pane window.
+         the settings app can embed it in a multi pane window.
          @hide -->
-    <permission android:name="android.permission.ALLOW_PLACE_IN_TWO_PANE_SETTINGS"
+    <permission android:name="android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS"
                 android:protectionLevel="signature" />
 
     <!-- @SystemApi Allows applications to set a live wallpaper.
@@ -5333,6 +5347,15 @@
         android:description="@string/permdesc_startViewPermissionUsage"
         android:protectionLevel="signature|installer" />
 
+    <!--
+        Allows the holder to start the screen with a list of app features.
+        <p>Protection level: signature|installer
+    -->
+    <permission android:name="android.permission.START_VIEW_APP_FEATURES"
+                android:label="@string/permlab_startViewAppFeatures"
+                android:description="@string/permdesc_startViewAppFeatures"
+                android:protectionLevel="signature|installer" />
+
     <!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT
          flag is set.
          @hide -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 684202b..165dcad 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -28,3 +28,6 @@
 
 # Multiuser
 per-file res/xml/config_user_types.xml = file:/MULTIUSER_OWNERS
+
+# Car
+per-file res/values/dimens_car.xml = file:/platform/packages/services/Car:/OWNERS
\ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 76f4915..c592ef4 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Laat die program toe om op Bluetooth-toestelle in die omtrek te adverteer"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"bepaal relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Laat die program toe om relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek te bepaal"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interaksie met wi‑fi-toestelle in die omtrek te hê"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Laat die program toe om op toestelle in die omtrek te adverteer, aan hulle te koppel en hul relatiewe posisie te bepaal"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Voorkeur-NFC-betalingdiensinligting"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Laat die program toe om voorkeur-NFC-betalingdiensinligting soos geregistreerde hulpmiddels en roetebestemming te kry."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"beheer kortveldkommunikasie"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die program toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"begin kyk van toestemminggebruik"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n program te begin. Behoort nooit vir normale programme nodig te wees nie."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"begin Bekyk Programkenmerke"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Laat die houer toe om die kenmerke-inligting vir \'n program te begin bekyk."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"kry toegang tot sensordata teen \'n hoë monsternemingkoers"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Laat die program toe om monsters van sensordata teen \'n hoër koers as 200 Hz te neem"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Stel wagwoordreëls"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 21c21a9..74fcfc6 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"በአቅራቢያ ላሉ የብሉቱዝ መሣሪያዎችን እንዲያስተዋውቅ መተግበሪያው ያስችለዋል"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"በአቅራቢያ ባሉ ልዕለ ሰፊ ባንድ መሣሪያዎች መካከል ተዛማጅ የሆነውን ቦታ ይወቁ"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"በአቅራቢያ ባሉ ልዕለ-ሰፊ ባንድ መሣሪያዎች መካከል ያለውን አንጻራዊ አቀማመጣቸውን ለማወቅ ንዲችል ለመተግበሪያው ይፍቀዱ"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"በአቅራቢያ ካሉ የWi‑Fi መሣሪያዎች ጋር መስተጋብር መፍጠር"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"መተግበሪያው በአቅራቢያ ያሉ የWi-Fi መሣሪያዎች አንጻራዊ ቦታን እንዲያሳውቅ፣ እንዲያገናኝ እና እንዲያውቅ ያስችለዋል"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ተመራጭ NFC የክፍያ አገልግሎት መረጃ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"እንደ የተመዘገቡ እርዳታዎች እና የጉዞ መሥመር መዳረሻ የመሳሰለ ተመራጭ nfc የክፍያ አገልግሎት መረጃን ለማግኘት ለመተግበሪያው ያፈቅድለታል።"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ቅርብ የግኑኙነትመስክ (NFC) ተቆጣጠር"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"መተግበሪያው የአትረብሽ ውቅረትን እንዲያነብብ እና እንዲጸፍ ይፈቅዳል።"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"የእይታ ፈቃድ መጠቀምን መጀመር"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ያዢው ለአንድ መተግበሪያ የፈቃድ አጠቃቀሙን እንዲያስጀምር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"የመተግበሪያ ባህሪያትን ማየት መጀመር"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ያዢው የአንድ መተግበሪያ የባህሪያት መረጃን ማየት እንዲጀምር ያስችለዋል።"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"የዳሳሽ ውሂቡን በከፍተኛ የናሙና ብዛት ላይ ይድረሱበት"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"መተግበሪያው የዳሳሽ ውሂቡን ከ200 ኸ በሚበልጥ ፍጥነት ናሙና እንዲያደርግ ይፈቅድለታል"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"የይለፍ ቃል ደንቦች አዘጋጅ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 14a1ae5..363f833e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -555,6 +555,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"للسماح للتطبيق بعرض إعلانات على الأجهزة القريبة التي تتضمّن بلوتوث."</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"تحديد الموضع النسبي بين الأجهزة المجاورة التي تستخدم النطاق الواسع جدًا"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"يسمح هذا الإذن للتطبيق بتحديد الموضع النسبي بين الأجهزة المجاورة التي تستخدم النطاق الواسع جدًا."</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏التفاعل مع أجهزة Wi‑Fi المجاورة"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏للسماح للتطبيق بالإعلان والربط وتحديد الموقع النسبي لأجهزة Wi-Fi المجاورة."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏يسمح هذا الإذن للتطبيق بالحصول على معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل، مثلاً المساعدات المسجّلة ووجهة المسار."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"التحكم في اتصال الحقل القريب"</string>
@@ -738,6 +740,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"بدء عرض ميزات التطبيق"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"للسماح للمالك ببدء عرض معلومات عن ميزات التطبيق."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"الوصول إلى بيانات جهاز الاستشعار بمعدّل مرتفع للبيانات في الملف الصوتي"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"يسمح هذا الأذن للتطبيق بزيادة بيانات جهاز الاستشعار بمعدّل بيانات في الملف الصوتي أكبر من 200 هرتز."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"تعيين قواعد كلمة المرور"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 7992f13..7dbeabf 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -291,12 +291,12 @@
     <string name="notification_channel_retail_mode" msgid="3732239154256431213">"খুচুৰা ডেম\'"</string>
     <string name="notification_channel_usb" msgid="1528280969406244896">"ইউএছবি সংযোগ"</string>
     <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"এপ্ চলি আছে"</string>
-    <string name="notification_channel_foreground_service" msgid="7102189948158885178">"বেটাৰি খৰচ কৰা এপসমূহ"</string>
+    <string name="notification_channel_foreground_service" msgid="7102189948158885178">"বেটাৰী খৰচ কৰা এপ্‌সমূহ"</string>
     <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"বিবৰ্ধন"</string>
     <string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"সাধ্য সুবিধাৰ ব্যৱহাৰ"</string>
-    <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বেটাৰি ব্যৱহাৰ কৰি আছে"</string>
-    <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>টা এপে বেটাৰি ব্যৱহাৰ কৰি আছে"</string>
-    <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"বেটাৰি আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে বিশদভাৱে জানিবলৈ টিপক"</string>
+    <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বেটাৰী ব্যৱহাৰ কৰি আছে"</string>
+    <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>টা এপে বেটাৰী ব্যৱহাৰ কৰি আছে"</string>
+    <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"বেটাৰী আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে সবিশেষ জানিবলৈ টিপক"</string>
     <string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
     <string name="safeMode" msgid="8974401416068943888">"সুৰক্ষিত ম\'ড"</string>
     <string name="android_system_label" msgid="5974767339591067210">"Android ছিষ্টেম"</string>
@@ -391,7 +391,7 @@
     <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"এই এপটো অইন এপৰ ওপৰত প্ৰদৰ্শিত হ\'ব পাৰে"</string>
     <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"এই এপ্‌টো অন্য এপৰ ওপৰত বা স্ক্ৰীনৰ অন্য অংশত প্ৰদৰ্শিত হ\'ব পাৰে। এই কাৰ্যই এপৰ স্বাভাৱিক ব্যৱহাৰত ব্যাঘাত জন্মাব পাৰে আৰু অন্য এপ্‌সমূহক স্ক্ৰীনত কেনেকৈ দেখা পোৱা যায় সেইটো সলনি কৰিব পাৰে।"</string>
     <string name="permlab_runInBackground" msgid="541863968571682785">"নেপথ্যত চলিব পাৰে"</string>
-    <string name="permdesc_runInBackground" msgid="4344539472115495141">"এই এপটো নেপথ্যত চলিব পাৰে। ইয়াৰ ফলত বেটাৰি সোনকালে শেষ হ\'ব পাৰে।"</string>
+    <string name="permdesc_runInBackground" msgid="4344539472115495141">"এই এপ্‌টো নেপথ্যত চলিব পাৰে। ইয়াৰ ফলত বেটাৰী সোনকালে শেষ হ’ব পাৰে।"</string>
     <string name="permlab_useDataInBackground" msgid="783415807623038947">"নেপথ্যত ডেটা ব্যৱহাৰ কৰিব পাৰে"</string>
     <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"এই এপটোৱে নেপথ্যত ডেটা ব্যৱহাৰ কৰিব পাৰে। ইয়াৰ ফলত ডেটা বেছি খৰছ হ\'ব পাৰে।"</string>
     <string name="permlab_persistentActivity" msgid="464970041740567970">"এপক সদায়ে চলি থকা কৰক"</string>
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"এপ্‌টোক নিকটৱৰ্তী ব্লুটুথ ডিভাইচত বিজ্ঞাপন প্ৰচাৰ কৰিবলৈ দিয়ে"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"নিকটৱৰ্তী আল্ট্ৰা-ৱাইডবেণ্ড ডিভাইচৰ মাজৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰক"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"এপ্‌টোক নিকটৱৰ্তী আল্ট্ৰা-ৱাইডবেণ্ড ডিভাইচসমূহৰ মাজৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ক"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"নিকটৱৰ্তী ৱাই-ফাই ডিভাইচসমূহৰ সৈতে ভাব বিনিময় কৰক"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"এপ্‌টোক বিজ্ঞাপন প্ৰচাৰ কৰিবলৈ, সংযোগ কৰিবলৈ আৰু নিকটৱৰ্তী ৱাই-ফাই ডিভাইচৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"অগ্ৰাধিকাৰ দিয়া NFC পৰিশোধ সেৱাৰ তথ্য"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"এপ্‌টোক অগ্ৰাধিকাৰ দিয়া nfc পৰিশোধ সেৱাৰ পঞ্জীকৃত সহায়কসমূহ আৰু পৰিশোধ কৰিব লগা লক্ষ্যস্থান দৰে তথ্য পাবলৈ অনুমতি দিয়ে।"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"নিয়েৰ ফিল্ড কমিউনিকেশ্বন নিয়ন্ত্ৰণ কৰক"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অসুবিধা নিদিবৰ কনফিগাৰেশ্বনক পঢ়িবলৈ আৰু সালসলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"চোৱাৰ অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰক"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ধাৰকক কোনো এপৰ বাবে অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰিবলৈ দিয়ে। সাধাৰণ এপ্‌সমূহৰ বাবে কেতিয়াও প্ৰয়োজন হ’ব নালাগে।"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"এপৰ সুবিধাসমূহৰ সম্পর্কীয় তথ্য চোৱাটো আৰম্ভ কৰক"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ধাৰকক কোনো এপৰ সুবিধাসমূহৰ সম্পর্কীয় তথ্য চোৱাটো আৰম্ভ কৰিবলৈ দিয়ে।"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"এটা উচ্চ ছেম্পলিঙৰ হাৰত ছেন্সৰৰ ডেটা এক্সেছ কৰে"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"এপ্‌টোক ২০০ হাৰ্টজতকৈ অধিক হাৰত ছেন্সৰৰ ডেটাৰ নমুনা ল’বলৈ অনুমতি দিয়ে"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"পাছৱর্ডৰ নিয়ম ছেট কৰক"</string>
@@ -1467,8 +1471,8 @@
     <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"পেকেজ ইনষ্টল কৰাৰ অনুৰোধ প্ৰেৰণ কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
     <string name="permlab_requestDeletePackages" msgid="2541172829260106795">"পেকেজ মচাৰ অনুৰোধ কৰিব পাৰে"</string>
     <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"এপটোক পেকেজবোৰ মচাৰ অনুৰোধ কৰিবলৈ দিয়ে।"</string>
-    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"বেটাৰি অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ বিচাৰক"</string>
-    <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো এপক সেই এপটোৰ বাবে বেটাৰি অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ অনুমতি বিচাৰিবলৈ দিয়ে।"</string>
+    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"বেটাৰী অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ বিচাৰক"</string>
+    <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো এপক সেই এপ্‌টোৰ বাবে বেটাৰী অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ অনুমতি বিচাৰিবলৈ দিয়ে।"</string>
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"আটাইবোৰ পেকেজত প্ৰশ্ন সোধক"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"এপক আটাইবোৰ ইনষ্টল কৰি থোৱা পেকেজ চাবলৈ দিয়ে।"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্ৰণ কৰিবলৈ দুবাৰ টিপক"</string>
@@ -2108,10 +2112,10 @@
     <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"অধিক জানক"</string>
     <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12ত Androidৰ অভিযোজিত জাননীক উন্নত জাননীৰ দ্বাৰা সলনি কৰা হৈছে। এই সুবিধাটোৱে পৰামৰ্শ দিয়া কাৰ্য আৰু প্ৰত্যুত্তৰ দেখুৱায় আৰু আপোনাৰ জাননীসমূহ শৃংখলাবদ্ধ কৰে।\n\nউন্নত জাননীয়ে সম্পৰ্কৰ নাম আৰু বাৰ্তাৰ দৰে ব্যক্তিগত তথ্যকে ধৰি জাননীৰ সমল এক্সেছ কৰিব পাৰে। এই সুবিধাটোৱে জাননী অগ্ৰাহ্য কৰিব অথবা জাননীৰ প্ৰতি সঁহাৰি জনাবও পাৰে, যেনে ফ’ন কলৰ উত্তৰ দিয়া আৰু অসুবিধা নিদিব সুবিধাটো নিয়ন্ত্ৰণ কৰা আদি।"</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ৰুটিন ম’ডৰ তথ্য জাননী"</string>
-    <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"চ্চাৰ্জ কৰাৰ সচৰাচৰ সময়ৰ আগতেই বেটাৰি শেষ হ’ব পাৰে"</string>
-    <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"বেটাৰিৰ খৰচ কমাবলৈ বেটাৰি সঞ্চয়কাৰী অন কৰা হৈছে"</string>
+    <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"চাৰ্জ কৰাৰ সচৰাচৰ সময়ৰ আগতেই বেটাৰী শেষ হ’ব পাৰে"</string>
+    <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"বেটাৰীৰ খৰচ কমাবলৈ বেটাৰী সঞ্চয়কাৰী অন কৰা হৈছে"</string>
     <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"বেটাৰী সঞ্চয়কাৰী"</string>
-    <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"বেটাৰি সঞ্চয়কাৰী অফ কৰা হ’ল"</string>
+    <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"বেটাৰী সঞ্চয়কাৰী অফ কৰা হ’ল"</string>
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"ফ\'নটোত পর্যাপ্ত পৰিমাণে চার্জ আছে। সুবিধাবোৰ আৰু সীমাবদ্ধ কৰা নাই।"</string>
     <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"টেবলেটটোত পর্যাপ্ত পৰিমাণে চার্জ আছে। সুবিধাবোৰ আৰু সীমাবদ্ধ কৰা নাই।"</string>
     <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"ডিভাইচটোত পর্যাপ্ত পৰিমাণে চার্জ আছে। সুবিধাবোৰ আৰু সীমাবদ্ধ কৰা নাই।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index d269032..e949a53 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -543,6 +543,10 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Tətbiqə yaxınlıqdakı Bluetooth cihazlarında reklam etmək imkanı verir"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"yaxınlıqdakı Ultra Genişzolaqlı cihazları arasında nisbi mövqeyi təyin etmək"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tətbiqə yaxınlıqdakı Ultra Genişzolaqlı cihazları arasında nisbi mövqeyi təyin etməyə icazə verin"</string>
+    <!-- no translation found for permlab_nearby_wifi_devices (392774237063608500) -->
+    <skip />
+    <!-- no translation found for permdesc_nearby_wifi_devices (3054307728646332906) -->
+    <skip />
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Tərcih edilən NFC ödəniş xidməti məlumatı"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tətbiqə qeydiyyatdan keçmiş yardım və marşrut təyinatı kimi tərcih edilən nfc ödəniş xidməti məlumatını əldə etmək icazəsi verir."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication\'ı kontrol et"</string>
@@ -726,6 +730,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Tətbiqə \"Narahat Etməyin\" konfiqurasiyasını oxumağa və yazmağa icazə verin."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Baxış icazəsinin istifadəsinə başlayın"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Sahibinə tətbiqin icazədən istifadəsinə başlamağa imkan verir. Adi tətbiqlər üçün heç vaxt tələb edilmir."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"tətbiqin funksiyalarını görməyə başlamaq"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"İstifadəçinin tətbiqin funksiyaları barədə məlumatları görməyə başlamasına icazə verir."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"sensor datasına yüksək ölçmə sürəti ilə giriş etmək"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tətbiqə sensor datasını 200 Hz-dən yüksək sürətlə ölçməyə imkan verir"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Parol qaydalarını təyin edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 1b10856..e7968e5 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -546,6 +546,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Dozvoljava aplikaciji da se oglašava na Bluetooth uređajima u blizini"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"određivanje razdaljine između uređaja ultra-širokog pojasa u blizini"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dozvoljava aplikaciji da određuje relativnu razdaljinu između uređaja ultra-širokog pojasa u blizini"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcija sa WiFi uređajima u blizini"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Dozvoljava aplikaciji da se oglašava, povezuje i utvrđuje relativnu poziciju WiFi uređaja u blizini"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o željenoj NFC usluzi za plaćanje"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Dozvoljava aplikaciji da preuzima informacije o željenoj NFC usluzi za plaćanje, poput registrovanih identifikatora aplikacija i odredišta preusmeravanja."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrola komunikacije u užem polju (Near Field Communication)"</string>
@@ -729,6 +731,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Dozvoljava aplikaciji da čita i upisuje konfiguraciju podešavanja Ne uznemiravaj."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"početak korišćenja dozvole za pregled"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da započne korišćenje dozvole za aplikaciju. Nikada ne bi trebalo da bude potrebna za uobičajene aplikacije."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"pokretanje prikaza funkcija aplikacije"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Dozvoljava korisniku da započne pregledanje informacija o funkcijama aplikacije."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri velikoj brzini uzorkovanja"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Dozvoljava aplikaciji da uzima uzorak podataka senzora pri brzini većoj od 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Podešavanje pravila za lozinku"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index d4cd127..b06dbd9 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Дазволіць праграме адпраўляць рэкламу на прылады з Bluetooth, якія знаходзяцца паблізу"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"вызначаць адлегласць паміж прыладамі з звышшырокапалоснай сувяззю"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дазволіць праграме вызначаць адлегласць паміж прыладамі паблізу, якія выкарыстоўваюць звышшырокапалосную сувязь"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Узаемадзеянне з прыладамі з Wi‑Fi паблізу"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Праграма зможа адпраўляць даныя на прылады Wi-Fi паблізу, падключацца да іх і вызначаць іх месцазнаходжанне"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Інфармацыя пра прыярытэтны сэрвіс аплаты NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дазваляе праграме атрымаць доступ да інфармацыі пра прыярытэтны сэрвіс аплаты NFC, напрыклад зарэгістраваныя ідэнтыфікатары праграм і маршруты адпраўкі даных."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"кантроль Near Field Communication"</string>
@@ -732,6 +734,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дазваляе праграме чытаць і выконваць запіс у канфігурацыю рэжыму «Не турбаваць»."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"запусціць выкарыстанне дазволаў на прагляд"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дазваляе трымальніку запусціць выкарыстанне дазволаў праграмай. Не патрэбна для звычайных праграм."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"запусціць прагляд функцый праграмы"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Дазваляе трымальніку запусціць прагляд інфармацыі пра функцыі для праграмы."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"атрымліваць даныя датчыка з высокай частатой дыскрэтызацыі"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Праграма зможа распазнаваць даныя датчыка з частатой звыш 200 Гц"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Устанавіць правілы паролю"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 1b963f6..256ab09 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Разрешава на приложението да рекламира на устройства с Bluetooth в близост"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"опред. на относителната позиция м/у у-вата с ултрашироколентови сигнали в близост"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Разрешаване на приложението да определя относителната позиция между устройствата с ултрашироколентови сигнали в близост"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Взаимодействие с устройствата с Wi-Fi в близост"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Разрешава на приложението да рекламира, да се свързва и да определя относителната позиция на у-вата с Wi-Fi в близост"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информация за предпочитаната услуга за плащане чрез NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дава възможност на приложението да получава информация за предпочитаната услуга за плащане чрез NFC, като например регистрирани помощни средства и местоназначение."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"контролиране на комуникацията в близкото поле"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Предоставя на приложението достъп за четене и запис до конфигурацията на „Не безпокойте“."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"стартиране на прегледа на използваните разрешения"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Разрешава на притежателя да стартира прегледа на използваните разрешения за дадено приложение. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"стартиране на прегледа на функциите на приложението"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Разрешава на притежателя да стартира прегледа на информацията за функциите на дадено приложение."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"осъществяване на достъп до данните от сензорите при висока скорост на семплиране"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Разрешава на приложението да семплира данните от сензорите със скорост, по-висока от 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Задаване на правила за паролата"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 0f47e29..9f29d8e 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -543,6 +543,10 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"আশেপাশের ব্লুটুথ ডিভাইস বিজ্ঞাপন দেওয়ার জন্য অ্যাপকে অনুমতি দেয়"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"আশেপাশের Ultra-Wideband ডিভাইসগুলির আপেক্ষিক অবস্থান নির্ণয় করুন"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"অ্যাপকে আশেপাশের Ultra-Wideband ডিভাইসগুলির আপেক্ষিক অবস্থান নির্ণয় করার অনুমতি দিন"</string>
+    <!-- no translation found for permlab_nearby_wifi_devices (392774237063608500) -->
+    <skip />
+    <!-- no translation found for permdesc_nearby_wifi_devices (3054307728646332906) -->
+    <skip />
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"পছন্দের NFC পেমেন্ট পরিষেবার তথ্য"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"অ্যাপের মাধ্যমে পছন্দসই এনএফসি পেমেন্ট পরিষেবার তথ্য, যেমন রেজিস্ট্রার করার সহায়তা এবং রুট ডেস্টিনেশন সম্পর্কিত তথ্য অ্যাক্সেস করার অনুমতি দেয়।"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"নিয়ার ফিল্ড কমিউনিকেশন নিয়ন্ত্রণ করে"</string>
@@ -726,6 +730,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অ্যাপটিকে \'বিরক্ত করবে না\' কনফিগারেশন পড়া এবং লেখার অনুমতি দেয়।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"দেখার অনুমতি কাজে লাগানো শুরু করুন"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"কোনও অ্যাপের কোনও নির্দিষ্ট অনুমতির ব্যবহার শুরু করার ক্ষেত্রে হোল্ডারকে সাহায্য করে। সাধারণ অ্যাপের জন্য এটির পরিবর্তন হওয়ার কথা নয়।"</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"হাই স্যাম্পলিং রেটে সেন্সর ডেটা অ্যাক্সেস করুন"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz-এর বেশি রেটে অ্যাপকে স্যাম্পল সেন্সর ডেটার জন্য অনুমতি দিন"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"পাসওয়ার্ড নিয়মগুলি সেট করে"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 32bc006..e6bfadcb 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -546,6 +546,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Dozvoljava aplikaciji da vrši oglašavanje na Bluetooth uređajima u blizini"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"određivanje rel. položaja uređaja ultra širokog opsega u blizini"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dozvolite aplikaciji da odredi relativni položaj između uređaja ultra širokog opsega u blizini"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"stupanje u interakciju s WiFi uređajima u blizini"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Dozvoljava aplikaciji da se oglašava, povezuje i određuje relativni položaj WiFi uređaja u blizini"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o preferiranoj usluzi plaćanja putem NFC-a"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Dozvoljava aplikaciji da dobije informacije o preferiranoj usluzi plaćanja putem NFC-a kao što su registrirana pomagala i odredište rute."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"upravljanje NFC-om"</string>
@@ -729,6 +731,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućava aplikaciji da čita i upisuje konfiguraciju načina rada Ne ometaj."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti korištenje odobrenja za pregled"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da pokrene korištenje odobrenja za aplikaciju. Ne bi trebalo biti potrebno za obične aplikacije."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"pokretanje pregleda funkcija aplikacije"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Dozvoljava vlasniku da pokrene pregled informacija o funkcijama za aplikaciju."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora velikom brzinom uzorkovanja"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Dozvoljava aplikaciji da uzorkuje podatke senzora brzinom većom od 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Postavljanje pravila za lozinke"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 4bc1ea7..89e466a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permet que l\'aplicació s\'anunciï als dispositius Bluetooth propers"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar posició entre dispositius de banda ultraampla propers"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permet que l\'aplicació determini la posició relativa entre els dispositius de banda ultraampla propers"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interaccionar amb els dispositius Wi‑Fi propers"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet que l\'aplicació s\'anunciï i es connecti als dispositius Wi‑Fi propers, i en determini la posició relativa"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informació preferent sobre el servei de pagament per NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet que l\'aplicació obtingui informació preferent sobre el servei de pagament per NFC, com ara complements registrats i destinacions de rutes."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar Comunicació de camp proper (NFC)"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet que l\'aplicació llegeixi la configuració No molestis i hi escrigui."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"comença a utilitzar el permís de visualització"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet que un propietari comenci a utilitzar el permís amb una aplicació. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"iniciar la visualització de les funcions d\'una aplicació"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permet que el propietari vegi la informació de les funcions d\'una aplicació."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accedir a les dades del sensor a una freqüència de mostratge alta"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permet que l\'aplicació dugui a terme un mostratge de les dades del sensor a una freqüència superior a 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Definir les normes de contrasenya"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4de0251..c57d844 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Umožňuje aplikaci inzerovat zařízením Bluetooth v okolí"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"zjišťování vzájemné pozice mezi ultra-širokopásmovými zařízeními v okolí"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Aplikace bude moci zjišťovat vzájemnou pozici mezi ultra-širokopásmovými zařízeními v okolí"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakce se zařízeními Wi-Fi v okolí"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Umožňuje aplikaci inzerovat, připojovat se a odhadovat relativní polohu zařízení Wi-Fi v okolí"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informace o preferované platební službě NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Umožňuje aplikaci získat informace o preferované platební službě NFC, například o registrovaných pomůckách a cíli směrování."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ovládání technologie NFC"</string>
@@ -732,6 +734,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikaci číst a zapisovat konfiguraci režimu Nerušit."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"zahájení zobrazení využití oprávnění"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje přístup zahájit využití oprávnění jiné aplikace. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"přístup k datům ze senzorů s vyšší vzorkovací frekvencí"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Umožňuje aplikaci vzorkovat data ze senzorů s frekvencí vyšší než 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Nastavit pravidla pro heslo"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9662952..b868ff8 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Giver appen tilladelse til at give sig til kende for Bluetooth-enheder i nærheden"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"fastlægge den relative position mellem UWB-enheder i nærheden"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tillad, at appen fastlægger den relative position mellem UWB-enheder (Ultra-Wideband) i nærheden"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagere med Wi‑Fi-enheder i nærheden"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Giver appen tilladelse til at informere om, oprette forbindelse til og fastslå den relative placering af Wi‑Fi -enheder i nærheden"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Foretrukne oplysninger vedrørende NFC-betalingstjeneste"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tillader, at appen får foretrukne oplysninger vedrørende NFC-betalingstjeneste, f.eks. registrerede hjælpemidler og rutedestinationer."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"administrere Near Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Giver appen tilladelse til at læse og redigere konfigurationen af Forstyr ikke."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start brugen at tilladelsesvisning"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Tillader, at brugeren kan bruge en tilladelse for en app. Dette bør aldrig være nødvendigt for almindelige apps."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"se appfunktioner"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Giver den app, som har tilladelsen, mulighed for at se oplysninger om en apps funktioner."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"få adgang til sensordata ved høj samplingfrekvens"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tillader, at appen kan sample sensordata ved en højere frekvens end 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Angiv regler for adgangskoder"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 368961b..d325ae3 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Erlaubt der App, Inhalte an Bluetooth-Geräte in der Nähe zu senden"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Relative Distanz zwischen Ultrabreitband-Geräten in der Nähe bestimmen"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ermöglicht der App, die relative Distanz zwischen Ultrabreitband-Geräten in der Nähe zu bestimmen"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Mit WLAN-Geräten in der Nähe interagieren"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Erlaubt der App, Inhalte an WLAN-Geräte in der Nähe zu senden, sich mit ihnen zu verbinden und ihre relative Positionierung zu ermitteln"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informationen zum bevorzugten NFC-Zahlungsdienst"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ermöglicht der App, Informationen zum bevorzugten NFC-Zahlungsdienst abzurufen, etwa registrierte Hilfsmittel oder das Routenziel."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Nahfeldkommunikation steuern"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ermöglicht der App Lese- und Schreibzugriff auf die „Bitte nicht stören“-Konfiguration"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Mit der Verwendung der Anzeigeberechtigung beginnen"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ermöglicht dem Inhaber, die Berechtigungsnutzung für eine App zu beginnen. Sollte für normale Apps nie benötigt werden."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"App-Funktionen ansehen"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Ermöglicht der App, die Informationen über die Funktionen einer anderen App anzusehen."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Sensordaten mit hoher Frequenz auslesen"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Erlaubt der App, Sensordaten mit einer Frequenz von mehr als 200 Hz auszulesen"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Passwortregeln festlegen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 57598f4..b3a556d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Επιτρέπει στην εφαρμογή να προβάλλει διαφημίσεις σε κοντινές συσκευές Bluetooth"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"προσδιορισμός σχετ. θέσης μεταξύ κοντινών συσκευών Ultra-Wideband"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Επιτρέψτε στην εφαρμογή να προσδιορίζει τη σχετική θέση μεταξύ κοντινών συσκευών Ultra-Wideband"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"αλληλεπίδραση με κοντινές συσκευές Wi‑Fi"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Επιτρέπει στην εφαρμογή: προβολή διαφημίσεων, σύνδεση και καθορισμό της σχετικής τοποθεσίας των κοντινών συσκευών Wi‑Fi"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Πληροφορίες προτιμώμενης υπηρεσίας πληρωμών NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Επιτρέπει στην εφαρμογή να λαμβάνει πληροφορίες προτιμώμενης υπηρεσίας πληρωμής NFC, όπως καταχωρημένα βοηθήματα και προορισμό διαδρομής."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ελέγχει την Επικοινωνία κοντινού πεδίου (FNC)"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Επιτρέπει στην εφαρμογή την εγγραφή και τη σύνταξη διαμόρφωσης για τη λειτουργία \"Μην ενοχλείτε\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"έναρξη χρήσης άδειας προβολής"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Επιτρέπει στον κάτοχο να ξεκινήσει τη χρήση της άδειας για μια εφαρμογή. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"έναρξη προβολής λειτουργιών εφαρμογής"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Επιτρέπει στον κάτοχο να ξεκινήσει την προβολή των πληροφοριών για τις λειτουργίες μιας εφαρμογής."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"πρόσβαση σε δεδομένα αισθητήρα με υψηλό ρυθμό δειγματοληψίας"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Επιτρέπει στην εφαρμογή τη δειγματοληψία των δεδομένων αισθητήρα με ρυθμό μεγαλύτερο από 200 Hz."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Ορισμός κανόνων κωδικού πρόσβασης"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 62fe27d..53753aa 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Allows the app to advertise to nearby Bluetooth devices"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determine relative position between nearby ultra-wideband devices"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"start view app features"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Allows the holder to start viewing the features info for an app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 4257d94..bbeb426 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Allows the app to advertise to nearby Bluetooth devices"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determine relative position between nearby ultra-wideband devices"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"start view app features"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Allows the holder to start viewing the features info for an app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index a686500..16de6b2 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Allows the app to advertise to nearby Bluetooth devices"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determine relative position between nearby ultra-wideband devices"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"start view app features"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Allows the holder to start viewing the features info for an app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 89dc230..0b19c5c 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Allows the app to advertise to nearby Bluetooth devices"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determine relative position between nearby ultra-wideband devices"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"start view app features"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Allows the holder to start viewing the features info for an app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 2d8d038..7fba349 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎Allows the app to advertise to nearby Bluetooth devices‎‏‎‎‏‎"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎determine relative position between nearby Ultra-Wideband devices‎‏‎‎‏‎"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎Allow the app to determine relative position between nearby Ultra-Wideband devices‎‏‎‎‏‎"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎interact with nearby Wi‑Fi devices‎‏‎‎‏‎"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎Allows the app to advertise, connect, and determine the relative position of nearby Wi‑Fi devices‎‏‎‎‏‎"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎Preferred NFC Payment Service Information‎‏‎‎‏‎"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‏‎‏‏‏‎Allows the app to get preferred nfc payment service information like registered aids and route destination.‎‏‎‎‏‎"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎control Near Field Communication‎‏‎‎‏‎"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎Allows the app to read and write Do Not Disturb configuration.‎‏‎‎‏‎"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‎start view permission usage‎‏‎‎‏‎"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‎Allows the holder to start the permission usage for an app. Should never be needed for normal apps.‎‏‎‎‏‎"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎start view app features‎‏‎‎‏‎"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎Allows the holder to start viewing the features info for an app.‎‏‎‎‏‎"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎access sensor data at a high sampling rate‎‏‎‎‏‎"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎Allows the app to sample sensor data at a rate greater than 200 Hz‎‏‎‎‏‎"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‏‏‎Set password rules‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 843d04f..38b4963 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que la app muestre anuncios a dispositivos Bluetooth cercanos"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar posición entre disposit. Ultra Wideband"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que la app determine la posición relativa con dispositivos Ultra Wideband cercanos"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactuar con dispositivos Wi-Fi cercanos"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que la app muestre anuncios, se conecte y determine la posición relativa de los dispositivos Wi-Fi cercanos"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información sobre servicio de pago NFC preferido"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que la app reciba información del servicio de pago NFC preferido, como el servicio de asistencia registrado y el destino de la ruta."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar la Transmisión de datos en proximidad"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de la función No interrumpir."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el propietario inicie el uso de permisos para una app. No debería requerirse para apps normales."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"iniciar vista de funciones de la app"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que el propietario vea la información de las funciones de una app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Acceder a los datos del sensor a una tasa de muestreo alta"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que la app tome una muestra de los datos del sensor a una tasa superior a 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer reglas de contraseña"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 8127201..4afc20c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que la aplicación emita a dispositivos Bluetooth cercanos"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"calcular posición de dispositivos de banda ultraancha cercanos"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que la aplicación determine la posición relativa de los dispositivos de banda ultraancha cercanos"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Interactuar con dispositivos Wi-Fi cercanos"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite a la aplicación mostrar información de dispositivos Wi-Fi cercanos, conectarse a ellos y determinar su posición"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información sobre el servicio de pago por NFC preferido"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que la aplicación obtenga información sobre el servicio de pago por NFC preferido, como identificadores de aplicación registrados y destinos de rutas."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar Comunicación de campo cercano (NFC)"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de No molestar."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de visualización"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el titular inicie el uso de permisos de una aplicación. Las aplicaciones normales no deberían necesitar nunca este permiso."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acceder a datos de sensores a una frecuencia de muestreo alta"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que la aplicación consulte datos de sensores a una frecuencia superior a 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Establecimiento de reglas de contraseña"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 510826e..3320ab2 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Lubab rakendusel läheduses olevatele Bluetooth-seadmetele reklaamida"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Määrata lähedalasuvate ülilairibaühendust kasutavate seadmete suhtelise kauguse üksteisest."</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Võimaldab rakendusel määrata lähedalasuvate ülilairibaühendust kasutavate seadmete suhtelise kauguse üksteisest"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Läheduses olevate WiFi-seadmetega suhtlemine"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lubab rakendusel läheduses olevatele WiFi-seadmetele reklaamida, nendega ühenduse luua ja määrata nende suhteline asend"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Eelistatud NFC-makseteenuse teave"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Võimaldab rakendusel hankida eelistatud NFC-makseteenuse teavet (nt registreeritud abi ja marsruudi sihtkoht)."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"lähiväljaside juhtimine"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Võimaldab rakendusel lugeda ja kirjutada funktsiooni Mitte segada seadistusi."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"vaatamisloa kasutamise alustamine"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Võimaldab omanikul rakenduse puhul alustada loa kasutamist. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"rakenduse funktsioonide vaatamise alustamine"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Võimaldab omanikul alustada rakenduse funktsioonide teabe vaatamist."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"juurdepääs anduri andmetele kõrgel diskreetimissagedusel"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Võimaldab rakendusel anduri andmeid diskreetida sagedusel, mis on suurem kui 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Parooli reeglite määramine"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 5706aa1..dc28a1a 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Inguruko Bluetooth bidezko gailuetan informazioa iragartzeko baimena ematen die aplikazioei"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"banda ultrazabala darabilten inguruko gailuen arteko distantzia erlatiboa zehaztu"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Banda ultrazabala darabilten inguruko gailuen arteko distantzia erlatiboa zehazteko baimena ematen dio aplikazioari"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"inguruko wifi-gailuekin interakzioan jardun"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Inguruko wifi-gailuetan iragartzeko, haiekin konektatzeko eta haien kokapena zehazteko baimena ematen die aplikazioei"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa jasotzeko baimena ematen die aplikazioei, hala nola erregistratutako laguntzaileak eta ibilbidearen helmuga."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrolatu Near Field Communication komunikazioa"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ez molestatzeko moduaren konfigurazioa irakurtzeko eta bertan idazteko baimena ematen die aplikazioei."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"hasi ikusteko baimena erabiltzen"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Aplikazioaren baimena erabiltzen hasteko baimena ematen die titularrei. Aplikazio normalek ez lukete beharko."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"hasi aplikazioaren eginbideak ikusten"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Aplikazio baten eginbideei buruzko informazioa ikusten hasteko baimena ematen die titularrei."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"atzitu sentsoreen datuen laginak abiadura handian"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikazioak 200 Hz-tik gorako abiaduran hartu ahal izango ditu sentsoreen datuen laginak"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Ezarri pasahitzen arauak"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 3eb3f3d..7c964ae 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"برنامه مجاز می‌شود در دستگاه‌های بلوتوث اطراف تبلیغ کند."</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"مشخص کردن موقعیت نسبی بین دستگاه‌های باند فوق‌وسیع اطراف"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"به برنامه اجازه داده می‌شود موقعیت نسبی بین دستگاه‌های باند فوق‌وسیع اطراف را مشخص کند"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏برقراری تعامل با دستگاه‌های Wi-Fi اطراف"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏به برنامه اجازه می‌دهد در دستگاه‌های Wi-Fi اطراف تبلیغ کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را تشخیص دهد"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏اطلاعات ترجیحی سرویس پولی NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏به برنامه اجازه می‌دهد اطلاعات ترجیحی سرویس پولی NFC، مانند کمک‌های ثبت‌شده و مقصد مسیر را دریافت کند."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"کنترل ارتباط راه نزدیک"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"به برنامه امکان می‌دهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"شروع مشاهده استفاده از مجوز"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"به دارنده اجازه شروع استفاده از مجوز را برای برنامه می‌دهد. هرگز برای برنامه‌های معمول نیاز نیست."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"مشاهده ویژگی‌های برنامه"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"به دارنده اجازه می‌دهد اطلاعات مربوط به ویژگی‌های برنامه را مشاهده کند."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"دسترسی به داده‌های حسگر با نرخ نمونه‌برداری بالا"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"به برنامه اجازه می‌دهد داده‌های حسگر را با نرخ بیش‌از ۲۰۰ هرتز نمونه‌برداری کند"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"تنظیم قوانین گذرواژه"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index d40ed60..aba14ae 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Sallii sovelluksen mainostaa lähellä oleville Bluetooth-laitteille"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"määrittää UVB:ta käyttävien laitteiden suhteellisen sijainnin"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Sallii sovelluksen määrittää UVB-taajuutta käyttävien laitteiden sijainnin suhteessa toisiinsa"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"käyttää lähellä olevia Wi-Fi-laitteita"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Sallii sovelluksen ilmoittaa ja määrittää lähellä olevien Wi-Fi-laitteiden suhteellisen sijainnin sekä yhdistää niihin"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Ensisijaiset NFC-maksupalvelutiedot"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Sallii sovelluksen noutaa tietoja rekisteröidyistä sovellustunnuksista, maksureitin kohteesta ja muita ensisijaisia NFC-maksupalvelutietoja."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"hallitse Near Field Communication -tunnistusta"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Sallii sovelluksen lukea ja muokata Älä häiritse -tilan asetuksia."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"aloita katseluoikeuksien käyttö"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Antaa luvanhaltijan käynnistää sovelluksen käyttöoikeuksien käytön. Ei tavallisten sovelluksien käyttöön."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"aloittaa sovellusominaisuuksien katselun"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Antaa luvanhaltijan aloittaa sovelluksen ominaisuustietojen katselun."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"saada pääsyn anturidataan suuremmalla näytteenottotaajuudella"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Sallii sovelluksen ottaa anturidatasta näytteitä yli 200 Hz:n taajuudella"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Asentaa salasanasäännöt"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 43d6c09..56ab0af 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permet à l\'application d\'envoyer des annonces aux appareils Bluetooth à proximité"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"déterminer la position relative entre des appareils à bande ultralarge à proximité"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Autorisez l\'application à déterminer la position relative entre des appareils à bande ultralarge à proximité"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir avec les appareils Wi-Fi à proximité"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet à l\'application d\'annoncer, de se connecter et de déterminer la position relative des appareils Wi-Fi à proximité"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Information sur le service préféré de paiement CCP"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet à l\'application d\'obtenir de l\'information sur le service préféré de paiement CCP comme les aides enregistrées et la route de destination."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"gérer la communication en champ proche"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"démarrer l\'affichage de l\'usage des autorisations"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet au détenteur de démarrer l\'usage des autorisations pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d’échantillonnage élevé"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permet à l’application d’échantillonner les données des capteurs à une fréquence supérieure à 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Définir les règles du mot de passe"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index a88df26..17ef670 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permet à l\'appli de diffuser du contenu sur les appareils Bluetooth à proximité"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"déterminer position relative entre appareils ultra-wideband à proximité"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Autoriser l\'appli à déterminer la position relative entre des appareils ultra-wideband à proximité"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir avec les appareils Wi-Fi à proximité"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet à l\'appli de déterminer la position relative des appareils Wi‑Fi à proximité, vous en informer et s\'y connecter"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informations sur le service de paiement NFC préféré"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet à l\'application d\'obtenir des informations sur le service de paiement NFC préféré, y compris les ID d\'applications et les destinations de routage enregistrés."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"contrôler la communication en champ proche"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"activer l\'utilisation de l\'autorisation d\'affichage"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet à l\'application autorisée d\'activer l\'utilisation de l\'autorisation pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"commencer à voir les fonctionnalités d\'une appli"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permet à l\'appli autorisée de commencer à voir les infos sur les fonctionnalités d\'une appli."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d\'échantillonnage élevé"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Autorise l\'appli à échantillonner les données des capteurs à un taux supérieur à 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Définir les règles du mot de passe"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 74df014..987464a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -543,6 +543,10 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que a aplicación envíe anuncios a dispositivos Bluetooth próximos"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar posición entre dispositivos próximos"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que a aplicación determine a posición relativa entre os dispositivos próximos que usen banda ultralarga"</string>
+    <!-- no translation found for permlab_nearby_wifi_devices (392774237063608500) -->
+    <skip />
+    <!-- no translation found for permdesc_nearby_wifi_devices (3054307728646332906) -->
+    <skip />
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información do servizo de pago de NFC preferido"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que a aplicación obteña información do servizo de pago de NFC preferido, como as axudas rexistradas e o destino da ruta."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar Near Field Communication"</string>
@@ -726,6 +730,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite á aplicación ler e escribir a configuración do modo Non molestar."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite ao propietario iniciar o uso de permisos dunha aplicación. As aplicacións normais non deberían precisalo nunca."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"comezar a ver as funcións da aplicación"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que o propietario comece a ver a información das funcións dunha aplicación."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acceder aos datos dos sensores usando unha taxa de mostraxe alta"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que a aplicación recompile mostras dos datos dos sensores cunha taxa superior a 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer as normas de contrasinal"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5b0682b..0a2649d 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"નજીકના બ્લૂટૂથ ડિવાઇસ પર ઍપને જાહેરાત કરવાની મંજૂરી આપે છે"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"અલ્ટ્રા-વાઇડબૅન્ડ ડિવાઇસની વચ્ચેનું અંતર નક્કી કરો"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ઍપને નજીકના અલ્ટ્રા-વાઇડબૅન્ડ ડિવાઇસની વચ્ચેનું સંબંધિત અંતર નક્કી કરવાની મંજૂરી આપો"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"નજીકના વાઇ-ફાઇ ડિવાઇસ સાથે ક્રિયાપ્રતિક્રિયા કરો"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ઍપને નજીકના વાઇ-ફાઇ ડિવાઇસની માહિતી બતાવવાની, તેની સાથે કનેક્ટ કરવાની અને તેની સંબંધિત સ્થિતિ નક્કી કરવાની મંજૂરી આપો"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"પસંદગીની NFC ચુકવણીની સેવા વિશે માહિતી"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"આ મંજૂરીને આપવાથી, ઍપ તમારી પસંદગીની NFC ચુકવણીની સેવા વિશે માહિતી મેળવી શકે છે, જેમ કે રજિસ્ટર થયેલી સહાય અને નિર્ધારિત સ્થાન."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"નિઅર ફીલ્ડ કમ્યુનિકેશન નિયંત્રિત કરો"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"એપ્લિકેશનને ખલેલ પાડશો નહીં ગોઠવણી વાંચવા અને લખવાની મંજૂરી આપે છે."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"પરવાનગી વપરાશ જુઓને શરૂ કરો"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"કોઈ ઍપ માટે પરવાનગી વપરાશ શરૂ કરવાની ધારકને મંજૂરી આપે છે. સામાન્ય ઍપ માટે ક્યારેય જરૂર પડી ન શકે."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ઉચ્ચ સેમ્પ્લિંગ રેટ પર સેન્સરનો ડેટા ઍક્સેસ કરો"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ઍપને 200 Hzથી વધુના દરે સેન્સરના ડેટાના નમૂનાની મંજૂરી આપે છે"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"પાસવર્ડ નિયમો સેટ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index e7cdae4..1d9230a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"अनुमति मिलने पर, ऐप्लिकेशन, आस-पास मौजूद ब्लूटूथ डिवाइसों पर विज्ञापन दिखा पाएगा"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"आस-पास मौजूद Ultra-Wideband डिवाइसों के बीच की दूरी का पता लगाएं"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ऐप्लिकेशन को आस-पास मौजूद Ultra-Wideband डिवाइसों के बीच की दूरी का पता लगाने की अनुमति दें"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"आस-पास मौजूद वाई-फ़ाई डिवाइसों से इंटरैक्ट करें"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"इससे, ऐप्लिकेशन आस-पास मौजूद वाई-फ़ाई डिवाइसों की जानकारी दिखा पाएगा, उनसे कनेक्ट कर पाएगा, और उनकी दूरी पता लगा पाएगा"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC का इस्तेमाल करने वाली पैसे चुकाने की पसंदीदा सेवा की जानकारी"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"अगर ऐप्लिकेशन को अनुमति दी जाती है, तो वह पैसे चुकाने की आपकी उस पसंदीदा सेवा के बारे में जानकारी पा सकता है जो NFC का इस्तेमाल करती है. इसमें रजिस्टर किए गए डिवाइस और उनके आउटपुट के रूट जैसी जानकारी शामिल होती है."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"नियर फ़ील्‍ड कम्‍यूनिकेशन नियंत्रित करें"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ऐप को परेशान न करें कॉन्फ़िगरेशन पढ़ने और लिखने देती है."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"देखने की अनुमतियां चालू करें"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"इस्तेमाल करने वाले को किसी ऐप्लिकेशन के लिए अनुमतियों का इस्तेमाल शुरू करने देता है. सामान्य ऐप्लिकेशन के लिए इसकी ज़रूरत कभी नहीं पड़ती."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ऐप्लिकेशन की सुविधाओं को देखना शुरू करें"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ऐप्लिकेशन को, किसी ऐप्लिकेशन की सुविधाओं की जानकारी देखने की अनुमति देता है."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"सेंसर डेटा को, नमूने लेने की तेज़ दर पर ऐक्सेस करें"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"यह अनुमति मिलने पर ऐप्लिकेशन, 200 हर्ट्ज़ से ज़्यादा की दर पर सेंसर डेटा का नमूना ले पाएगा"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करना"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 04832f7..5f276c3 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -546,6 +546,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Aplikaciji omogućuje oglašavanje na Bluetooth uređajima u blizini"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"odredite približni položaj između uređaja u blizini koji upotrebljavaju ultraširokopojasno povezivanje"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dopušta aplikaciji da odredi približni položaj između uređaja u blizini koji upotrebljavaju ultraširokopojasno povezivanje"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcija s Wi-Fi uređajima u blizini"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Aplikaciji omogućuje oglašavanje, povezivanje i određivanje približnog položaja Wi-Fi uređaja u blizini"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o preferiranoj usluzi plaćanja NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Omogućuje aplikaciji primanje informacija o preferiranoj usluzi plaćanja NFC kao što su registrirana pomagala i odredište."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"upravljanje beskontaktnom komunikacijom (NFC)"</string>
@@ -729,6 +731,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućuje aplikaciji čitanje i pisanje konfiguracije opcije Ne uznemiravaj."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti upotrebu dopuštenja za pregled"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dopušta nositelju pokretanje upotrebe dopuštenja za aplikaciju. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri višoj brzini uzorkovanja"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikaciji omogućuje uzorkovanje podataka senzora pri brzini većoj od 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Postavi pravila zaporke"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 4a4e13d..a0f8eac 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Lehetővé teszi az alkalmazás számára, hogy közzétegye jelenlétét a közeli Bluetooth-eszközök számára"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"közeli, ultraszélessávú eszközök közötti relatív pozíció meghatározása"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Az alkalmazás meghatározhatja a közeli, ultraszélessávú eszközök közötti relatív pozíciót"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"műveleteket végezhet a közeli Wi‑Fi-eszközökkel"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Engedélyezi az alkalmazás számára, hogy közzétegye és meghatározza a közeli Wi-Fi-eszközök viszonylagos helyzetét, és csatlakozzon hozzájuk."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferált NFC fizetési szolgáltatási információk"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lehetővé teszi az alkalmazás számára preferált NFC fizetési szolgáltatási információk (pl. regisztrált alkalmazásazonosítók és útvonali cél) lekérését."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"NFC technológia vezérlése"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Az alkalmazás olvashatja és szerkesztheti a „Ne zavarjanak” funkció beállításait."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"engedélyhasználat megtekintésének elindítása"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lehetővé teszi a felhasználó számára, hogy elindítsa az alkalmazás engedélyhasználatát. A normál alkalmazásoknak erre soha nincs szükségük."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"hozzáférés a szenzoradatokhoz nagy mintavételezési gyakorisággal"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lehetővé teszi az alkalmazás számára, hogy 200 Hz-nél magasabb gyakorisággal vegyen mintát a szenzoradatokból"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Jelszavakkal kapcsolatos szabályok beállítása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 389412c..c79d4f5 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Թույլատրում է հավելվածին գովազդ փոխանցել մոտակա Bluetooth սարքերին"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"որոշել մոտակա UWB սարքերի միջև հարաբերական դիրքավորումը"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Թույլատրել հավելվածին որոշել գերլայնաշերտ կապի տեխնոլոգիան աջակցող մոտակա սարքերի միջև հարաբերական դիրքավորումը"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Փոխազդում մոտակա Wi‑Fi սարքերի հետ"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Թույլ է տալիս հավելվածին տվյալներ փոխանցել մոտակա Wi‑Fi սարքերին, միանալ դրանց և որոշել դրանց մոտավոր դիրքը։"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Տեղեկություններ NFC վճարային ծառայության մասին"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Թույլ է տալիս հավելվածին ստանալ նախընտրելի NFC վճարային ծառայության մասին տեղեկություններ (օր․՝ գրանցված լրացուցիչ սարքերի և երթուղու նպատակակետի մասին տվյալներ)։"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"վերահսկել Մոտ Տարածությամբ Հաղորդակցումը"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Թույլ է տալիս հավելվածին փոփոխել «Չանհանգստացնել» գործառույթի կազմաձևումը:"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"թույլտվությունների մասին տվյալների հասանելիություն"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Հավելվածին հասանելի կդառնան թույլտվությունների մասին տվյալները։ Այս թույլտվությունն անհրաժեշտ չէ սովորական հավելվածներին։"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"հավելվածի գործառույթների դիտում"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Թույլ է տալիս դիտել հավելվածի գործառույթների մասին տեղեկությունները։"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"օգտագործել սենսորների տվյալները բարձր հաճախականության վրա"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Թույլ է տալիս հավելվածին փորձել սենսորների տվյալները 200 Հց-ից բարձր հաճախականության վրա"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Սահմանել գաղտնաբառի կանոնները"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 2ad3a55..080f1e1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Mengizinkan aplikasi untuk menampilkan iklan ke perangkat Bluetooth di sekitar"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"menentukan posisi relatif antar-perangkat Ultra-Wideband di sekitar"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Mengizinkan aplikasi menentukan posisi relatif antar-perangkat Ultra-Wideband di sekitar"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"berinteraksi dengan perangkat Wi-Fi di sekitar"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Mengizinkan aplikasi menampilkan informasi, menghubungkan, dan menentukan posisi relatif perangkat Wi-Fi di sekitar"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasi Layanan Pembayaran NFC Pilihan"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Mengizinkan aplikasi untuk mendapatkan informasi layanan pembayaran NFC pilihan seperti bantuan terdaftar dan tujuan rute."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrol NFC"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Mengizinkan aplikasi membaca dan menulis konfigurasi status Jangan Ganggu."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulai melihat penggunaan izin"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Memungkinkan pemegang memulai penggunaan izin untuk aplikasi. Tidak diperlukan untuk aplikasi normal."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"mulai lihat fitur aplikasi"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Memungkinkan pemegang mulai melihat info fitur untuk aplikasi."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mengakses data sensor pada frekuensi pengambilan sampel yang tinggi"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Mengizinkan aplikasi mengambil sampel data sensor pada frekuensi yang lebih besar dari 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Setel aturan sandi"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 982b6c2..78e01c9 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Leyfir forritinu að birta nálægum Bluetooth-tækjum auglýsingar"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ákvarða fjarlægð milli nálægra tækja með ofurbreiðband"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Leyfa forritinu að ákvarða fjarlægð milli nálægra tækja með ofurbreiðband"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"eiga í samskiptum við nálæg WiFi-tæki"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Leyfir forritinu að auglýsa, tengja og áætla staðsetningu nálægra WiFi-tækja"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Upplýsingar um valda NFC-greiðsluþjónustu"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Gerir forritinu kleift að fá valda NFC-greiðsluþjónustu, svo sem skráða aðstoð og áfangastað leiðar."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"stjórna nándarsamskiptum (NFC)"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leyfir forriti að lesa og skrifa í grunnstillingu „Ónáðið ekki“."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"heimildanotkun upphafsyfirlits"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leyfir handhafa að byrja heimildanotkun fyrir forrit. Ætti aldrei að þurfa fyrir venjuleg forrit."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"byrja að skoða eiginleika forrits"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Leyfir handhafa að skoða upplýsingar um eiginleika tiltekins forrits."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"aðgangur að skynjaragögnum með hárri upptökutíðni"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Leyfir forritinu að nota upptökutíðni yfir 200 Hz fyrir skynjaragögn"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Setja reglur um aðgangsorð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ad19a87..71cda82 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Consente all\'app di trasmettere annunci ai dispositivi Bluetooth nelle vicinanze"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Possibilità di stabilire la posizione relativa tra dispositivi a banda ultralarga nelle vicinanze"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Consenti all\'app di stabilire la posizione relativa tra dispositivi a banda ultralarga nelle vicinanze"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Interazione con dispositivi Wi-Fi vicini"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Consente all\'app di trasmettere annunci e connettersi a dispositivi Wi‑Fi vicini e di stabilirne la posizione relativa."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informazioni del servizio di pagamento NFC preferito"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Consente all\'app di recuperare informazioni del servizio di pagamento NFC preferito, quali destinazione della route e identificatori applicazione registrati."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controllo Near Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Consente all\'app di leggere e modificare la configurazione della funzione Non disturbare."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"avvio dell\'uso dell\'autorizzazione di visualizzazione"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Consente al titolare di avviare l\'uso delle autorizzazioni per un\'app. Non dovrebbe essere mai necessaria per le normali applicazioni."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"Inizio della visualizzazione di funzionalità delle app"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Consente all\'app che ha questa autorizzazione di iniziare a visualizzare le informazioni relative alle funzionalità di un\'app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Accesso ai dati dei sensori a una frequenza di campionamento elevata"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Consente all\'app di campionare i dati dei sensori a una frequenza maggiore di 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Impostare regole per le password"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 52539e0..0c5a4a8 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"‏האפליקציה תוכל לפרסם במכשירי Bluetooth בקרבת מקום"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"זיהוי מיקום יחסי בין מכשירי \'תחום רחב סרט\' קרובים"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"האפליקציה תזהה את המיקום היחסי בין מכשירים קרובים שמשדרים בטכנולוגיה \'תחום רחב סרט\'"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏אינטראקציה עם מכשירי Wi-Fi בקרבת מקום"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏האפליקציה תוכל לפרסם במכשירי Wi-Fi בקרבת מקום, להתחבר אליהם ולהעריך את המיקום היחסי שלהם"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏פרטים על שירות תשלום מועדף ב-NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏מאפשרת לאפליקציה לקבל פרטים על שירות תשלום מועדף ב-NFC, כמו עזרים רשומים ויעד של נתיב."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"שליטה בתקשורת מטווח קצר"</string>
@@ -732,6 +734,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"מאפשרת לאפליקציה לקרוא ולכתוב את התצורה של התכונה \'נא לא להפריע\'."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"התחלת צפייה בהרשאות השימוש"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"מאפשרת לבעלים להפעיל את השימוש בהרשאות עבור אפליקציה מסוימת. הרשאה זו אף פעם לא נדרשת עבור אפליקציות רגילות."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"התחלת צפייה בהרשאות של אפליקציות"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"בעלי ההרשאה יוכלו להתחיל לצפות בפרטי התכונות של אפליקציות."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"גישה לנתוני חיישנים בתדירות דגימה גבוהה"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"האפליקציה תוכל לדגום נתוני חיישנים בתדירות של מעל 200 הרץ"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"הגדרת כללי סיסמה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 962b65b..911b6d2 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"付近の Bluetooth デバイスへの広告の配信をアプリに許可します"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"付近の Ultra Wideband デバイス間の相対位置の特定"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"付近の Ultra Wideband デバイス間の相対位置の特定をアプリに許可します"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"付近の Wi-Fi デバイスとのやり取り"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"付近の Wi-Fi デバイスについて、情報の表示、接続、相対位置の確認をアプリに許可します"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"優先される NFC お支払いサービスの情報"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"登録されている支援やルートの目的地など、優先される NFC お支払いサービスの情報を取得することをアプリに許可します。"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"NFCの管理"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"サイレント モード設定の読み取りと書き込みをアプリに許可します。"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"表示権限の使用の開始"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"アプリの権限使用の開始を所有者に許可します。通常のアプリでは不要です。"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"アプリ機能の表示の開始"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"アプリの機能情報の表示の開始を所有者に許可します。"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"高サンプリング レートでセンサーデータにアクセスする"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz を超えるレートでセンサーデータをサンプリングすることをアプリに許可します"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"パスワードルールの設定"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index bb307f9..d5b92b7 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"საშუალებას აძლევს აპს, რეკლამა განათავსოს ახლომახლო Bluetooth მოწყობილობებზე"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"შედარებითი პოზიციის დადგენა ახლომახლო ულტრაფართო სიხშირის მოწყობილობების შესახებ"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ნებას რთავს აპს, დაადგინოს შედარებითი პოზიცია ახლომახლო ულტრაფართო სიხშირის მოწყობილობების შესახებ"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ინტერაქცია ახლომახლო Wi-Fi მოწყობილობებთან"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"საშუალებას აძლევს აპს, განაცხადოს ახლომახლო Wi-Fi მოწყობილობების შესახებ, დაუკავშირდეს მათ და განსაზღვროს მათი შედარებითი პოზიცია"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"უპირატესი NFC გადახდის სერვისის ინფორმაცია"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"საშუალებას აძლევს აპს, მიიღოს უპირატესი NFC გადახდის სერვისის ინფორმაცია, მაგალითად, რეგისტრირებული დახმარება და დანიშნულება."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ახლო მოქმედების რადიოკავშირი (NFC) მართვა"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"საშუალებას აძლევს აპს, წაიკითხოს და დაწეროს კონფიგურაცია „არ შემაწუხოთ“."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ნახვის ნებართვის გამოყენების დაწყება"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"მფლობელს საშუალებას აძლევს, დაიწყოს აპის ნებართვის გამოყენება. ჩვეულებრივი აპებისთვის არასოდეს უნდა იყოს საჭირო."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"აპის ფუნქციების ნახვის დაწყება"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"მფლობელს საშუალებას აძლევს, დაიწყოს აპის ფუნქციების ინფორმაციის ნახვა."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"სენსორის მონაცემებზე წვდომა სემპლინგის მაღალი სიხშირით"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"საშუალებას აძლევს აპს, მიიღოს სენსორის მონაცემების ნიმუშები 200 ჰც-ზე მეტი სიხშირით"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"პაროლის წესების დაყენება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 1e84e78..05da5fd 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -543,6 +543,10 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Қолданба жарнаманы маңайдағы Bluetooth құрылғыларына бере алады."</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"маңайдағы кең жолақты құрылғылардың бір-біріне қатысты орнын анықтау"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Қолданбаға маңайдағы кең жолақты құрылғылардың бір-біріне қатысты орнын анықтауға мүмкіндік береді."</string>
+    <!-- no translation found for permlab_nearby_wifi_devices (392774237063608500) -->
+    <skip />
+    <!-- no translation found for permdesc_nearby_wifi_devices (3054307728646332906) -->
+    <skip />
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Таңдаулы NFC төлеу қызметі туралы ақпарат"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Қолданба тіркелген көмектер және баратын жер маршруты сияқты таңдаулы NFC төлеу қызметі туралы ақпаратты ала алатын болады."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"NFC функциясын басқару"</string>
@@ -726,6 +730,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Қолданбаға «Мазаламау» конфигурациясын оқу және жазу мүмкіндігін береді."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"рұқсаттарды пайдалану туралы деректерді көру"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Пайдаланушы қолданбаға берілетін рұқсаттарды басқара алады. Ондай рұқсаттар әдеттегі қолданбаларға керек емес."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"қолданба функцияларын көре бастау"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Қолданбаға функциялар туралы ақпаратты көре бастауды кідіртуге мүмкіндік береді."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"жоғары дискретизация жиілігіндегі датчик деректерін пайдалану"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Қолданбаға жиілігі 200 Гц-тен жоғары датчик деректерінің үлгісін таңдауға рұқсат береді."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Құпия сөз ережелерін тағайындау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 406e0a5..6dc8abd 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"អនុញ្ញាតឱ្យ​កម្មវិធី​ផ្សាយពាណិជ្ជកម្ម​ទៅឧបករណ៍​ប៊្លូធូសដែលនៅជិត"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"កំណត់ចម្ងាយពាក់ព័ន្ធរវាងឧបករណ៍ Ultra-Wideband ដែលនៅជិត"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"អនុញ្ញាតឱ្យ​កម្មវិធី​កំណត់ចម្ងាយ​ពាក់ព័ន្ធ​រវាងឧបករណ៍ Ultra-Wideband ដែលនៅជិត"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ធ្វើអន្តរកម្ម​ជាមួយឧបករណ៍ Wi‑Fi ដែលនៅជិត"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"អនុញ្ញាតឱ្យ​កម្មវិធី​ផ្សាយពាណិជ្ជកម្ម ភ្ជាប់ និងកំណត់ទីតាំង​ពាក់ព័ន្ធរបស់​ឧបករណ៍ Wi‑Fi ដែលនៅជិត"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ព័ត៌មានអំពី​សេវាបង់ប្រាក់តាម NFC ជាអាទិភាព"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"អនុញ្ញាតឱ្យ​កម្មវិធី​ទទួលបាន​ព័ត៌មានអំពី​សេវាបង់ប្រាក់តាម nfc ជាអាទិភាព​ដូចជា គោលដៅផ្លូវ និង​ព័ត៌មាន​កំណត់អត្តសញ្ញាណ​កម្មវិធី ដែលបានចុះឈ្មោះ​ជាដើម។"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ពិនិត្យ​ការ​ទាក់ទង​នៅ​ក្បែរ (NFC)"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"អនុញ្ញាតឲ្យកម្មវិធីអាន និងសរសេរការកំណត់រចនាសម្ព័ន្ធមុខងារ កុំរំខាន។"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ចាប់ផ្ដើម​មើល​ការប្រើប្រាស់​ការអនុញ្ញាត"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"អនុញ្ញាត​ឱ្យម្ចាស់​ចាប់ផ្ដើម​ការប្រើប្រាស់​ការអនុញ្ញាត​សម្រាប់កម្មវិធី។ មិនគួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ចាប់ផ្ដើមមើល​មុខងារកម្មវិធី"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"អនុញ្ញាតឱ្យកម្មវិធី​ចាប់ផ្ដើម​មើលព័ត៌មានមុខងារ​សម្រាប់កម្មវិធី។"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ចូលប្រើទិន្នន័យ​ឧបករណ៍ចាប់សញ្ញា​នៅអត្រាសំណាកខ្ពស់"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"អនុញ្ញាតឱ្យកម្មវិធី​ធ្វើសំណាកទិន្នន័យ​ឧបករណ៍ចាប់សញ្ញា​នៅអត្រាលើសពី 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"កំណត់​ក្បួន​ពាក្យ​សម្ងាត់"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index c8edc77..89a5043 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"ಸಮೀಪದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳಿಗೆ ಜಾಹೀರಾತು ನೀಡಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ಸಮೀಪದಲ್ಲಿರುವ ಅಲ್ಟ್ರಾ-ವೈಡ್‌ಬ್ಯಾಂಡ್ ಸಾಧನಗಳ ನಡುವೆ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸುತ್ತದೆ"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ಸಮೀಪದಲ್ಲಿರುವ ಅಲ್ಟ್ರಾ-ವೈಡ್‌ಬ್ಯಾಂಡ್ ಸಾಧನಗಳ ನಡುವೆ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಿ"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ಹತ್ತಿರದ ವೈ -ಫೈ ಸಾಧನಗಳ ಜೊತೆಗೆ ಸಂವಹನ ನಡೆಸಿ"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ಹತ್ತಿರದ ವೈ -ಫೈ ಸಾಧನಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ಸೂಚಿಸಲು, ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ನಿರ್ಧರಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ಆದ್ಯತೆಯ NFC ಪಾವತಿ ಸೇವಾ ಮಾಹಿತಿ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ನೋಂದಾಯಿತ ಅಪ್ಲಿಕೇಶನ್ ಗುರುತಿಸುವಿಕೆಗಳು ಮತ್ತು ಮಾರ್ಗ ಗಮ್ಯಸ್ಥಾನಗಳಂತಹ ಆದ್ಯತೆಯ NFC ಪಾವತಿ ಸೇವೆಗಳ ಬಗ್ಗೆ ಮಾಹಿತಿಯನ್ನು ಪಡೆಯಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ಸಮೀಪ ಕ್ಷೇತ್ರ ಸಂವಹನವನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ಓದಲು ಮತ್ತು ಬರೆಯಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ವೀಕ್ಷಣಾ ಅನುಮತಿಯ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ಆ್ಯಪ್‌ಗಾಗಿ ಅನುಮತಿ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಲು ಹೊಂದಿರುವವರಿಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಆ್ಯಪ್‌ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ಹೆಚ್ಚಿನ ನಮೂನೆ ದರದಲ್ಲಿ ಸೆನ್ಸಾರ್ ಡೇಟಾ ಪ್ರವೇಶಿಸಿ"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ಗಿಂತಲೂ ಹೆಚ್ಚಿನ ವೇಗದಲ್ಲಿ ಸೆನ್ಸಾರ್ ಡೇಟಾದ ಮಾದರಿ ಪರೀಕ್ಷಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಿ"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"ಪಾಸ್‌ವರ್ಡ್ ನಿಮಯಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4e67b19..5c4c93c 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"앱에서 근처의 블루투스 기기로 광고하도록 허용"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"근처 초광대역 기기 간 상대적 위치 파악"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"앱이 근처의 초광대역 기기 간 상대적 위치를 파악하도록 허용"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"근처 Wi‑Fi 기기와 상호작용"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"앱이 광역 신호를 보내 근처에 있는 Wi‑Fi 기기의 상대적인 위치를 확인하고 연결할 수 있도록 허용합니다."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"기본 NFC 결제 서비스 정보"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"앱이 등록된 AID와 경로 목적지 같은 기본 NFC 결제 서비스 정보를 확인하도록 허용합니다."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"NFC(Near Field Communication) 제어"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"앱에서 방해 금지 모드 설정을 읽고 작성하도록 허용합니다."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"권한 사용 보기 시작"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"앱의 권한 사용을 시작하려면 보유자를 허용하세요. 일반 앱에는 필요하지 않습니다."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"앱 기능 보기"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"권한을 보유한 앱에서 앱의 기능 정보를 보도록 허용합니다."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"더 높은 샘플링 레이트로 센서 데이터 액세스"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"앱에서 200Hz보다 빠른 속도로 센서 데이터를 샘플링하도록 허용합니다."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"비밀번호 규칙 설정"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 0568133..87faa67 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Колдонмого жакын жердеги Bluetooth түзмөктөрүнө жарнама көрсөтүүгө мүмкүндүк берет"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктоо"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Колдонмо кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктай алат"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"жакын жердеги Wi‑Fi түзмөктөрү менен байланышуу"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Колдонмого жарнама көрсөтүүгө, байланышууга жана жакын жердеги Wi‑Fi түзмөктөрүн аныктоого мүмкүндүк берет"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Тандалган NFC төлөм кызматы жөнүндө маалымат"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Колдонмого катталган жардам же көздөлгөн жерге маршрут сыяктуу тандалган nfc төлөм кызматы жөнүндө маалыматты алууга уруксат берүү."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication көзөмөлү"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Колдонмого \"Тынчымды алба\" режиминин конфигурациясын окуу жана жазуу мүмкүнчүлүгүн берет."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"уруксаттын колдонулушун көрүп баштоо"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Колдонмонун пайдаланылышына уруксат берүүгө мүмкүнчүлүк берет. Кадимки колдонмолорго эч качан талап кылынбашы керек."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"колдонмонун функцияларын көрүп баштоо"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Колдонуучуга функциялары тууралуу маалыматты көрүп баштоо мүмкүнчүлүгүн берет."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"үлгүнү жаздыруу ылдамдыгы жогору болгон сенсор дайындарынын үлгүсүнө мүмкүнчүлүк алуу"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Колдонмолорго сенсор дайындарынын үлгүсү 200 Герцтен жогору болгон үлгүлөрдү алууга уруксат берет"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Сырсөз эрежелерин коюу"</string>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index ca549ae..f1e5888 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -27,8 +27,6 @@
     <dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen>
     <dimen name="preference_widget_width">72dp</dimen>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
     <!-- Height of area above QQS where battery/time go -->
     <dimen name="quick_qs_offset_height">48dp</dimen>
     <!-- Default height of an action bar. -->
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index fe39b00..2d8aef7 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"ອະນຸຍາດໃຫ້ແອັບໂຄສະນາຫາອຸປະກອນ Bluetooth ທີ່ຢູ່ໃກ້ຄຽງໄດ້"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ກຳນົດຕຳແໜ່ງທີ່ສຳພັນກັນລະຫວ່າງອຸປະກອນ Ultra-Wideband ທີ່ຢູ່ໃກ້ຄຽງ"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ອະນຸຍາດໃຫ້ແອັບກຳນົດຕຳແໜ່ງທີ່ສຳພັນກັນລະຫວ່າງອຸປະກອນ Ultra-Wideband ທີ່ຢູ່ໃກ້ຄຽງ"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ໂຕ້ຕອບກັບອຸປະກອນ Wi‑Fi ທີ່ຢູ່ໃກ້ຄຽງ"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ອະນຸຍາດໃຫ້ແອັບໂຄສະນາ, ເຊື່ອມຕໍ່ ແລະ ກຳນົດຕຳແໜ່ງສຳພັນຂອງອຸປະກອນ Wi-Fi ທີ່ຢູ່ໃກ້ຄຽງໄດ້"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ຂໍ້ມູນບໍລິການການຈ່າຍເງິນ NFC ທີ່ຕ້ອງການ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ອະນຸຍາດໃຫ້ແອັບຮັບຂໍ້ມູນບໍລິການການຈ່າຍເງິນ NFC ທີ່ຕ້ອງການໄດ້ ເຊັ່ນ: ການຊ່ວຍເຫຼືອແບບລົງທະບຽນ ແລະ ປາຍທາງເສັ້ນທາງ."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ຄວບຄຸມ Near Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ອະນຸຍາດ​​ໃຫ້​ແອັບ​ອ່ານ​ ​ແລະ​ຂຽນການກນຳ​ດຄ່າ ບໍ່​ລົບ​ກວນ."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ເລີ່ມການໃຊ້ສິດອະນຸຍາດການເບິ່ງ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ອະນຸຍາດໃຫ້ຜູ້ຖືເລີ່ມການໃຊ້ສິດອະນຸຍາດສຳລັບແອັບໃດໜຶ່ງໄດ້. ແອັບປົກກະຕິບໍ່ຄວນຕ້ອງໃຊ້."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ເລີ່ມເບິ່ງຄຸນສົມບັດແອັບ"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ອະນຸຍາດໃຫ້ຜູ້ຖືເລີ່ມການເບິ່ງຂໍ້ມູນຄຸນສົມບັດສຳລັບແອັບໃດໜຶ່ງ."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ເຂົ້າເຖິງຂໍ້ມູນເຊັນເຊີໃນອັດຕາຕົວຢ່າງສູງ"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ອະນຸຍາດໃຫ້ແອັບສຸ່ມຕົວຢ່າງຂໍ້ມູນເຊັນເຊີໃນອັດຕາທີ່ຫຼາຍກວ່າ 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"ຕັ້ງຄ່າກົດຂອງລະຫັດຜ່ານ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index aa70bd2..66b30aa 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Programai leidžiama reklamuoti netoliese esančiuose „Bluetooth“ įrenginiuose"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"nustatyti apyt. netoliese es. itin plataus dažnio juostos įreng. poziciją"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Leisti programai nustatyti apytikslę netoliese esančių itin plataus dažnio juostos įrenginių poziciją"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"sąveikauti su „Wi‑Fi“ įrenginiais netoliese"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Leidžiama programai reklamuoti, prisijungti ir nustatyti apytikslę netoliese esančių „Wi-Fi“ įrenginių poziciją"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Pageidaujama ARL mokėjimo paslaugos informacija"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Programai leidžiama gauti pageidaujamą ARL mokamos paslaugos informaciją, pvz., užregistruotą pagalbą ir maršrutų tikslus."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"valdyti artimo lauko perdavimą (angl. „Near Field Communication“)"</string>
@@ -732,6 +734,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leidžiama programai skaityti ir rašyti „Do Not Disturb“ konfigūraciją."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pradėti peržiūrėti leidimo naudojimą"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leidžia savininkui pradėti naudoti programos leidimą. Įprastoms programoms to neturėtų prireikti."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"pradėti programos funkcijų peržiūrą"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Savininkui leidžiama pradėti programos funkcijų informacijos peržiūrą."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pasiekti jutiklių duomenis dideliu skaitmeninimo dažniu"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Programai leidžiama skaitmeninti jutiklių duomenis didesniu nei 200 Hz dažniu"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Nustatyti slaptažodžio taisykles"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index b36cf76..db4ae0b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -546,6 +546,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Atļauj lietotnei veikt reklamēšanu tuvumā esošās Bluetooth ierīcēs"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"novietojums starp tuvējām ultraplatjoslas ierīcēm"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Atļaut lietotnei noteikt relatīvo atrašanās vietu starp tuvumā esošām ultraplatjoslas ierīcēm"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Mijiedarbība ar tuvumā esošām Wi‑Fi ierīcēm"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Atļauj lietotnei nodot datus tuvumā esošām Wi‑Fi ierīcē, izveidot savienojumu ar tām un noteikt to relatīvo pozīciju."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informācija par vēlamo NFC maksājumu pakalpojumu"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ļauj lietotnei iegūt informāciju par vēlamo NFC maksājumu pakalpojumu, piemēram, par reģistrētajiem lietojumprogrammu ID un maršruta galamērķi."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrolē tuvlauka saziņu"</string>
@@ -729,6 +731,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ļauj lietotnei lasīt un rakstīt režīma “Netraucēt” konfigurāciju."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Datu skatīšana par izmantojamajām atļaujām"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ļauj atļaujas īpašniekam sākt lietotnes atļauju izmantošanu. Parastām lietotnēm tas nekad nav nepieciešams."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"Skatīt lietotnes funkcijas"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Lietotne ar šo atļauju var skatīt informāciju par citas lietotnes funkcijām."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"piekļuve sensoru datiem, izmantojot augstu iztveršanas frekvenci"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ļauj lietotnei iztvert sensoru datus, izmantojot frekvenci, kas ir augstāka par 200 Hz."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Paroles kārtulu iestatīšana"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 396097d..e863205 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Дозволува апликацијата да рекламира на уреди со Bluetooth во близина"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"да ја одреди релативната положба помеѓу уредите со ултраширок појас во близина"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дозволува апликацијата да ја одреди релативната положба помеѓу уредите со ултраширок појас во близина"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"да има интеракција со уредите со Wi‑Fi во близина"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Дозволува апликацијата да рекламира, да се поврзува и да ја одредува релативната положба уредите со Wi‑Fi во близина"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информации за претпочитаната услуга за плаќање преку NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозволува апликацијата да добие информации за претпочитаната услуга за плаќање преку NFC, како регистрирани помагала и дестинација на маршрутата."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"контролирај комуникација на блиско поле"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозволува апликацијата да чита и пишува конфигурација Не вознемирувај."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"започнете со користење на дозволата за приказ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозволува сопственикот да почне со користење на дозволата за апликација. Не треба да се користи за стандардни апликации."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"да почне со прегледување на функциите на апликацијата"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"му дозволува на сопственикот да почне со прегледување на податоците за функциите за некоја апликација"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"пристапува до податоците со висока фреквенција на семпл"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Дозволува апликацијата да пристапува до податоците од сензорите со фреквенција на семпл поголема од 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Постави правила за лозинката"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 1dfb50b..95f3fa1 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"സമീപമുള്ള Bluetooth ഉപകരണങ്ങളിലേക്ക് പരസ്യം ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"സമീപമുള്ള അൾട്രാ-വെെഡ്ബാൻഡ് ഉപകരണങ്ങൾ തമ്മിലുള്ള ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കൂ"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"സമീപമുള്ള അൾട്രാ-വെെഡ്ബാൻഡ് ഉപകരണങ്ങൾ തമ്മിലുള്ള ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"സമീപമുള്ള വൈഫൈ ഉപകരണങ്ങളുമായി ഇടപഴകുക"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"സമീപമുള്ള വൈഫൈ ഉപകരണത്തെ കാണിക്കാനും അവയിലേക്ക് കണക്റ്റ് ചെയ്യാനും അവയുടെ ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാനും ആപ്പിനെ അനുവദിക്കൂ"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"തിരഞ്ഞെടുത്ത NFC പേയ്‌മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"റൂട്ട് ലക്ഷ്യസ്ഥാനം, രജിസ്‌റ്റർ ചെയ്തിരിക്കുന്ന സഹായങ്ങൾ എന്നിവ പോലുള്ള, തിരഞ്ഞെടുത്ത NFC പേയ്‌മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ ലഭിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"സമീപ ഫീൽഡുമായുള്ള ആശയവിനിമയം നിയന്ത്രിക്കുക"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\'ശല്യപ്പെടുത്തരുത്\' കോൺഫിഗറേഷൻ വായിക്കുന്നതിനും എഴുതുന്നതിനും ആപ്പിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"അനുമതി ഉപയോഗം കാണാൻ ആരംഭിക്കുക"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ഒരു ആപ്പിനുള്ള അനുമതി ഉപയോഗം ആരംഭിക്കാൻ ഹോൾഡറിനെ അനുവദിക്കുന്നു. സാധാരണ ആപ്പുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ഉയർന്ന സാം‍പ്ലിംഗ് റേറ്റിൽ സെൻസർ ഡാറ്റ ആക്സസ് ചെയ്യുക"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz-നേക്കാൾ ഉയർന്ന റേറ്റിൽ സെൻസർ ഡാറ്റ സാമ്പിൾ ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"പാസ്‌വേഡ് നിയമങ്ങൾ സജ്ജീകരിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index f479b73..e8fdc15 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Аппад ойролцоох Bluetooth төхөөрөмжүүдэд сурталчлахыг зөвшөөрнө"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ойролцоох ультра өргөн зурвасын төхөөрөмжүүдийн хоорондох холбоотой байрлалыг тодорхойлох"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Аппад ойролцоох ультра өргөн зурвасын төхөөрөмжүүдийн хоорондох холбоотой байрлалыг тодорхойлохыг зөвшөөрөх"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ойролцоох Wi-Fi төхөөрөмжүүдтэй харилцах"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Аппад ойролцоох Wi-Fi төхөөрөмжүүдтэй холбоотой байрлалыг мэдэгдэх, холбох, тодорхойлохыг зөвшөөрнө"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Сонгосон NFC төлбөрийн үйлчилгээний мэдээлэл"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Бүртгүүлсэн төхөөрөмж болон маршрутын хүрэх цэг зэрэг сонгосон nfc төлбөрийн үйлчилгээний мэдээллийг авахыг аппад зөвшөөрдөг."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ойролцоо талбарын холбоог удирдах"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Апп-д Бүү саад бол тохируулгыг уншиж, бичихийг зөвшөөрөх"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"зөвшөөрлийн ашиглалтыг харж эхлэх"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Эзэмшигчид аппын зөвшөөрлөө ашиглаж эхлэхийг зөвшөөрдөг. Энгийн аппуудад шаардлагагүй."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"аппын онцлогуудыг үзэж эхлэх"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Аппын онцлогуудын мэдээллийг үзэж эхлэхийг эзэмшигчид зөвшөөрдөг."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"түүврийн өндөр хувиар мэдрэгчийн өгөгдөлд хандах"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Аппад 200 Гц-ээс их хувиар мэдрэгчийн өгөгдлийг түүвэрлэх боломжийг олгодог"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Нууц үгний дүрмийг тохируулах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 6aa9330..a57f60d 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"जवळपासच्या ब्लूटूथ डिव्‍हाइसवर जाहिरात करण्याची ॲपला परवानगी देते"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"जवळच्या अल्ट्रा-वाइडबँड डिव्हाइसदरम्यानचे संबंधित स्थान निर्धारित करा"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ॲपला जवळच्या अल्ट्रा-वाइडबँड डिव्हाइसदरम्यानचे संबंधित स्थान निर्धारित करण्याची अनुमती द्या"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"जवळपासच्या वाय-फाय डिव्हाइसशी संवाद साधा"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ॲपला जाहिरात करण्याची, कनेक्ट करण्याची आणि जवळपासच्या वाय-फाय डिव्हाइसचे संबंधित स्थान निर्धारित करण्याची परवानगी देते"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"प्राधान्यकृत NFC पेमेंट सेवा माहिती"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"नोंदणीकृत एड्स आणि मार्ग गंतव्यस्थान सारखी प्राधान्यकृत एनएफसी पेमेंट सेवेची माहिती मिळवण्यासाठी अ‍ॅपला अनुमती देते."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"फील्ड जवळील कम्युनिकेशन नियंत्रित करा"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अ‍ॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अ‍ॅप्ससाठी कधीही आवश्यकता नसते."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"उच्च नमुना दराने सेन्सर डेटा अ‍ॅक्सेस करते"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ॲपला २०० Hz पेक्षा जास्त दराने सेन्सर डेटाचा नमुना तयार करण्याची अनुमती देते"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 6021f70..50ca2d9 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Membenarkan apl menyiarkan kandungan kepada peranti Bluetooth yang berdekatan"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"tentukan kedudukan relatif antara peranti Ultrajalur Lebar berdekatan"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Benarkan apl menentukan kedudukan relatif antara peranti Ultrajalur Lebar berdekatan"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"berinteraksi dengan peranti Wi-Fi berdekatan"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Membenarkan apl mengiklankan, menyambung dan menentukan penempatan relatif peranti Wi-Fi berdekatan"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Maklumat Perkhidmatan Pembayaran NFC Pilihan"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Membenarkan apl mendapatkan maklumat perkhidmatan pembayaran nfc pilihan seperti bantuan berdaftar dan destinasi laluan."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"mengawal Komunikasi Medan Dekat"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Membenarkan apl membaca dan menulis konfigurasi Jangan Ganggu."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulakan lihat penggunaan kebenaran"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Membenarkan pemegang memulakan penggunaan kebenaran untuk apl. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"mula melihat ciri apl"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Membenarkan pemegang mula melihat maklumat ciri untuk apl."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"akses data penderia pada data pensampelan yang tinggi"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Membenarkan apl mengambil sampel data penderia pada kadar yang lebih besar daripada 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Tetapkan peraturan kata laluan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 8f415ec..6874414 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"အနီးတစ်ဝိုက်ရှိ ဘလူးတုသ်သုံးစက်များတွင် ကြော်ငြာရန် အက်ပ်အား ခွင့်ပြုမည်"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"အနီးတစ်ဝိုက်ရှိ ‘အလွန်ကျယ်ပြန့်သော လှိုင်းအလျားသုံးစက်များ’ ကြား ဆက်စပ်နေရာကို သတ်မှတ်ခြင်း"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"အနီးတစ်ဝိုက်ရှိ ‘အလွန်ကျယ်ပြန့်သော လှိုင်းအလျားသုံးစက်များ’ ကြား ဆက်စပ်နေရာကို သတ်မှတ်ရန် အက်ပ်ကို ခွင့်ပြုမည်"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"အနီးရှိ Wi-Fi စက်များနှင့် ပြန်လှန်တုံ့ပြန်ခြင်း"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"အက်ပ်ကို ကြော်ငြာရန်၊ ချိတ်ဆက်ရန်နှင့် အနီးတစ်ဝိုက်ရှိ Wi-Fi စက်များ၏ ဆက်စပ်နေရာကို သတ်မှတ်ရန် ခွင့်ပြုနိုင်သည်"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ဦးစားပေး NFC ငွေပေးချေမှုဆိုင်ရာ ဝန်ဆောင်မှု အချက်အလက်များ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"အက်ပ်အား ဦစားပေး NFC ငွေပေးချေမှုဆိုင်ရာ ဝန်ဆောင်မှု အချက်အလက်များဖြစ်သည့် မှတ်ပုံတင်ထားသော အကူအညီများနှင့် သွားလာရာ လမ်းကြောင်းတို့ကို ရယူရန် ခွင့်ပြုသည်။"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communicationအား ထိန်းချုပ်ရန်"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"မနှောင့်ယှက်ရန် ချိန်ညှိမှုကို အပ်ဖ်များ ဖတ်ခြင်း ပြင်ခြင်းပြုလုပ်နိုင်ရန် ခွင့်ပြုမည်။"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"အစမြင်ကွင်း ခွင့်ပြုချက် အသုံးပြုမှု"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"အက်ပ်တစ်ခုအတွက် ခွင့်ပြုချက်စတင်အသုံးပြုမှုကို ကိုင်ဆောင်သူအား ခွင့်ပြုသည်။ ပုံမှန်အက်ပ်များအတွက် ဘယ်သောအခါမျှ မလိုအပ်ပါ။"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"အက်ပ်ဝန်ဆောင်မှုများကို စတင်ကြည့်ခြင်း"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ဝန်ဆောင်မှုအချက်အလက်ကိုများကို ခွင့်ပြုချက်ရထားသည့် အက်ပ်အား စတင်ကြည့်နိုင်ရန် ခွင့်ပြုသည်။"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"နမူနာနှုန်းမြင့်သော အာရုံခံစနစ်ဒေတာကို သုံးပါ"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"၂၀၀ Hz နှုန်းထက်ပိုများသော အာရုံခံစနစ်ဒေတာကို နမူနာယူရန် အက်ပ်အား ခွင့်ပြုပါ"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"စကားဝှက်စည်းမျဥ်းကိုသတ်မှတ်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ec4378f..e26e41a 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Lar appen vise annonser til Bluetooth-enheter i nærheten"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"fastslå relativ posisjon mellom enheter som bruker ultrabredbånd"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"tillate at appen fastslår den relative posisjonen mellom enheter i nærheten som bruker ultrabredbånd"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"samhandle med Wi-Fi-enheter i nærheten"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lar appen annonsere, koble til og fastslå den relative posisjonen til Wi-Fi-enheter i nærheten"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasjon om prioritert NFC-betalingstjeneste"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Gir appen tilgang til informasjon om prioritert NFC-betalingstjeneste, for eksempel registrerte hjelpemidler og destinasjon."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontroller overføring av data med NFC-teknologi"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lar appen lese og skrive konfigurasjon av Ikke forstyrr."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start visning av bruk av tillatelser"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lar innehaveren starte bruk av tillatelser for en app. Dette skal aldri være nødvendig for vanlige apper."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"starte visning av appfunksjoner"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Lar innehaveren se informasjon om funksjonene for en app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"tilgang til sensordata ved høy samplingfrekvens"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lar appen samle inn sensordata ved en hastighet som er høyere enn 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Angi passordregler"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 9d74bb0..c6f3826 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"यो एपले नजिकै रहेका ब्लुटुथ चल्ने डिभाइसहरूमा विज्ञापन गर्न पाउँछ"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"नजिकै रहेका अल्ट्रा-वाइडब्यान्ड चल्ने डिभाइसहरूबिचको तुलनात्मक स्थान पत्ता लगाउने"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"यो एपलाई नजिकै रहेका अल्ट्रा-वाइडब्यान्ड चल्ने डिभाइसहरूबिचको तुलनात्मक स्थान पत्ता लगाउन दिनुहोस्"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Wi-Fi चल्ने नजिकै रहेका डिभाइसहरूमा चलाउन दिन्छ"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"यसले एपलाई Wi-Fi चल्ने नजिकै रहेका डिभाइसहरूमा विज्ञापन गर्न, कनेक्ट गर्न र सापेक्ष स्थिति निर्धारण गर्न दिन्छ"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC भुक्तानी सेवासम्बन्धी रुचाइएको जानकारी"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"यसले एपलाई दर्ता गरिएका सहायता तथा मार्गको गन्तव्य जस्ता रुचाइएका NFC भुक्तानी सेवासम्बन्धी जानकारी प्राप्त गर्न दिन्छ।"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"नजिक क्षेत्र संचार नियन्त्रणहरू"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"बाधा नपुर्याउँनुहोस् कन्फिगरेसन पढ्न र लेख्‍नको लागि एपलाई अनुमति दिनुहोस्।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"हेर्ने अनुमतिको प्रयोग सुरु गर्नुहोस्"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"वाहकलाई कुनै एपसम्बन्धी अनुमतिको प्रयोग सुरु गर्न दिन्छ। साधारण एपहरूलाई कहिल्यै आवश्यक नपर्नु पर्ने हो।"</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"नमुना लिने उच्च दरमा सेन्सरसम्बन्धी डेटा प्रयोग गर्ने"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"यो अनुमति दिइएमा एपले २०० हर्जभन्दा बढी दरमा सेन्सरसम्बन्धी डेटाको नमुना लिन सक्छ"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियमहरू मिलाउनुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 06ef43e..9bd6a28 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Hiermee kan de app adverteren op bluetooth-apparaten in de buurt"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"relatieve positie tussen ultrabreedbandapparaten in de buurt bepalen"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"De app toestaan om de relatieve positie tussen ultrabreedbandapparaten in de buurt te bepalen"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactie met wifi-apparaten in de buurt"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Hiermee kan de app adverteren op, verbinding maken met en de relatieve positie bepalen van wifi-apparaten in de buurt"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informatie over voorkeursservice voor NFC-betaling"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Hiermee kun je zorgen dat de app informatie krijgt over de voorkeursservice voor NFC-betaling, zoals geregistreerde hulpmiddelen en routebestemmingen."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication regelen"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Hiermee kan de app configuratie voor Niet storen lezen en schrijven."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rechtengebruik starten"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Hiermee kan de houder het rechtengebruik voor een app starten. Nooit vereist voor normale apps."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"app-functies bekijken"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Hiermee kan de houder informatie over functies bekijken voor een app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"toegang krijgen tot sensorgegevens met een hoge samplingsnelheid"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Hiermee kan de app sensorgegevens samplen met een snelheid die hoger is dan 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Wachtwoordregels instellen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index edc206b..cc368a0 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"ଆଖପାଖର ବ୍ଲୁଟୁଥ୍ ଡିଭାଇସଗୁଡ଼ିକରେ ବିଜ୍ଞାପନ ଦେବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ଆଖପାଖର ଅଲଟ୍ରା-ୱାଇଡବ୍ୟାଣ୍ଡ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ନିର୍ଦ୍ଧାରଣ କର"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ଆଖପାଖର ଅଲଟ୍ରା-ୱାଇଡବ୍ୟାଣ୍ଡ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ନିର୍ଦ୍ଧାରଣ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ଆଖପାଖର ୱାଇ-ଫାଇ ଡିଭାଇସଗୁଡ଼ିକ ସହ ଇଣ୍ଟରାକ୍ଟ କରନ୍ତୁ"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ଆଖପାଖର ୱାଇ-ଫାଇ ଡିଭାଇସରେ ବିଜ୍ଞାପନ ଦେବା, ତା ସହ ସଂଯୋଗ କରିବା ଓ ତା’ର ଆପେକ୍ଷିକ ଅବସ୍ଥିତି ନିର୍ଦ୍ଧାରଣ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ପସନ୍ଦର NFC ପେମେଣ୍ଟ ସେବା ସୂଚନା"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ପଞ୍ଜିକୃତ ଯନ୍ତ୍ର ଏବଂ ମାର୍ଗ ଲକ୍ଷସ୍ଥଳ ପରି ପସନ୍ଦର nfc ପେମେଣ୍ଟ ସେବା ସୂଚନା ପାଇବାକୁ ଆପ୍ ଅନୁମତି କରିଥାଏ।"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ନିଅର୍ ଫିଲ୍ଡ କମ୍ୟୁନିକେଶନ୍ ଉପରେ ନିୟନ୍ତ୍ରଣ ରଖନ୍ତୁ"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" କନଫିଗରେଶନ୍‍ ପଢ଼ିବା ତଥା ଲେଖିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ଅନୁମତି ବ୍ୟବହାର ଦେଖିବା ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ଏକ ଆପ୍ ପାଇଁ ଅନୁମତିର ବ୍ୟବହାର ଆରମ୍ଭ କରିବାକୁ ଧାରକକୁ ଅନୁମତି ଦେଇଥାଏ। ସାଧାରଣ ଆପ୍‌ଗୁଡ଼ିକ ପାଇଁ ଏହା ଆବଶ୍ୟକ ନୁହେଁ।"</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ଏକ ଉଚ୍ଚ ନମୁନାକରଣ ରେଟରେ ସେନ୍ସର୍ ଡାଟାକୁ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ଠାରୁ ଅଧିକ ଏକ ରେଟରେ ସେନ୍ସର୍ ଡାଟାର ନମୁନା ନେବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"ପାସ୍‌ୱର୍ଡ ନିୟମାବଳୀ ସେଟ୍ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 7f7c6b6..9090830 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -543,6 +543,10 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ \'ਤੇ ਵਿਗਿਆਪਨ ਦੇਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ਨਜ਼ਦੀਕੀ ਅਲਟ੍ਰਾ-ਵਾਈਡਬੈਂਡ ਡੀਵਾਈਸਾਂ ਵਿਚਾਲੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨਿਰਧਾਰਿਤ ਕਰੋ"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਅਲਟ੍ਰਾ-ਵਾਈਡਬੈਂਡ ਡੀਵਾਈਸਾਂ ਦੇ ਵਿਚਾਲੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+    <!-- no translation found for permlab_nearby_wifi_devices (392774237063608500) -->
+    <skip />
+    <!-- no translation found for permdesc_nearby_wifi_devices (3054307728646332906) -->
+    <skip />
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ਤਰਜੀਹੀ NFC ਭੁਗਤਾਨਸ਼ੁਦਾ ਸੇਵਾ ਜਾਣਕਾਰੀ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ਐਪ ਨੂੰ ਤਰਜੀਹੀ NFC ਭੁਗਤਾਨਸ਼ੁਦਾ ਸੇਵਾ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰਨ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਕਿ ਰਜਿਸਟਰ ਕੀਤੇ ਸਾਧਨ ਅਤੇ ਮੰਜ਼ਿਲ ਰਸਤਾ।"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ਨਜ਼ਦੀਕੀ ਖੇਤਰ ਸੰਚਾਰ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string>
@@ -726,6 +730,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ਐਪ ਨੂੰ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਕੌਂਫਿਗਰੇਸ਼ਨ ਨੂੰ ਪੜ੍ਹਨ ਅਤੇ ਲਿਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ਇਜਾਜ਼ਤ ਵਰਤੋਂ ਦੇਖਣਾ ਸ਼ੁਰੂ ਕਰੋ"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ਧਾਰਕ ਨੂੰ ਕਿਸੇ ਹੋਰ ਐਪ ਲਈ ਇਜਾਜ਼ਤ ਵਰਤੋਂ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਦਿੰਦਾ ਹੈ। ਸਧਾਰਨ ਐਪਾਂ ਲਈ ਕਦੇ ਵੀ ਲੋੜੀਂਦਾ ਨਹੀਂ ਹੋਵੇਗਾ।"</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ਉੱਚ ਸੈਂਪਲਿੰਗ ਰੇਟ \'ਤੇ ਸੈਂਸਰ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ਐਪ ਨੂੰ 200 Hz ਤੋਂ ਵੱਧ ਦੀ ਦਰ \'ਤੇ ਸੈਂਸਰ ਡਾਟੇ ਦਾ ਨਮੂਨਾ ਲੈਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"ਪਾਸਵਰਡ ਨਿਯਮ ਸੈੱਟ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 176dffe..af2b120 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Zezwala na kierowanie przez aplikację informacji do urządzeń Bluetooth w pobliżu"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"określanie względnego położenia urządzeń ultraszerokopasmowych w pobliżu"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Zezwól na określanie przez aplikację względnego położenia urządzeń ultraszerokopasmowych w pobliżu"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcje z urządzeniami Wi-Fi w pobliżu"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Zezwala aplikacji na przesyłanie informacji o sobie, łączenie się z urządzeniami Wi‑Fi w pobliżu i określanie ich względnego położenia"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacje o preferowanych usługach płatniczych NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Pozwala aplikacji uzyskiwać informacje o preferowanych usługach płatniczych NFC, np. zarejestrowanych pomocach i miejscach docelowych tras."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrolowanie łączności Near Field Communication"</string>
@@ -732,6 +734,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Pozwala aplikacji na odczyt i zmianę konfiguracji trybu Nie przeszkadzać."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rozpocząć wyświetlanie użycia uprawnień"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umożliwia rozpoczęcie korzystania z uprawnienia dotyczącego danej aplikacji jego posiadaczowi. Zwykłe aplikacje nie powinny potrzebować tego uprawnienia."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"rozpoczęcie wyświetlania funkcji aplikacji"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Umożliwia posiadaczowi rozpoczęcie przeglądania informacji o funkcjach aplikacji."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"dostęp do danych czujnika z wysoką częstotliwością"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Zezwala aplikacji na pobieranie próbek danych z czujnika z częstotliwością wyższą niż 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Określ reguły hasła"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index a4eb8f6..1f9c0775 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que o app seja anunciado em dispositivos Bluetooth por perto"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permitir que o app determine o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi-Fi por perto"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app veicule anúncios, conecte-se à rede e determine a posição relativa de dispositivos Wi-Fi por perto"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações preferidas de serviço de pagamento por NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que o app acesse as informações preferidas de serviço de pagamento por NFC, como auxílios registrados ou destinos de trajetos."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar a comunicação a curta distância"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ver recursos do app"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que o sistema comece a ver as informações de recursos de um app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acessar os dados do sensor em uma taxa de amostragem elevada"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que o app receba amostras de dados do sensor em uma taxa maior que 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras para senha"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 7e9520d..7006273 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que a app transmita para dispositivos Bluetooth próximos"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar posição relativa dispos. de banda ultralarga próximos"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permita que a app determine a posição relativa entre os dispositivos de banda ultralarga próximos"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi‑Fi próximos"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que a app anuncie, estabeleça ligação e determine a posição relativa de dispositivos Wi‑Fi próximos"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações de serviços de pagamento com NFC preferenciais"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que a app obtenha informações de serviços de pagamento com NFC preferenciais, como apoios registados e destino da rota."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlo Near Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite à app ler e alterar a configuração de Não incomodar"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar utilização da autorização de visualização"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o titular inicie a utilização de autorizações para uma app. Nunca deverá ser necessário para aplicações normais."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"começar a ver as funcionalidades da app"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que o titular comece a ver as informações das funcionalidades de uma app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"aceder aos dados de sensores a uma taxa de amostragem elevada"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que a app obtenha uma amostra dos dados de sensores a uma taxa superior a 200 Hz."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras de palavra-passe"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a4eb8f6..1f9c0775 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que o app seja anunciado em dispositivos Bluetooth por perto"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permitir que o app determine o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi-Fi por perto"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app veicule anúncios, conecte-se à rede e determine a posição relativa de dispositivos Wi-Fi por perto"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações preferidas de serviço de pagamento por NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que o app acesse as informações preferidas de serviço de pagamento por NFC, como auxílios registrados ou destinos de trajetos."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar a comunicação a curta distância"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ver recursos do app"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite que o sistema comece a ver as informações de recursos de um app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acessar os dados do sensor em uma taxa de amostragem elevada"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que o app receba amostras de dados do sensor em uma taxa maior que 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras para senha"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index adb5618..5bbad82 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -546,6 +546,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite aplicației să difuzeze anunțuri pe dispozitive Bluetooth din apropiere"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permiteți-i aplicației să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"să interacționeze cu dispozitive Wi‑Fi din apropiere"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite aplicației să se conecteze la, să afișeze date și să stabilească poziția relativă pentru dispozitive Wi-Fi din apropiere"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informații despre serviciul de plăți NFC preferat"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite aplicației să obțină informații despre serviciul de plăți NFC preferat, de exemplu, identificatorii de aplicație înregistrați și destinația traseului."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlare schimb de date prin Near Field Communication"</string>
@@ -729,6 +731,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite aplicației să citească și să scrie configurația Nu deranja."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"porniți folosirea permisiunii de vizualizare"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite proprietarului să pornească folosirea permisiunii pentru o aplicație. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"începeți să vedeți funcțiile aplicației"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permite proprietarului să înceapă să vadă informațiile despre funcții pentru o aplicație."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"să acceseze date de la senzori la o rată de eșantionare mare"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite aplicației să colecteze date de la senzori la o rată de eșantionare de peste 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Să seteze reguli pentru parolă"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index acdc7db..b8f190f 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Приложение сможет передавать рекламу на устройства Bluetooth поблизости."</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Определять относительное позиционирование устройств с технологией сверхширокополосной связи поблизости"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Приложение сможет определять относительное позиционирование устройств с технологией сверхширокополосной связи поблизости"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Взаимодействие с устройствами Wi‑Fi поблизости"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Приложение сможет передавать данные на устройства Wi‑Fi рядом, подключаться к ним и определять их примерное местоположение."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Сведения о предпочтительном платежном сервисе NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Приложение сможет получать сведения о предпочтительном платежном сервисе NFC (например, зарегистрированные идентификаторы AID и конечный пункт маршрута)."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Управление NFC-модулем"</string>
@@ -732,6 +734,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Открывает приложению доступ к настройкам режима \"Не беспокоить\" и позволяет изменять их."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Просмотр данных об используемых разрешениях"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Приложение получит доступ к данным об используемых разрешениях. Это разрешение не требуется обычным приложениям."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"Просмотр функций приложения"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Позволяет просматривать информацию о функциях приложения."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Доступ к данным датчиков при высокой частоте дискретизации"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Приложение сможет считывать данные датчиков на частоте более 200 Гц."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Настройка правил для паролей"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index f565433..6b1b614 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"අවට ඇති බ්ලූටූත් උපාංගවලට වෙළඳ ප්‍රචාරණය කිරීමට යෙදුමට ඉඩ දෙයි"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"අවට අල්ට්‍රා-වයිඩ්බෑන්ඩ් උපාංග අතර සාපේක්ෂ පිහිටීම නිර්ණය"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"අවට ඇති අල්ට්‍රා-වයිඩ්බෑන්ඩ් උපාංග අතර සාපේක්ෂ පිහිටීම නිර්ණය කිරීමට යෙදුමට ඉඩ දීම"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"අවට Wi‑Fi උපාංග සමග අන්තර්ක්‍රියා කරන්න"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"වෙළඳ දැන්වීම් පළ කිරීමට, සම්බන්ධ වීමට සහ අවට ඇති Wi-Fi උපාංගවල සාපේක්ෂ පිහිටීම නිර්ණය කිරීමට යෙදුමට ඉඩ දෙයි"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"කැමති NFC ගෙවීම් සේවා තොරතුරු"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ලියාපදිංචි කළ ආධාර සහ ගමන් මාර්ග ගමනාන්ත වැනි කැමති nfc ගෙවීම් සේවා තොරතුරු ලබා ගැනීමට යෙදුමට ඉඩ දෙයි."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ආසන්න ක්ෂේත්‍ර සන්නිවේදනය පාලනය කරන්න"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"බාධා නොකරන්න වින්‍යාස කිරීම කියවීමට සහ ලිවීමට යෙදුමට ඉඩ දෙයි."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"අවසර භාවිතය බැලීමට ආරම්භ කරන්න"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"තබා සිටින්නාට යෙදුමක් සඳහා අවසර භාවිතය ආරම්භ කිරීමට ඉඩ දෙයි. සාමාන්‍ය යෙදුම් සඳහා කිසි විටෙක අවශ්‍ය නොවිය යුතු ය."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"යෙදුම බලන්න විශේෂාංග ආරම්භ කරන්න"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"යෙදුමක් සඳහා විශේෂාංග තොරතුරු බැලීම ආරම්භ කිරීමට දරන්නාට ඉඩ දෙන්න."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ඉහළ නියැදි කිරීමේ වේගයකින් සංවේදක දත්ත වෙත පිවිසෙන්න"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ට වඩා වැඩි වේගයකින් සංවේදක දත්ත නියැදි කිරීමට යෙදුමට ඉඩ දෙයි"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"මුරපද නීති සකස් කිරීම"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index aa23ee3..081f901 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Umožňuje aplikácii zobrazovať reklamu v zariadeniach s rozhraním Bluetooth v okolí"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"určovať relatívnu polohu medzi zariadeniami so ultraširokopásmovým pripojením v okolí"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Povoľte aplikácii určovať relatívnu polohu medzi zariadeniami s ultraširokopásmovým pripojením v okolí"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcia so zariadeniami Wi-Fi v okolí"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Umožňuje aplikácii oznamovať a rozpoznávať relatívnu polohu zariadení Wi‑Fi v okolí a pripájať sa k nim"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferované informácie platenej služby NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Umožňuje aplikácii získavať preferované informácie platenej služby NFC, napríklad o registrovanej pomoci a trasách k cieľu."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ovládať technológiu NFC"</string>
@@ -732,6 +734,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikácii čítať a zapisovať konfiguráciu režimu bez vyrušení."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"spustenie používania povolenia na zobrazenie"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje držiteľovi spustiť používanie povolenia aplikáciou. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"zobrazenie informácií o funkciách aplikácie"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Umožňuje držiteľovi zobraziť informácie o funkciách aplikácie."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"prístup k dátam senzorom s vysokou vzorkovacou frekvenciou"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Umožňuje aplikácii vzorkovať dáta senzorov s frekvenciou vyššou ako 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Nastaviť pravidlá pre heslo"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 17e1588..a6f8257 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Aplikaciji dovoljuje oddajanje napravam Bluetooth v bližini."</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"določanje relativne oddaljenosti med napravami UWB v bližini"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Aplikaciji dovoli, da določi relativno oddaljenost med napravami UWB v bližini."</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"komunikacijo z napravami Wi‑Fi v bližini"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Aplikaciji dovoljuje objavljanje in določanje relativnega položaja naprav Wi‑Fi v bližini ter povezovanje z njimi."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Podatki o prednostni storitvi za plačevanje prek povezave NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Aplikaciji omogoča pridobivanje podatkov o prednostni storitvi za plačevanje prek povezave NFC, kot so registrirani pripomočki in cilj preusmeritve."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"nadzor nad komunikacijo s tehnologijo bližnjega polja"</string>
@@ -732,6 +734,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Aplikaciji omogoča branje in pisanje konfiguracije načina »ne moti«."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"začetek uporabe dovoljenja za ogledovanje"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Imetniku omogoča začetek uporabe dovoljenj za aplikacijo. Nikoli ni potrebno za navadne aplikacije."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"začetek ogledovanja funkcij aplikacije"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Imetniku omogoča začetek ogledovanja informacij o funkcijah poljubne aplikacije."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"dostop do podatkov tipal z večjo hitrostjo vzorčenja"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikaciji dovoljuje, da vzorči podatke tipal s hitrostjo, večjo od 200 Hz."</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Nastavitev pravil za geslo"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 379aaf2..d42c91e 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Lejon që aplikacioni të reklamojë në pajisjet me Bluetooth në afërsi"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"të përcaktojë pozicionin e përafërt mes pajisjeve në afërsi me brezin ultra të gjerë"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Lejo që aplikacioni të përcaktojë pozicionin e përafërt mes pajisjeve në afërsi me brezin ultra të gjerë"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"të ndërveprojë me pajisjet Wi-Fi në afërsi"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lejon që aplikacioni të reklamojë, të lidhet dhe të përcaktojë pozicionin relativ të pajisjeve Wi-Fi në afërsi"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacionet për shërbimin e preferuar të pagesës me NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lejon aplikacionin të marrë informacione për shërbimin e preferuar të pagesës me NFC si p.sh. ndihmat e regjistruara dhe destinacionin e itinerarit."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrollo \"Komunikimin e fushës në afërsi\" NFC"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lejon aplikacionin të lexojë dhe shkruajë konfigurimin e \"Mos shqetëso\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"nis përdorimin e lejes për shikimin"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lejon që mbajtësi të nisë përdorimin e lejeve për një aplikacion. Nuk duhet të nevojitet asnjëherë për aplikacionet normale."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"fillojë shikimin e veçorive të aplikacionit"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Lejon që zotëruesi të fillojë të shikojë informacionin e veçorive për një aplikacion."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"qasu te të dhënat e sensorit me një shpejtësi kampionimi më të lartë"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lejon aplikacionin të mbledhë shembujt e të dhënave të sensorit me shpejtësi më të lartë se 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Cakto rregullat e fjalëkalimit"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 823eae9..b04ddaf 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -546,6 +546,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Дозвољава апликацији да се оглашава на Bluetooth уређајима у близини"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"одређивање раздаљине између уређаја ултра-широког појаса у близини"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дозвољава апликацији да одређује релативну раздаљину између уређаја ултра-широког појаса у близини"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"интеракција са WiFi уређајима у близини"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Дозвољава апликацији да се оглашава, повезује и утврђује релативну позицију WiFi уређаја у близини"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информације о жељеној NFC услузи за плаћање"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозвољава апликацији да преузима информације о жељеној NFC услузи за плаћање, попут регистрованих идентификатора апликација и одредишта преусмеравања."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"контрола комуникације у ужем пољу (Near Field Communication)"</string>
@@ -729,6 +731,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозвољава апликацији да чита и уписује конфигурацију подешавања Не узнемиравај."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"почетак коришћења дозволе за преглед"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозвољава власнику да започне коришћење дозволе за апликацију. Никада не би требало да буде потребна за уобичајене апликације."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"покретање приказа функција апликације"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Дозвољава кориснику да започне прегледање информација о функцијама апликације."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"приступ подацима сензора при великој брзини узорковања"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Дозвољава апликацији да узима узорак података сензора при брзини већој од 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Подешавање правила за лозинку"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 2785f90..357f98a 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Tillåter appen att annonsera till Bluetooth-enheter i närheten"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"fastställa relativ position för Ultra Wideband-enheter i närheten"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tillåt att appen fastställer den relativa positionen mellan Ultra Wideband-enheter i närheten"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagera med wifi-enheter i närheten"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Tillåter appen att sända ut till, ansluta till och fastställa relativ position för wifi-enheter i närheten"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Information kopplad till standardtjänsten för NFC-betalning"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tillåter att appen hämtar information kopplad till standardtjänsten för NFC-betalning, till exempel registrerade hjälpmedel och ruttdestinationer."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrollera närfältskommunikationen"</string>
@@ -726,6 +728,10 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ger appen läs- och skrivbehörighet till konfigurationen för Stör ej."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"börja visa behörighetsanvändningen"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Gör att innehavaren kan öppna behörighetsanvändning för en app. Ska inte behövas för vanliga appar."</string>
+    <!-- no translation found for permlab_startViewAppFeatures (7955084203185903001) -->
+    <skip />
+    <!-- no translation found for permdesc_startViewAppFeatures (7207240860165206107) -->
+    <skip />
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"åtkomst till sensordata med en hög samplingsfrekvens"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tillåter att appen får åtkomst till sensordata med en högre samplingsfrekvens än 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Ange lösenordsregler"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 72774ab..84daf98 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Huruhusu programu itangaze kwenye vifaa vyenye Bluetooth vilivyo karibu"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"kubainisha nafasi kati ya vifaa vyenye Bendi Pana Zaidi vilivyo karibu"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ruhusu programu ibainishe nafasi kati ya vifaa vyenye Bendi Pana Zaidi vilivyo karibu"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"tumia vifaa vya Wi‑Fi vilivyo karibu"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Huruhusu programu kutangaza, kuunganisha na kubaini mahali palipokadiriwa vilipo vifaa vya Wi-Fi vilivyo karibu"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Maelezo ya Huduma Inayopendelewa ya Malipo ya NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Huruhusu programu kupata maelezo ya huduma inayopendelewa ya malipo ya nfc kama vile huduma zilizosajiliwa na njia."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kudhibiti Mawasiliano ya Vifaa Vilivyokaribu (NFC)"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Inaruhusu programu kusoma na kuandika usanidi wa kipengee cha Usinisumbue."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"anzisha kipengele cha kuona matumizi ya ruhusa"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Huruhusu kishikiliaji kuanzisha matumizi ya ruhusa ya programu. Haipaswi kuhitajika kwa ajili ya programu za kawaida."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"anzisha kipengele cha kuangalia vipengele vya programu"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Huruhusu mmiliki kuanza kuangalia maelezo ya vipengele vya programu."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"fikia data ya vitambuzi kwa kasi ya juu ya sampuli"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Huruhusu programu kujaribu sampuli ya data ya vitambuzi kwa kasi inayozidi Hz 200"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Kuweka kanuni za nenosiri"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 8f8325a..6e4148d 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"அருகிலுள்ள புளூடூத் சாதனங்களுக்குத் தெரியப்படுத்த ஆப்ஸை அனுமதிக்கும்"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"அருகிலுள்ள அல்ட்ரா-வைடுபேண்ட் சாதனங்களுக்கிடையிலான தூரத்தைத் தீர்மானித்தல்"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"அருகிலுள்ள அல்ட்ரா-வைடுபேண்ட் சாதனங்களுக்கிடையிலான தூரத்தைத் தீர்மானிக்க ஆப்ஸை அனுமதிக்கும்"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"அருகிலுள்ள வைஃபை சாதனங்களுடன் தொடர்பில் இருக்கும்"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"அருகிலுள்ள வைஃபை சாதனங்களைத் தெரியப்படுத்தவும் இணைக்கவும் இருப்பிடத்தைத் தீர்மானிக்கவும் இது ஆப்ஸை அனுமதிக்கும்"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"விருப்பமான NFC பேமெண்ட் சேவை தொடர்பான தகவல்கள்"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"பதிவுசெய்யப்பட்ட கருவிகள், சேருமிடத்திற்கான வழி போன்ற விருப்பமான NFC பேமெண்ட் சேவை தொடர்பான தகவல்களைப் பெற ஆப்ஸை அனுமதிக்கிறது."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"குறுகிய இடைவெளி தகவல்பரிமாற்றத்தைக் கட்டுப்படுத்துதல்"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"தொந்தரவு செய்ய வேண்டாம் உள்ளமைவைப் படிக்கவும் எழுதவும், ஆப்ஸை அனுமதிக்கிறது."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"அனுமதி உபயோகத்தை அணுகுதல்"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ஆப்ஸிற்கான அனுமதி உபயோகத்தை ஹோல்டருக்கு வழங்கும். இயல்பான ஆப்ஸிற்கு இது எப்போதுமே தேவைப்படாது."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ஆப்ஸின் அம்சங்களைப் பார்க்கத் தொடங்குதல்"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ஆப்ஸின் அம்சங்கள் குறித்த தகவல்களைப் பார்ப்பதற்கான அனுமதியை ஹோல்டருக்கு வழங்கும்."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"அதிகளவிலான சாம்பிளிங் ரேட்டில் சென்சார் தரவை அணுகுதல்"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 ஹெர்ட்ஸ்க்கும் அதிகமான வீதத்தில் சென்சார் தரவை மாதிரியாக்க ஆப்ஸை அனுமதிக்கும்"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"கடவுச்சொல் விதிகளை அமைக்கவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index c216de3..c3e5a66 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -432,7 +432,7 @@
     <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ఈ యాప్ మీ టాబ్లెట్‌లో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్‌లన్నీ చదవగలదు మరియు మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ఈ యాప్‌ మీ Android TV పరికరంలో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్‌లన్నీ చదవగలదు, మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
     <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"ఈ యాప్ మీ ఫోన్‌లో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్‌లన్నీ చదవగలదు మరియు మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
-    <string name="permlab_writeCalendar" msgid="6422137308329578076">"యజమానికి తెలియకుండానే క్యాలెండర్ ఈవెంట్‌లను జోడించి లేదా సవరించి, అతిథులకు ఇమెయిల్ పంపడం"</string>
+    <string name="permlab_writeCalendar" msgid="6422137308329578076">"యజమానికి తెలియకుండానే క్యాలెండర్ ఈవెంట్‌లను జోడించి లేదా సవరించి, అతిథులకు ఈమెయిల్‌ పంపడం"</string>
     <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్‌లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్‌లను పంపగలదు లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
     <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్‌లను పంపగలదు లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
     <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్‌లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్‌లను పంపగలదు, లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"సమీపంలోని బ్లూటూత్ పరికరాలలో అడ్వర్టయిజ్ చేయడానికి యాప్‌కు అనుమతిని ఇస్తుంది"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"సమీపంలోని అల్ట్రా-వైడ్‌బ్యాండ్ పరికరాల మధ్య సాపేక్ష స్థానాన్ని నిర్ణయించండి"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"సమీపంలోని అల్ట్రా-వైడ్‌బ్యాండ్ పరికరాల మధ్య సాపేక్ష స్థానాన్ని నిర్ణయించడానికి యాప్‌ను అనుమతించండి"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"సమీపంలోని Wi-Fi పరికరాలతో ఇంటరాక్ట్ చేస్తుంది"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"అడ్వర్టయిజ్, కనెక్ట్ చేయడానికి, సమీపంలోని Wi-Fi పరికరాల సంబంధిత పొజిషన్‌ను నిర్ణయించడానికి యాప్‌ను అనుమతిస్తుంది"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ప్రాధాన్యత ఇవ్వబడిన NFC చెల్లింపు సేవల సమాచారం"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ప్రాధాన్యత ఇవ్వబడిన NFC చెల్లింపు సేవల సమాచారాన్ని, అంటే రిజిస్టర్ చేయబడిన సహాయక సాధనాలు, మార్గం, గమ్యస్థానం వంటి వాటిని పొందేందుకు యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"సమీప క్షేత్ర కమ్యూనికేషన్‌ను నియంత్రించడం"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"యాప్‌నకు అనుమతి వినియోగాన్ని ప్రారంభించడానికి హోల్డర్‌‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ ఇటువంటి అనుమతి అవసరం ఉండదు."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"యాప్ ఫీచర్‌లను చూడటాన్ని ప్రారంభించండి"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"యాప్ ఫీచర్‌ల సమాచారాన్ని చూడటాన్ని ప్రారంభించడానికి హోల్డర్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"అధిక శాంపిల్ రేటు వద్ద సెన్సార్ డేటాను యాక్సెస్ చేయండి"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz కంటే ఎక్కువ రేట్ వద్ద శాంపిల్ సెన్సార్ డేటాకు యాప్‌ను అనుమతిస్తుంది"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"పాస్‌వర్డ్ నియమాలను సెట్ చేయండి"</string>
@@ -933,7 +937,7 @@
     <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"ఖాతా అన్‌లాక్"</string>
     <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేశారు"</string>
     <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"అన్‌లాక్ చేయడానికి, మీ Google ఖాతాతో సైన్ ఇన్ చేయండి."</string>
-    <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"వినియోగదారు పేరు (ఇమెయిల్)"</string>
+    <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"వినియోగదారు పేరు (ఈమెయిల్‌)"</string>
     <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"పాస్‌వర్డ్"</string>
     <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"సైన్ ఇన్ చేయి"</string>
     <string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"వినియోగదారు పేరు లేదా పాస్‌వర్డ్ చెల్లదు."</string>
@@ -1673,7 +1677,7 @@
     <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"పిన్‌ కోడ్‌లు సరిపోలలేదు"</string>
     <string name="kg_login_too_many_attempts" msgid="699292728290654121">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేశారు"</string>
     <string name="kg_login_instructions" msgid="3619844310339066827">"అన్‌లాక్ చేయడానికి, మీ Google ఖాతాతో సైన్ ఇన్ చేయండి."</string>
-    <string name="kg_login_username_hint" msgid="1765453775467133251">"వినియోగదారు పేరు (ఇమెయిల్)"</string>
+    <string name="kg_login_username_hint" msgid="1765453775467133251">"వినియోగదారు పేరు (ఈమెయిల్‌)"</string>
     <string name="kg_login_password_hint" msgid="3330530727273164402">"పాస్‌వర్డ్"</string>
     <string name="kg_login_submit_button" msgid="893611277617096870">"సైన్ ఇన్ చేయి"</string>
     <string name="kg_login_invalid_input" msgid="8292367491901220210">"చెల్లని వినియోగదారు పేరు లేదా పాస్‌వర్డ్."</string>
@@ -1688,9 +1692,9 @@
     <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"మీరు టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ ఆటోమేటిక్‌కు రీసెట్ చేయబడుతుంది."</string>
     <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
     <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ ఆటోమేటిక్‌కు రీసెట్ చేయబడుతుంది."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టాబ్లెట్‌ను అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత మీ Android TV పరికరాన్ని ఇమెయిల్ ఖాతా ద్వారా అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని కోరడం జరుగుతుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్‌ను అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఈమెయిల్‌ ఖాతాను ఉపయోగించి మీ టాబ్లెట్‌ను అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత మీ Android TV పరికరాన్ని ఈమెయిల్‌ ఖాతా ద్వారా అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని కోరడం జరుగుతుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఈమెయిల్‌ ఖాతాను ఉపయోగించి మీ ఫోన్‌ను అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"తీసివేయి"</string>
     <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"వాల్యూమ్‌ను సిఫార్సు చేయబడిన స్థాయి కంటే ఎక్కువగా పెంచాలా?\n\nసుదీర్ఘ వ్యవధుల పాటు అధిక వాల్యూమ్‌లో వినడం వలన మీ వినికిడి శక్తి దెబ్బ తినవచ్చు."</string>
@@ -2056,7 +2060,7 @@
     <string name="autofill_save_type_payment_card" msgid="6555012156728690856">"చెల్లింపు కార్డ్"</string>
     <string name="autofill_save_type_generic_card" msgid="1019367283921448608">"కార్డ్"</string>
     <string name="autofill_save_type_username" msgid="1018816929884640882">"వినియోగదారు పేరు"</string>
-    <string name="autofill_save_type_email_address" msgid="1303262336895591924">"ఇమెయిల్ అడ్రస్‌"</string>
+    <string name="autofill_save_type_email_address" msgid="1303262336895591924">"ఈమెయిల్‌ అడ్రస్‌"</string>
     <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string>
     <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"వెంటనే తీర ప్రాంతాలు మరియు నదీ పరీవాహక ప్రాంతాలను ఖాళీ చేసి మెట్ట ప్రాంతాలకు తరలి వెళ్లండి."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 986f64d..207e522 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"อนุญาตให้แอปประกาศไปยังอุปกรณ์บลูทูธใกล้เคียง"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ระบุตำแหน่งสัมพันธ์ระหว่างอุปกรณ์ที่ใช้แถบความถี่กว้างยิ่งยวดซึ่งอยู่ใกล้เคียง"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"อนุญาตให้แอประบุตำแหน่งสัมพันธ์ระหว่างอุปกรณ์ที่ใช้แถบความถี่กว้างยิ่งยวดซึ่งอยู่ใกล้เคียง"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"โต้ตอบกับอุปกรณ์ Wi-Fi ที่อยู่ใกล้เคียง"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"อนุญาตให้แอปแสดงข้อมูล เชื่อมต่อ และระบุตำแหน่งซึ่งสัมพันธ์กับอุปกรณ์ Wi-Fi ที่อยู่ใกล้เคียง"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ข้อมูลบริการชำระเงิน NFC ที่ต้องการ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"อนุญาตให้แอปรับข้อมูลบริการชำระเงิน NFC ที่ต้องการ เช่น รหัสแอป (AID) ที่ลงทะเบียนและปลายทางของเส้นทาง"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"ควบคุม Near Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"อนุญาตให้แอปอ่านและเขียนการกำหนดค่าโหมดห้ามรบกวน"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"เริ่มการใช้สิทธิ์การดู"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"อนุญาตให้เจ้าของเริ่มการใช้สิทธิ์ของแอป ไม่จำเป็นสำหรับแอปทั่วไป"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"เริ่มดูฟีเจอร์ของแอป"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"อนุญาตให้เจ้าของเริ่มดูข้อมูลฟีเจอร์สำหรับแอป"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"เข้าถึงข้อมูลเซ็นเซอร์ที่อัตราการสุ่มตัวอย่างสูง"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"อนุญาตให้แอปสุ่มตัวอย่างข้อมูลเซ็นเซอร์ที่อัตราสูงกว่า 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"ตั้งค่ากฎรหัสผ่าน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 8ace595..e99f0c4 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Nagbibigay-daan sa app na mag-advertise sa mga kalapit na Bluetooth device"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"tukuyin ang relatibong posisyon sa pagitan ng mga kalapit na Ultra-Wideband device"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Payagan ang app na tukuyin ang relatibong posisyon sa pagitan ng mga kalapit na Ultra-Wideband device"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"makipag-ugnayan sa mga kalapit na Wi‑Fi device"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Nagbibigay-daan sa app na i-advertise, kumonekta sa, at tukuyin ang nauugnay na posisyon ng mga kalapit na Wi‑Fi device"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Impormasyon sa Gustong NFC na Serbisyo sa Pagbabayad"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Pinapayagan ang app na makakuha ng impormasyon sa gustong nfc na serbisyo sa pagbabayad tulad ng mga nakarehistrong application ID at destinasyon ng ruta."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrolin ang Near Field Communication"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Nagbibigay-daan sa app na basahin at isulat ang configuration ng Huwag Istorbohin."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"simulan ang paggamit sa pahintulot sa pagtingin"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Binibigyang-daan ang may hawak na simulan ang paggamit ng pahintulot para sa isang app. Hindi dapat kailanganin kailanman para sa mga normal na app."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"simulang tingnan ang mga feature ng app"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Nagbibigay-daan sa may hawak na simulang tingnan ang impormasyon ng mga feature para sa isang app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mag-access ng data ng sensor sa mataas na rate ng pag-sample"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Pinapahintulutan ang app na mag-sample ng data ng sensor sa rate na higit sa 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Magtakda ng mga panuntunan sa password"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 52614a8..b3f15a2 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Uygulamaya, yakındaki Bluetooth cihazlara reklam yayınlama izni verir"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"yakındaki Ultra Geniş Bant cihazların birbirine göre konumunu bul"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Uygulamanın, yakındaki Ultra Geniş Bant cihazların birbirine göre konumunu belirlemesine izin verin"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"yakındaki kablosuz cihazlarla etkileşim kurma"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Uygulamanın reklam sunmasına, bağlanmasına ve yakındaki kablosuz cihazların göreli konumunu belirlemesine izin verir"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Tercih Edilen NFC Ödeme Hizmeti Bilgileri"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Uygulamaya, kayıtlı yardımlar ve rota hedefi gibi tercih edilen NFC ödeme hizmeti bilgilerini alma izni verir."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Yakın Alan İletişimini denetle"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Uygulamaya, Rahatsız Etmeyin yapılandırmasını okuma ve yazma izni verir."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"izin kullanımı görüntülemeye başlama"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"İzin sahibinin bir uygulama için izin kullanımı başlatmasına olanak tanır. Normal uygulamalar için hiçbir zaman kullanılmamalıdır."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"uygulama özelliklerini görüntülemeye başlama"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"İzin sahibinin, bir uygulamanın özellik bilgilerini görüntülemeye başlamasına izin verir."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"sensör verilerine daha yüksek örnekleme hızında eriş"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Uygulamanın, sensör verilerini 200 Hz\'den daha yüksek bir hızda örneklemesine olanak tanır"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Şifre kuralları ayarla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 934c51d..7fb0b7b 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -549,6 +549,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Додаток зможе показувати рекламу на пристроях із Bluetooth поблизу"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"визначення відстані між пристроями поблизу з надширокосмуговим зв’язком"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"З цим дозволом додаток може визначати відстань між розташованими поблизу пристроями з надширокосмуговим зв’язком"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"взаємодіяти з пристроями Wi‑Fi поблизу"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Додаток може виявляти пристрої Wi‑Fi поблизу, підключатися до них і визначати їх відносне розташування"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Використання інформації з платіжного NFC-сервісу"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозволяє додатку отримувати доступ до інформації потрібного платіжного NFC-сервісу (наприклад, пов\'язаних ідентифікаторів чи даних про маршрутизацію трансакцій)."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"контрол. Near Field Communication"</string>
@@ -732,6 +734,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Додаток зможе переглядати та змінювати конфігурацію режиму \"Не турбувати\"."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"перегляньте дані про використання дозволів"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Власник зможе використовувати дозволи для цього додатка. Цей дозвіл не потрібен для звичайних додатків."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"почати перегляд функцій додатка"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Дозволяє додатку почати перегляд інформації про функції іншого додатка."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"доступ до даних датчиків із високою частотою дикретизації"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Додаток зможе дискретизувати дані даних датчиків із частотою понад 200 Гц"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Устан. правила пароля"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 60f086b..9621b95 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"اس سے ایپ کو قریبی بلوٹوتھ آلات پر تشہیر کرنے کی اجازت ملتی ہے"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"قریبی الٹرا وائڈ بینڈ آلات کے مابین متعلقہ پوزیشن کا تعین کریں"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ایپ کو قریبی الٹرا وائڈ بینڈ آلات کے مابین متعلقہ پوزیشن کا تعین کرنے کی اجازت دیں"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏قریبی Wi-Fi آلات کے ساتھ تعامل کریں"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏ایپ کو اشتہار دینے، منسلک کرنے اور قریبی Wi-Fi آلات کی متعلقہ پوزیشن کا تعین کرنے کی اجازت دیتا ہے"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏ترجیح شدہ NFC ادائیگی کی سروس کی معلومات"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏ایپ کو رجسٹرشدہ ایڈز اور روٹ ڈسٹنیشن جیسی ترجیح شدہ nfc ادائیگی سروس کی معلومات حاصل کرنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"‏Near Field کمیونیکیشن کنٹرول کریں"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ایپ کو ڈسٹرب نہ کریں کنفیگریشن لکھنے اور پڑھنے کے قابل کرتا ہے۔"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"اجازت کی استعمال کا ملاحظہ شروع کریں"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"حامل کو ایپ کی اجازت کے استعمال کو شروع کرنے کی اجازت دیتا ہے۔ عام ایپس کے لیے کبھی بھی درکار نہیں ہونا چاہیے۔"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ایپ کی خصوصیات کا ملاحظہ شروع کریں"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ہولڈر کو ایپ کے لیے خصوصیات کی معلومات دیکھنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"نمونہ کاری کی اعلی شرح پر سینسر کے ڈیٹا تک رسائی حاصل کریں"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"‏ایپ کو Hz‏200 سے زیادہ شرح پر سینسر ڈیٹا کا نمونہ لینے کی اجازت دیتی ہے"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"پاس ورڈ کے اصول سیٹ کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index d4e2fcb..3d98dab 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Ilovaga yaqin-atrofdagi Bluetooth qurilmalariga reklama yuborish imkonini beradi"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"yaqin atrofdagi ultra keng polosali qurilmalarining nisbiy joylashishini aniqlash"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ilovaga yaqin atrofdagi ultra keng polosali qurilmalarining nisbiy joylashishini aniqlashga ruxsat beradi"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Yaqin-atrofdagi Wi-Fi qurilmalari bilan ishlash"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Ilovaga yaqin-atrofdagi Wi-Fi qurilmalarga reklama yuborish, ulanish va taxminiy joylashuvini aniqlash imkonini beradi."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Asosiy NFC toʻlov xizmati haqidagi axborot"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Bu ilovaga asosiy NFC toʻlov xizmati haqidagi axborotni olish imkonini beradi (masalan, qayd qilingan AID identifikatorlari va marshrutning yakuniy manzili)."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"NFC modulini boshqarish"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"“Bezovta qilinmasin” rejimi sozlamalarini ko‘rish va o‘zgartirish."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"foydalaniladigan ruxsatlar axborotini ochish"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ilova foydalanadigan ruxsatlar axborotini ishga tushirishga ruxsat beradi. Oddiy ilovalar uchun talab qilinmaydi."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"ilova funksiyalari axborotini koʻrish"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Qurilma egasiga ilova funksiyalari axborotini koʻrishga ruxsat beradi."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"yuqori diskretlash chastotali sensor axborotiga ruxsat"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ilova sensor axborotini 200 Hz dan yuqori tezlikda hisoblashi mumkin"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Parol qoidalarini o‘rnatish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 872a175..052a658 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Cho phép ứng dụng quảng cáo trên các thiết bị Bluetooth ở gần"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"xác định khoảng cách tương đối giữa các thiết bị ở gần dùng Băng tần siêu rộng"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Cho phép ứng dụng xác định khoảng cách tương đối giữa các thiết bị ở gần dùng Băng tần siêu rộng"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"tương tác với các thiết bị Wi‑Fi lân cận"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Cho phép ứng dụng thông báo, kết nối và xác định vị trí tương đối của các thiết bị Wi‑Fi lân cận"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Thông tin về dịch vụ thanh toán qua công nghệ giao tiếp tầm gần (NFC) được ưu tiên"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Cho phép ứng dụng nhận thông tin về dịch vụ thanh toán qua công nghệ giao tiếp tầm gần mà bạn ưu tiên, chẳng hạn như các hình thức hỗ trợ đã đăng ký và điểm đến trong hành trình."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kiểm soát Liên lạc trường gần"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Cho phép ứng dụng đọc và ghi cấu hình Không làm phiền."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"cấp quyền xem"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Cho phép chủ sở hữu cấp quyền cho một ứng dụng. Các ứng dụng thông thường sẽ không bao giờ cần quyền này."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"bắt đầu xem các tính năng của ứng dụng"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Cho phép chủ sở hữu bắt đầu xem thông tin về tính năng của một ứng dụng."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"truy cập vào dữ liệu cảm biến ở tốc độ lấy mẫu cao"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Cho phép ứng dụng lấy mẫu dữ liệu cảm biến ở tốc độ lớn hơn 200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Đặt quy tắc mật khẩu"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 8bbcf69..2dd9d6b 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"允许该应用向附近的蓝牙设备广播"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"确定附近超宽带设备之间的相对位置"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允许应用确定附近超宽带设备之间的相对位置"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"与附近的 WLAN 设备互动"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允许该应用向附近的 WLAN 设备进行广播、连接到这些设备以及确定这些设备的相对位置"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首选 NFC 付款服务信息"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允许应用获取首选 NFC 付款服务信息,例如注册的应用标识符和路线目的地。"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"控制近距离通信"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允许此应用读取和写入“勿扰”模式配置。"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"授权使用“查看权限”"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允许该应用开始查看应用的权限使用情况(普通应用绝不需要此权限)。"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"开始查看应用功能"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"允许具有该权限的应用开始查看某个应用的功能信息。"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高采样率访问传感器数据"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允许应用以高于 200 Hz 的频率对传感器数据进行采样"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"设置密码规则"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 7c276a5..7a220a4 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"允許應用程式向附近的藍牙裝置宣傳"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"判斷附近超寬頻裝置之間的相對位置"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置之間的相對位置"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"與附近的 Wi‑Fi 裝置互動"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允許應用程式顯示附近 Wi-Fi 裝置的資料、與其連接並判斷其相對位置"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"由用戶允許授權的 NFC 付款服務資訊"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得由用戶允許授權的 NFC 付款服務資訊 (如已註冊的付款輔助功能和最終付款對象)。"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"控制近距離無線通訊"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取和寫入「請勿騷擾」設定。"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"開始查看權限使用情況"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始查看應用程式的權限使用情況 (一般應用程式並不需要)。"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"開始查看應用程式功能"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"允許擁有者開始查看應用程式的功能資料。"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高取樣率存取感應器資料"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允許應用程式以大於 200 Hz 的頻率對感應器資料進行取樣"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index afc1392..d7db7ed 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -543,6 +543,10 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"允許應用程式向附近的藍牙裝置廣播"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"判斷附近超寬頻裝置間的相對位置"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置間的相對位置"</string>
+    <!-- no translation found for permlab_nearby_wifi_devices (392774237063608500) -->
+    <skip />
+    <!-- no translation found for permdesc_nearby_wifi_devices (3054307728646332906) -->
+    <skip />
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首選 NFC 付費服務資訊"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得首選 NFC 付費服務資訊,例如已註冊的輔助工具和路線目的地。"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"控制近距離無線通訊"</string>
@@ -726,6 +730,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取及寫入「零打擾」設定。"</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"啟動檢視權限用途"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始使用其他應用程式 (一般應用程式並不需要)。"</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"開始查看應用程式功能"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"允許具有這項權限的應用程式開始查看其他應用程式的功能資訊。"</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高取樣率存取感應器資料"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允許應用程式以高於 200 Hz 的頻率對感應器資料進行取樣"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 2e3c20b..518a45f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -543,6 +543,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Ivumela i-app ikhangise kumadivayisi e-Bluetooth aseduze"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"nquma indawo ehambelanayo phakathi kwamadivayisi e-Ultra-Wideband aseduze"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Vumela i-app inqume indawo ehambelanayo phakathi kwamadivayisi e-Ultra-Wideband aseduze"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"xhumana namadivayisi we-Wi‑Fi aseduze"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Ivumela i-app ikhangise, ixhume, futhi inqume isimo esihambisanayo samadivayisi we-Wi-Fi aseduze"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Ulwazi Lwesevisi Yenkokhelo Ye-NFC Okhethwayo"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ivuemela uhlelo lokusebenza ukuthola ulwazi lesevisi yenkokhelo ye-nfc njengezinsiza zokubhalisa nezindawo zomzila."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"lawula Uxhumano Lwenkambu Eseduze"</string>
@@ -726,6 +728,8 @@
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ivumela izinhlelo zokusebenza ukufunda nokubhala ukulungiswa kokuthi Ungaphazamisi."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"qala ukusetshenziswa kokubuka imvume"</string>
     <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ivumela umphathi ukuthi aqale ukusetshenziswa kwemvume kohlelo lokusebenza. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string>
+    <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"qala ukubuka izakhi ze-app"</string>
+    <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Vumela isibambi ukuthi siqale ukubuka ulwazi lwezakhi lwe-app."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"finyelela idatha yenzwa ngenani eliphezulu lokwenza isampuli"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ivumela uhlelo lokusebenza lusampule idatha yenzwa ngenani elikhulu kuno-200 Hz"</string>
     <string name="policylab_limitPassword" msgid="4851829918814422199">"Misa imithetho yephasiwedi"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2eb32b0..74ce8f2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1051,6 +1051,39 @@
     -->
     <integer name="config_shortPressOnSleepBehavior">0</integer>
 
+    <!-- Control the behavior when the user long presses the stem primary button.
+         Stem primary button is only used on watch form factor. If a device is not
+         a watch, setting this config is no-op.
+            0 - Nothing
+            1 - Launch voice assistant
+    -->
+    <integer name="config_longPressOnStemPrimaryBehavior">0</integer>
+
+    <!-- Control the behavior when the user double presses the stem primary button.
+         Stem primary button is only used on watch form factor. If a device is not
+         a watch, setting this config is no-op.
+            0 - Nothing
+            1 - Switch to the recent app
+    -->
+    <integer name="config_doublePressOnStemPrimaryBehavior">0</integer>
+
+    <!-- Control the behavior when the user triple presses the stem primary button.
+         Stem primary button is only used on watch form factor. If a device is not
+         a watch, setting this config is no-op.
+            0 - Nothing
+            1 - Toggle accessibility
+    -->
+    <integer name="config_triplePressOnStemPrimaryBehavior">0</integer>
+
+    <!-- Control the behavior when the user short presses the stem primary button.
+        Stem primary button is only used on watch form factor. If a device is not
+        a watch, setting this config is no-op.
+           0 - Nothing
+           1 - Go to launch all apps
+    -->
+    <integer name="config_shortPressOnStemPrimaryBehavior">0</integer>
+
+
     <!-- Time to wait while a button is pressed before triggering a very long press. -->
     <integer name="config_veryLongPressTimeout">3500</integer>
 
@@ -2319,10 +2352,6 @@
     <!-- Type of the ambient tap sensor per device posture (defined by WM Jetpack posture).
          Unspecified values use config_dozeTapSensor -->
     <string-array name="config_dozeTapSensorPostureMapping" translatable="false">
-        <item></item> <!-- UNKNOWN -->
-        <item></item> <!-- CLOSED -->
-        <item></item> <!-- HALF_OPENED -->
-        <item></item> <!-- OPENED -->
     </string-array>
 
     <!-- Type of the long press sensor. Empty if long press is not supported. -->
@@ -5104,6 +5133,97 @@
     <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed">true</bool>
     <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default">true</bool>
 
+    <!-- Which Short Audio Descriptors a TV should query via CEC -->
+    <bool name="config_cecQuerySadLpcm_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadLpcmEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadLpcmEnabled_default">true</bool>
+    <bool name="config_cecQuerySadLpcmDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadLpcmDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadDd_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadDdEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDdEnabled_default">true</bool>
+    <bool name="config_cecQuerySadDdDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDdDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadMpeg1_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadMpeg1Enabled_allowed">true</bool>
+    <bool name="config_cecQuerySadMpeg1Enabled_default">true</bool>
+    <bool name="config_cecQuerySadMpeg1Disabled_allowed">true</bool>
+    <bool name="config_cecQuerySadMpeg1Disabled_default">false</bool>
+
+    <bool name="config_cecQuerySadMp3_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadMp3Enabled_allowed">true</bool>
+    <bool name="config_cecQuerySadMp3Enabled_default">true</bool>
+    <bool name="config_cecQuerySadMp3Disabled_allowed">true</bool>
+    <bool name="config_cecQuerySadMp3Disabled_default">false</bool>
+
+    <bool name="config_cecQuerySadMpeg2_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadMpeg2Enabled_allowed">true</bool>
+    <bool name="config_cecQuerySadMpeg2Enabled_default">true</bool>
+    <bool name="config_cecQuerySadMpeg2Disabled_allowed">true</bool>
+    <bool name="config_cecQuerySadMpeg2Disabled_default">false</bool>
+
+    <bool name="config_cecQuerySadAac_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadAacEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadAacEnabled_default">true</bool>
+    <bool name="config_cecQuerySadAacDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadAacDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadDts_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadDtsEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDtsEnabled_default">true</bool>
+    <bool name="config_cecQuerySadDtsDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDtsDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadAtrac_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadAtracEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadAtracEnabled_default">true</bool>
+    <bool name="config_cecQuerySadAtracDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadAtracDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadOnebitaudio_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadOnebitaudioEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadOnebitaudioEnabled_default">true</bool>
+    <bool name="config_cecQuerySadOnebitaudioDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadOnebitaudioDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadDdp_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadDdpEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDdpEnabled_default">true</bool>
+    <bool name="config_cecQuerySadDdpDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDdpDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadDtshd_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadDtshdEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDtshdEnabled_default">true</bool>
+    <bool name="config_cecQuerySadDtshdDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDtshdDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadTruehd_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadTruehdEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadTruehdEnabled_default">true</bool>
+    <bool name="config_cecQuerySadTruehdDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadTruehdDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadDst_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadDstEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDstEnabled_default">true</bool>
+    <bool name="config_cecQuerySadDstDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadDstDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadWmapro_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadWmaproEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadWmaproEnabled_default">true</bool>
+    <bool name="config_cecQuerySadWmaproDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadWmaproDisabled_default">false</bool>
+
+    <bool name="config_cecQuerySadMax_userConfigurable">true</bool>
+    <bool name="config_cecQuerySadMaxEnabled_allowed">true</bool>
+    <bool name="config_cecQuerySadMaxEnabled_default">true</bool>
+    <bool name="config_cecQuerySadMaxDisabled_allowed">true</bool>
+    <bool name="config_cecQuerySadMaxDisabled_default">false</bool>
+
     <!-- Whether app hibernation deletes OAT artifact files as part of global hibernation. -->
     <bool name="config_hibernationDeletesOatArtifactsEnabled">true</bool>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 45201a9..c40ec4f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -39,15 +39,21 @@
     <!-- Elevation of toast view -->
     <dimen name="toast_elevation">2dp</dimen>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
-    <!-- Height of the status bar in portrait. The height should be
-         Max((status bar content height + waterfall top size), top cutout size) -->
-    <dimen name="status_bar_height_portrait">24dp</dimen>
-    <!-- Height of the status bar in landscape. The height should be
-         Max((status bar content height + waterfall top size), top cutout size) -->
+    <!-- Height of the status bar.
+         Do not read this dimen directly. Use {@link SystemBarUtils#getStatusBarHeight} instead.
+         -->
+    <dimen name="status_bar_height">24dp</dimen>
+    <!-- Height of the status bar in portrait.
+         Do not read this dimen directly. Use {@link SystemBarUtils#getStatusBarHeight} instead.
+         -->
+    <dimen name="status_bar_height_portrait">@dimen/status_bar_height</dimen>
+    <!-- Height of the status bar in landscape.
+         Do not read this dimen directly. Use {@link SystemBarUtils#getStatusBarHeight} instead.
+         -->
     <dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
-    <!-- Height of area above QQS where battery/time go -->
+    <!-- Height of area above QQS where battery/time go.
+         Do not read this dimen directly. Use {@link SystemBarUtils#getQuickQsOffsetHeight} instead.
+         -->
     <dimen name="quick_qs_offset_height">48dp</dimen>
     <!-- Height of the bottom navigation / system bar. -->
     <dimen name="navigation_bar_height">48dp</dimen>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 84f82fd..747a918 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -263,6 +263,18 @@
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL}. -->
   <item type="id" name="accessibilityActionDragCancel" />
 
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SWIPE_LEFT}. -->
+  <item type="id" name="accessibilityActionSwipeLeft" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SWIPE_RIGHT}. -->
+  <item type="id" name="accessibilityActionSwipeRight" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SWIPE_UP}. -->
+  <item type="id" name="accessibilityActionSwipeUp" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SWIPE_DOWN}. -->
+  <item type="id" name="accessibilityActionSwipeDown" />
+
   <!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. -->
   <item type="id" name="remote_views_next_child" />
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a6e42bd..ed49fe4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3306,6 +3306,10 @@
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
+    <public name="accessibilityActionSwipeLeft" />
+    <public name="accessibilityActionSwipeRight" />
+    <public name="accessibilityActionSwipeUp" />
+    <public name="accessibilityActionSwipeDown" />
   </staging-public-group>
 
   <staging-public-group type="style" first-id="0x0dfd0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6f4c7ab..391c7dc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1926,6 +1926,11 @@
     <string name="permdesc_startViewPermissionUsage">Allows the holder to start the permission usage for an app. Should never be needed for normal apps.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permlab_startViewAppFeatures">start view app features</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permdesc_startViewAppFeatures">Allows the holder to start viewing the features info for an app.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
     <string name="permlab_highSamplingRateSensors">access sensor data at a high sampling rate</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
     <string name="permdesc_highSamplingRateSensors">Allows the app to sample sensor data at a rate greater than 200 Hz</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 253cd47..846ec60 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -459,6 +459,10 @@
   <java-symbol type="integer" name="config_toastDefaultGravity" />
   <java-symbol type="integer" name="config_triplePressOnPowerBehavior" />
   <java-symbol type="integer" name="config_shortPressOnSleepBehavior" />
+  <java-symbol type="integer" name="config_longPressOnStemPrimaryBehavior" />
+  <java-symbol type="integer" name="config_shortPressOnStemPrimaryBehavior" />
+  <java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" />
+  <java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" />
   <java-symbol type="integer" name="config_windowOutsetBottom" />
   <java-symbol type="integer" name="db_connection_pool_size" />
   <java-symbol type="integer" name="db_journal_size_limit" />
@@ -4432,6 +4436,97 @@
   <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed" />
   <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default" />
 
+  <!-- Which Short Audio Descriptors a TV should query via CEC -->
+  <java-symbol type="bool" name="config_cecQuerySadLpcm_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadLpcmEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadLpcmEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadLpcmDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadLpcmDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadDd_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadDdEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDdEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadDdDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDdDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadMpeg1_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadMpeg1Enabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadMpeg1Enabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadMpeg1Disabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadMpeg1Disabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadMp3_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadMp3Enabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadMp3Enabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadMp3Disabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadMp3Disabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadMpeg2_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadMpeg2Enabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadMpeg2Enabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadMpeg2Disabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadMpeg2Disabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadAac_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadAacEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadAacEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadAacDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadAacDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadDts_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadDtsEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDtsEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadDtsDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDtsDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadAtrac_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadAtracEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadAtracEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadAtracDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadAtracDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadOnebitaudio_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadOnebitaudioEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadOnebitaudioEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadOnebitaudioDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadOnebitaudioDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadDdp_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadDdpEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDdpEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadDdpDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDdpDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadDtshd_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadDtshdEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDtshdEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadDtshdDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDtshdDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadTruehd_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadTruehdEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadTruehdEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadTruehdDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadTruehdDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadDst_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadDstEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDstEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadDstDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadDstDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadWmapro_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadWmaproEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadWmaproEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadWmaproDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadWmaproDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecQuerySadMax_userConfigurable" />
+  <java-symbol type="bool" name="config_cecQuerySadMaxEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadMaxEnabled_default" />
+  <java-symbol type="bool" name="config_cecQuerySadMaxDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecQuerySadMaxDisabled_default" />
+
   <!-- Ids for RemoteViews -->
   <java-symbol type="id" name="remote_views_next_child" />
   <java-symbol type="id" name="remote_views_stable_id" />
diff --git a/core/tests/companiontests/Android.bp b/core/tests/companiontests/Android.bp
new file mode 100644
index 0000000..d31b8f4
--- /dev/null
+++ b/core/tests/companiontests/Android.bp
@@ -0,0 +1,21 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "CompanionTests",
+    // Include all test java files.
+    srcs: ["src/**/*.java"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: ["junit"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/core/tests/companiontests/AndroidManifest.xml b/core/tests/companiontests/AndroidManifest.xml
new file mode 100644
index 0000000..f436d97
--- /dev/null
+++ b/core/tests/companiontests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.companion.tests"
+          android:sharedUserId="android.uid.system" >
+
+    <application >
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="android.companion.CompanionTestRunner"
+            android:targetPackage="com.android.companion.tests"
+            android:label="Companion Tests" />
+
+</manifest>
diff --git a/core/tests/companiontests/src/android/companion/BluetoothDeviceFilterUtilsTest.java b/core/tests/companiontests/src/android/companion/BluetoothDeviceFilterUtilsTest.java
new file mode 100644
index 0000000..1ddbbd8
--- /dev/null
+++ b/core/tests/companiontests/src/android/companion/BluetoothDeviceFilterUtilsTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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.companion;
+
+import android.os.ParcelUuid;
+import android.test.InstrumentationTestCase;
+
+public class BluetoothDeviceFilterUtilsTest extends InstrumentationTestCase {
+    private static final String TAG = "BluetoothDeviceFilterUtilsTest";
+
+    private final ParcelUuid mServiceUuid =
+            ParcelUuid.fromString("F0FFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+    private final ParcelUuid mNonMatchingDeviceUuid =
+            ParcelUuid.fromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+    private final ParcelUuid mMatchingDeviceUuid =
+            ParcelUuid.fromString("F0FFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+    private final ParcelUuid mMaskUuid =
+            ParcelUuid.fromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+    private final ParcelUuid mMatchingMaskUuid =
+            ParcelUuid.fromString("F0FFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void testUuidsMaskedEquals() {
+        assertFalse(BluetoothDeviceFilterUtils.uuidsMaskedEquals(
+                mNonMatchingDeviceUuid.getUuid(),
+                mServiceUuid.getUuid(),
+                mMaskUuid.getUuid()));
+
+        assertTrue(BluetoothDeviceFilterUtils.uuidsMaskedEquals(
+                mMatchingDeviceUuid.getUuid(),
+                mServiceUuid.getUuid(),
+                mMaskUuid.getUuid()));
+
+        assertTrue(BluetoothDeviceFilterUtils.uuidsMaskedEquals(
+                mNonMatchingDeviceUuid.getUuid(),
+                mServiceUuid.getUuid(),
+                mMatchingMaskUuid.getUuid()));
+    }
+}
diff --git a/core/tests/companiontests/src/android/companion/CompanionTestRunner.java b/core/tests/companiontests/src/android/companion/CompanionTestRunner.java
new file mode 100644
index 0000000..caa2c68
--- /dev/null
+++ b/core/tests/companiontests/src/android/companion/CompanionTestRunner.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 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.companion;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+import junit.framework.TestSuite;
+
+
+/**
+ * Instrumentation test runner for Companion tests.
+ */
+public class CompanionTestRunner extends InstrumentationTestRunner {
+    private static final String TAG = "CompanionTestRunner";
+
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(BluetoothDeviceFilterUtilsTest.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return CompanionTestRunner.class.getClassLoader();
+    }
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        super.onCreate(arguments);
+    }
+}
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index b400b9b..ba7df1e 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -26,6 +26,7 @@
 
 import android.net.sntp.Duration64;
 import android.net.sntp.Timestamp64;
+import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -49,6 +50,7 @@
 import java.util.Random;
 import java.util.function.Supplier;
 
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class SntpClientTest {
     private static final String TAG = "SntpClientTest";
diff --git a/core/tests/coretests/src/android/net/sntp/Duration64Test.java b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
index 933800f..b228596 100644
--- a/core/tests/coretests/src/android/net/sntp/Duration64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
@@ -21,13 +21,20 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.time.Duration;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 
+@Presubmit
+@RunWith(AndroidJUnit4.class)
 public class Duration64Test {
 
     @Test
diff --git a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
index c923812..200c80e 100644
--- a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
@@ -21,13 +21,20 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.time.Instant;
 import java.util.HashSet;
 import java.util.Random;
 import java.util.Set;
 
+@Presubmit
+@RunWith(AndroidJUnit4.class)
 public class Timestamp64Test {
 
     @Test
@@ -191,6 +198,7 @@
         }
     }
 
+    @SuppressWarnings("JavaInstantGetSecondsGetNano")
     private static void assertInstantCreationOnlyFractionExact(
             int fractionBits, long expectedNanos) {
         Timestamp64 timestamp64 = Timestamp64.fromComponents(0, fractionBits);
@@ -201,6 +209,7 @@
         assertEquals(expectedNanos, instant.getNano());
     }
 
+    @SuppressWarnings("JavaInstantGetSecondsGetNano")
     private static void assertNanosWithTruncationAllowed(long expectedNanos, Instant instant) {
         // Allow for < 1ns difference due to truncation.
         long actualNanos = instant.getNano();
@@ -208,6 +217,7 @@
                 actualNanos == expectedNanos || actualNanos == expectedNanos - 1);
     }
 
+    @SuppressWarnings("JavaInstantGetSecondsGetNano")
     @Test
     public void testMillisRandomizationConstant() {
         // Mathematically, we can say that to represent 1000 different values, we need 10 binary
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
index 109b7ab..76c9f88 100644
--- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -23,6 +23,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -54,6 +55,7 @@
     private final Context mMockContext = mock(Context.class);
     @Mock
     private Context mRealContext;
+    @Mock
     private PackageManager mPm;
 
     private static final String PKG = "com.example.o";
@@ -78,6 +80,7 @@
                 InstrumentationRegistry.getContext().getResources());
         when(mMockContext.getPackageManager()).thenReturn(mPm);
         when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+        when(mPm.getApplicationLabel(any())).thenReturn("");
 
         mRealContext = InstrumentationRegistry.getContext();
     }
@@ -217,6 +220,26 @@
         assertEquals(pkg, resultContext.getPackageName());
     }
 
+    @Test
+    public void testGetUidFromKey() {
+        StatusBarNotification sbn = getNotification("pkg", null, "channel");
+
+        assertEquals(UID, StatusBarNotification.getUidFromKey(sbn.getKey()));
+
+        sbn.setOverrideGroupKey("addsToKey");
+        assertEquals(UID, StatusBarNotification.getUidFromKey(sbn.getKey()));
+    }
+
+    @Test
+    public void testGetPkgFromKey() {
+        StatusBarNotification sbn = getNotification("pkg", null, "channel");
+
+        assertEquals("pkg", StatusBarNotification.getPkgFromKey(sbn.getKey()));
+
+        sbn.setOverrideGroupKey("addsToKey");
+        assertEquals("pkg", StatusBarNotification.getPkgFromKey(sbn.getKey()));
+    }
+
     private StatusBarNotification getNotification(String pkg, String group, String channelId) {
         return getNotification(pkg, getNotificationBuilder(group, channelId));
     }
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 4779786..9696fdf 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -28,7 +28,7 @@
 import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
-import static com.android.internal.widget.FloatingToolbar.MenuItemRepr;
+import static com.android.internal.widget.FloatingToolbarPopup.MenuItemRepr;
 
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.is;
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
index 3c093d8..2c31b08 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
@@ -30,6 +30,7 @@
 import com.android.internal.content.om.OverlayConfig.PackageProvider;
 import com.android.internal.content.om.OverlayScanner;
 import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
+import com.android.internal.util.function.TriConsumer;
 
 import org.junit.Assert;
 import org.junit.rules.TestRule;
@@ -42,7 +43,6 @@
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
-import java.util.function.BiConsumer;
 import java.util.function.Supplier;
 
 /**
@@ -74,7 +74,7 @@
         TestOverlayInfo(String packageName, String targetPackageName,
                 int targetSdkVersion, boolean isStatic, int priority, File path,
                 String requiredSystemPropertyName, String requiredSystemPropertyValue) {
-            super(packageName, targetPackageName, targetSdkVersion, isStatic, priority, path);
+            super(packageName, targetPackageName, targetSdkVersion, isStatic, priority, path, null);
             this.requiredSystemPropertyName = requiredSystemPropertyName;
             this.requiredSystemPropertyValue = requiredSystemPropertyValue;
         }
@@ -174,8 +174,8 @@
                 mIteration = Iteration.SYSTEM_SERVER;
                 doAnswer((InvocationOnMock invocation) -> {
                     final Object[] args = invocation.getArguments();
-                    final BiConsumer<ParsingPackageRead, Boolean> f =
-                            (BiConsumer<ParsingPackageRead, Boolean>) args[0];
+                    final TriConsumer<ParsingPackageRead, Boolean, File> f =
+                            (TriConsumer<ParsingPackageRead, Boolean, File>) args[0];
                     for (Map.Entry<File, TestOverlayInfo> overlay :
                             mTestOverlayInfos.entrySet()) {
                         final ParsingPackageRead a = Mockito.mock(ParsingPackageRead.class);
@@ -191,7 +191,8 @@
                         when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
                         when(a.getOverlayPriority()).thenReturn(info.priority);
                         when(a.getBaseApkPath()).thenReturn(info.path.getPath());
-                        f.accept(a, !info.path.getPath().contains("data/overlay"));
+                        f.accept(a, !info.path.getPath().contains("data/overlay"),
+                                /*preInstalledApexPath=*/null);
                     }
                     return null;
                 }).when(mPkgProvider).forEachPackage(any());
diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
index e95f6c2..130f552 100644
--- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
@@ -38,26 +38,28 @@
 
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0, 10.0);
+            .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0, 10.0)
+            .setNumDisplays(1);
 
     @Test
     public void testMeasuredEnergyBasedModel() {
         mStatsRule.initMeasuredEnergyStatsLocked();
         BatteryStatsImpl stats = mStatsRule.getBatteryStats();
 
-        stats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON, 0);
+        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300_000_000},
+                new int[]{Display.STATE_ON}, 0);
 
         stats.noteScreenStateLocked(0, Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
                 30 * MINUTE_IN_MS);
 
-        stats.updateDisplayMeasuredEnergyStatsLocked(200_000_000, Display.STATE_DOZE,
-                30 * MINUTE_IN_MS);
+        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200_000_000},
+                new int[]{Display.STATE_DOZE}, 30 * MINUTE_IN_MS);
 
         stats.noteScreenStateLocked(0, Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
                 120 * MINUTE_IN_MS);
 
-        stats.updateDisplayMeasuredEnergyStatsLocked(100_000_000, Display.STATE_OFF,
-                120 * MINUTE_IN_MS);
+        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000},
+                new int[]{Display.STATE_OFF}, 120 * MINUTE_IN_MS);
 
         AmbientDisplayPowerCalculator calculator =
                 new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile());
@@ -75,6 +77,67 @@
     }
 
     @Test
+    public void testMeasuredEnergyBasedModel_multiDisplay() {
+        mStatsRule.initMeasuredEnergyStatsLocked()
+                .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 1, 20.0)
+                .setNumDisplays(2);
+        BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+
+
+        final int[] screenStates = new int[] {Display.STATE_OFF, Display.STATE_OFF};
+
+        stats.noteScreenStateLocked(0, screenStates[0], 0, 0, 0);
+        stats.noteScreenStateLocked(1, screenStates[1], 0, 0, 0);
+        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300, 400}, screenStates, 0);
+
+        // Switch display0 to doze
+        screenStates[0] = Display.STATE_DOZE;
+        stats.noteScreenStateLocked(0, screenStates[0], 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
+                30 * MINUTE_IN_MS);
+        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200, 300},
+                screenStates, 30 * MINUTE_IN_MS);
+
+        // Switch display1 to doze
+        screenStates[1] = Display.STATE_DOZE;
+        stats.noteScreenStateLocked(1, Display.STATE_DOZE, 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS,
+                90 * MINUTE_IN_MS);
+        // 100,000,000 uC should be attributed to display 0 doze here.
+        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000, 700_000_000},
+                screenStates, 90 * MINUTE_IN_MS);
+
+        // Switch display0 to off
+        screenStates[0] = Display.STATE_OFF;
+        stats.noteScreenStateLocked(0, screenStates[0], 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
+                120 * MINUTE_IN_MS);
+        // 40,000,000 and 70,000,000 uC should be attributed to display 0 and 1 doze here.
+        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{40_000_000, 70_000_000},
+                screenStates, 120 * MINUTE_IN_MS);
+
+        // Switch display1 to off
+        screenStates[1] = Display.STATE_OFF;
+        stats.noteScreenStateLocked(1, screenStates[1], 150 * MINUTE_IN_MS, 150 * MINUTE_IN_MS,
+                150 * MINUTE_IN_MS);
+        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100, 90_000_000}, screenStates,
+                150 * MINUTE_IN_MS);
+        // 90,000,000 uC should be attributed to display 1 doze here.
+
+        AmbientDisplayPowerCalculator calculator =
+                new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(calculator);
+
+        BatteryConsumer consumer = mStatsRule.getDeviceBatteryConsumer();
+        assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
+                .isEqualTo(120 * MINUTE_IN_MS);
+        // 100,000,000 + 40,000,000 + 70,000,000 + 90,000,000 uC / 1000 (micro-/milli-) / 3600
+        // (seconds/hour) = 27.777778 mAh
+        assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
+                .isWithin(PRECISION).of(83.33333);
+        assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+    }
+
+    @Test
     public void testPowerProfileBasedModel() {
         BatteryStatsImpl stats = mStatsRule.getBatteryStats();
 
@@ -96,4 +159,36 @@
         assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
                 .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
     }
+
+    @Test
+    public void testPowerProfileBasedModel_multiDisplay() {
+        mStatsRule.setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 1, 20.0)
+                .setNumDisplays(2);
+
+        BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+
+        stats.noteScreenStateLocked(1, Display.STATE_OFF, 0, 0, 0);
+        stats.noteScreenStateLocked(0, Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
+                30 * MINUTE_IN_MS);
+        stats.noteScreenStateLocked(1, Display.STATE_DOZE, 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS,
+                90 * MINUTE_IN_MS);
+        stats.noteScreenStateLocked(0, Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
+                120 * MINUTE_IN_MS);
+        stats.noteScreenStateLocked(1, Display.STATE_OFF, 150 * MINUTE_IN_MS, 150 * MINUTE_IN_MS,
+                150 * MINUTE_IN_MS);
+
+        AmbientDisplayPowerCalculator calculator =
+                new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
+
+        BatteryConsumer consumer = mStatsRule.getDeviceBatteryConsumer();
+        // Duration should only be the union of
+        assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
+                .isEqualTo(120 * MINUTE_IN_MS);
+        assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
+                .isWithin(PRECISION).of(35.0);
+        assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 358885e..b655369 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -996,7 +996,7 @@
         bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
 
         clocks.realtime = 0;
-        int screen = Display.STATE_OFF;
+        int[] screen = new int[]{Display.STATE_OFF};
         boolean battery = false;
 
         final int uid1 = 10500;
@@ -1006,35 +1006,35 @@
         long globalDoze = 0;
 
         // Case A: uid1 off, uid2 off, battery off, screen off
-        bi.updateTimeBasesLocked(battery, screen, clocks.realtime*1000, 0);
+        bi.updateTimeBasesLocked(battery, screen[0], clocks.realtime * 1000, 0);
         bi.setOnBatteryInternal(battery);
-        bi.updateDisplayMeasuredEnergyStatsLocked(500_000, screen, clocks.realtime);
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{500_000}, screen, clocks.realtime);
         checkMeasuredCharge("A", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case B: uid1 off, uid2 off, battery ON,  screen off
         clocks.realtime += 17;
         battery = true;
-        bi.updateTimeBasesLocked(battery, screen, clocks.realtime*1000, 0);
+        bi.updateTimeBasesLocked(battery, screen[0], clocks.realtime * 1000, 0);
         bi.setOnBatteryInternal(battery);
         clocks.realtime += 19;
-        bi.updateDisplayMeasuredEnergyStatsLocked(510_000, screen, clocks.realtime);
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{510_000}, screen, clocks.realtime);
         checkMeasuredCharge("B", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case C: uid1 ON,  uid2 off, battery on,  screen off
         clocks.realtime += 18;
         setFgState(uid1, true, bi);
         clocks.realtime += 18;
-        bi.updateDisplayMeasuredEnergyStatsLocked(520_000, screen, clocks.realtime);
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{520_000}, screen, clocks.realtime);
         checkMeasuredCharge("C", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case D: uid1 on,  uid2 off, battery on,  screen ON
         clocks.realtime += 17;
-        screen = Display.STATE_ON;
-        bi.updateDisplayMeasuredEnergyStatsLocked(521_000, screen, clocks.realtime);
+        screen[0] = Display.STATE_ON;
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{521_000}, screen, clocks.realtime);
         blame1 += 0; // Screen had been off during the measurement period
         checkMeasuredCharge("D.1", uid1, blame1, uid2, blame2, globalDoze, bi);
         clocks.realtime += 101;
-        bi.updateDisplayMeasuredEnergyStatsLocked(530_000, screen, clocks.realtime);
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{530_000}, screen, clocks.realtime);
         blame1 += 530_000;
         checkMeasuredCharge("D.2", uid1, blame1, uid2, blame2, globalDoze, bi);
 
@@ -1042,33 +1042,33 @@
         clocks.realtime += 20;
         setFgState(uid2, true, bi);
         clocks.realtime += 40;
-        bi.updateDisplayMeasuredEnergyStatsLocked(540_000, screen, clocks.realtime);
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{540_000}, screen, clocks.realtime);
         // In the past 60ms, sum of fg is 20+40+40=100ms. uid1 is blamed for 60/100; uid2 for 40/100
         blame1 += 540_000 * (20 + 40) / (20 + 40 + 40);
-        blame2 += 540_000 * ( 0 + 40) / (20 + 40 + 40);
+        blame2 += 540_000 * (0 + 40) / (20 + 40 + 40);
         checkMeasuredCharge("E", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case F: uid1 on,  uid2 OFF, battery on,  screen on
         clocks.realtime += 40;
         setFgState(uid2, false, bi);
         clocks.realtime += 120;
-        bi.updateDisplayMeasuredEnergyStatsLocked(550_000, screen, clocks.realtime);
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{550_000}, screen, clocks.realtime);
         // In the past 160ms, sum f fg is 200ms. uid1 is blamed for 40+120 of it; uid2 for 40 of it.
         blame1 += 550_000 * (40 + 120) / (40 + 40 + 120);
-        blame2 += 550_000 * (40 + 0  ) / (40 + 40 + 120);
+        blame2 += 550_000 * (40 + 0) / (40 + 40 + 120);
         checkMeasuredCharge("F", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case G: uid1 on,  uid2 off,  battery on, screen DOZE
         clocks.realtime += 5;
-        screen = Display.STATE_DOZE;
-        bi.updateDisplayMeasuredEnergyStatsLocked(570_000, screen, clocks.realtime);
+        screen[0] = Display.STATE_DOZE;
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{570_000}, screen, clocks.realtime);
         blame1 += 570_000; // All of this pre-doze time is blamed on uid1.
         checkMeasuredCharge("G", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case H: uid1 on,  uid2 off,  battery on, screen ON
         clocks.realtime += 6;
-        screen = Display.STATE_ON;
-        bi.updateDisplayMeasuredEnergyStatsLocked(580_000, screen, clocks.realtime);
+        screen[0] = Display.STATE_ON;
+        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{580_000}, screen, clocks.realtime);
         blame1 += 0; // The screen had been doze during the energy period
         globalDoze += 580_000;
         checkMeasuredCharge("H", uid1, blame1, uid2, blame2, globalDoze, bi);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 3102e21..f75a6df 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -118,6 +118,12 @@
         return this;
     }
 
+    public BatteryUsageStatsRule setNumDisplays(int value) {
+        when(mPowerProfile.getNumDisplays()).thenReturn(value);
+        mBatteryStats.setDisplayCountLocked(value);
+        return this;
+    }
+
     /** Call only after setting the power profile information. */
     public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() {
         return initMeasuredEnergyStatsLocked(new String[0]);
diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
index 73f4eb2..eee5d57 100644
--- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
@@ -42,11 +42,13 @@
     private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 43;
     private static final long MINUTE_IN_MS = 60 * 1000;
     private static final long MINUTE_IN_US = 60 * 1000 * 1000;
+    private static final long HOUR_IN_MS = 60 * MINUTE_IN_MS;
 
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0, 36.0)
-            .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0, 48.0);
+            .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0, 48.0)
+            .setNumDisplays(1);
 
     @Test
     public void testMeasuredEnergyBasedModel() {
@@ -54,12 +56,13 @@
         BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
 
         batteryStats.noteScreenStateLocked(0, Display.STATE_ON, 0, 0, 0);
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(0, Display.STATE_ON, 0);
+        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{0},
+                new int[]{Display.STATE_ON}, 0);
         setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true,
                 0, 0);
 
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(200_000_000, Display.STATE_ON,
-                15 * MINUTE_IN_MS);
+        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200_000_000},
+                new int[]{Display.STATE_ON}, 15 * MINUTE_IN_MS);
 
         setProcState(APP_UID1, ActivityManager.PROCESS_STATE_CACHED_EMPTY, false,
                 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
@@ -67,16 +70,16 @@
         setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP, true,
                 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
 
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON,
-                60 * MINUTE_IN_MS);
+        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300_000_000},
+                new int[]{Display.STATE_ON}, 60 * MINUTE_IN_MS);
 
         batteryStats.noteScreenStateLocked(0, Display.STATE_OFF,
                 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
         setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
                 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
 
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(100_000_000, Display.STATE_DOZE,
-                120 * MINUTE_IN_MS);
+        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000},
+                new int[]{Display.STATE_DOZE}, 120 * MINUTE_IN_MS);
 
         mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);
 
@@ -129,6 +132,104 @@
                 .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
     }
 
+
+    @Test
+    public void testMeasuredEnergyBasedModel_multiDisplay() {
+        mStatsRule.initMeasuredEnergyStatsLocked()
+                .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 1, 60.0)
+                .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 1, 100.0)
+                .setNumDisplays(2);
+
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        final int[] screenStates = new int[]{Display.STATE_ON, Display.STATE_OFF};
+
+        batteryStats.noteScreenStateLocked(0, screenStates[0], 0, 0, 0);
+        batteryStats.noteScreenStateLocked(1, screenStates[1], 0, 0, 0);
+        batteryStats.noteScreenBrightnessLocked(0, 255, 0, 0);
+        setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true, 0, 0);
+        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300, 400}, screenStates, 0);
+
+        batteryStats.noteScreenBrightnessLocked(0, 100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS);
+        batteryStats.noteScreenBrightnessLocked(0, 200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+
+        setProcState(APP_UID1, ActivityManager.PROCESS_STATE_CACHED_EMPTY, false,
+                20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+        setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP, true,
+                20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+
+        screenStates[0] = Display.STATE_OFF;
+        screenStates[1] = Display.STATE_ON;
+        batteryStats.noteScreenStateLocked(0, screenStates[0],
+                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+        batteryStats.noteScreenStateLocked(1, screenStates[1], 80 * MINUTE_IN_MS,
+                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{600_000_000, 500},
+                screenStates, 80 * MINUTE_IN_MS);
+
+        batteryStats.noteScreenBrightnessLocked(1, 25, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+        batteryStats.noteScreenBrightnessLocked(1, 250, 86 * MINUTE_IN_MS, 86 * MINUTE_IN_MS);
+        batteryStats.noteScreenBrightnessLocked(1, 75, 98 * MINUTE_IN_MS, 98 * MINUTE_IN_MS);
+
+        screenStates[1] = Display.STATE_OFF;
+        batteryStats.noteScreenStateLocked(1, screenStates[1], 110 * MINUTE_IN_MS,
+                110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
+        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{700, 800_000_000},
+                screenStates, 110 * MINUTE_IN_MS);
+
+        setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
+                110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
+
+        mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);
+
+        ScreenPowerCalculator calculator =
+                new ScreenPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(calculator);
+
+        BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+        assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(110 * MINUTE_IN_MS);
+        // (600000000 + 800000000) uAs * (1 mA / 1000 uA) * (1 h / 3600 s)  = 166.66666 mAh
+        assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isWithin(PRECISION).of(388.88888);
+        assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+        UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+        assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(20 * MINUTE_IN_MS);
+
+        // Uid1 ran for 20 out of 80 min during the first Display update.
+        // It also ran for 5 out of 45 min during the second Display update:
+        // Uid1 charge = 20 / 80 * 600000000 mAs = 41.66666 mAh
+        assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isWithin(PRECISION).of(41.66666);
+        assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+        UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+        assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(90 * MINUTE_IN_MS);
+
+        // Uid2 ran for 60 out of 80 min during the first Display update.
+        // It also ran for all of the second Display update:
+        // Uid1 charge = 60 / 80 * 600000000 + 800000000 mAs = 347.22222 mAh
+        assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isWithin(PRECISION).of(347.22222);
+        assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+        BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+        assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(110 * MINUTE_IN_MS);
+        assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isWithin(PRECISION).of(388.88888);
+        assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+    }
+
     @Test
     public void testPowerProfileBasedModel() {
         BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
@@ -197,6 +298,95 @@
                 .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
     }
 
+
+    @Test
+    public void testPowerProfileBasedModel_multiDisplay() {
+        mStatsRule.setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 1, 60.0)
+                .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 1, 100.0)
+                .setNumDisplays(2);
+
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        batteryStats.noteScreenStateLocked(0, Display.STATE_ON, 0, 0, 0);
+        batteryStats.noteScreenStateLocked(1, Display.STATE_OFF, 0, 0, 0);
+        batteryStats.noteScreenBrightnessLocked(0, 255, 0, 0);
+        setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true,
+                0, 0);
+
+        batteryStats.noteScreenBrightnessLocked(0, 100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS);
+        batteryStats.noteScreenBrightnessLocked(0, 200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+
+        setProcState(APP_UID1, ActivityManager.PROCESS_STATE_CACHED_EMPTY, false,
+                20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+        setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP, true,
+                20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+
+        batteryStats.noteScreenStateLocked(0, Display.STATE_OFF,
+                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+        batteryStats.noteScreenStateLocked(1, Display.STATE_ON, 80 * MINUTE_IN_MS,
+                80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+        batteryStats.noteScreenBrightnessLocked(1, 20, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+
+        batteryStats.noteScreenBrightnessLocked(1, 250, 86 * MINUTE_IN_MS, 86 * MINUTE_IN_MS);
+        batteryStats.noteScreenBrightnessLocked(1, 75, 98 * MINUTE_IN_MS, 98 * MINUTE_IN_MS);
+        batteryStats.noteScreenStateLocked(1, Display.STATE_OFF, 110 * MINUTE_IN_MS,
+                110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
+
+        setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
+                110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
+
+        mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);
+        ScreenPowerCalculator calculator =
+                new ScreenPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
+
+        BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+        assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(110 * MINUTE_IN_MS);
+        // First display consumed 92 mAh.
+        // Second display ran for 0.5 hours at a base drain rate of 60 mA.
+        // 6 minutes (0.1 hours) spent in the first brightness level which drains an extra 10 mA.
+        // 12 minutes (0.2 hours) spent in the fifth brightness level which drains an extra 90 mA.
+        // 12 minutes (0.2 hours) spent in the second brightness level which drains an extra 30 mA.
+        // 92 + 60 * 0.5 + 10 * 0.1 + 90 * 0.2 + 30 * 0.2 = 147
+        assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isWithin(PRECISION).of(147);
+        assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+        UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+        assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(20 * MINUTE_IN_MS);
+
+        // Uid1 took 20 out of the total of 110 min of foreground activity
+        // Uid1 charge = 20 / 110 * 147.0 = 23.0 mAh
+        assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isWithin(PRECISION).of(26.72727);
+        assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+        UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+        assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(90 * MINUTE_IN_MS);
+
+        // Uid2 took 90 out of the total of 110 min of foreground activity
+        // Uid2 charge = 90 / 110 * 92.0 = 69.0 mAh
+        assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isWithin(PRECISION).of(120.272727);
+        assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+        BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+        assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(110 * MINUTE_IN_MS);
+        assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isWithin(PRECISION).of(147);
+        assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+                .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+    }
+
     private void setProcState(int uid, int procState, boolean resumed, long realtimeMs,
             long uptimeMs) {
         BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
diff --git a/core/tests/coretests/src/com/android/internal/view/AbsCaptureHelperTest.java b/core/tests/coretests/src/com/android/internal/view/AbsCaptureHelperTest.java
index 42b2b16..c76e24c 100644
--- a/core/tests/coretests/src/com/android/internal/view/AbsCaptureHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/view/AbsCaptureHelperTest.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
@@ -38,7 +39,6 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
@@ -47,6 +47,10 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
 /**
  * This test contains a set of operations designed to verify the behavior of a
  * ScrollCaptureViewHelper implementation. Subclasses define and initialize
@@ -88,6 +92,7 @@
     private T mTarget;
     private Rect mScrollBounds;
     private H mHelper;
+    private CancellationSignal mCancellationSignal;
 
     private Instrumentation mInstrumentation;
 
@@ -96,6 +101,7 @@
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         Context context = mInstrumentation.getTargetContext();
         mWm = context.getSystemService(WindowManager.class);
+        mCancellationSignal = new CancellationSignal();
 
         // Instantiate parent view on the main thread
         mInstrumentation.runOnMainSync(() -> mContentRoot = new FrameLayout(context));
@@ -157,7 +163,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onScrollRequested_up_fromTop() {
         initHelper(ScrollPosition.TOP);
 
@@ -168,7 +173,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onScrollRequested_down_fromTop() {
         initHelper(ScrollPosition.TOP);
         Rect request = new Rect(0, WINDOW_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT + CAPTURE_HEIGHT);
@@ -182,7 +186,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onScrollRequested_up_fromMiddle() {
         initHelper(ScrollPosition.MIDDLE);
 
@@ -197,7 +200,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onScrollRequested_down_fromMiddle() {
         initHelper(ScrollPosition.MIDDLE);
 
@@ -212,7 +214,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onScrollRequested_up_fromBottom() {
         initHelper(ScrollPosition.BOTTOM);
 
@@ -227,7 +228,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onScrollRequested_down_fromBottom() {
         initHelper(ScrollPosition.BOTTOM);
 
@@ -242,7 +242,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onScrollRequested_offTopEdge() {
         initHelper(ScrollPosition.TOP);
 
@@ -262,7 +261,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void onScrollRequested_offBottomEdge() {
         initHelper(ScrollPosition.BOTTOM);
 
@@ -279,7 +277,7 @@
     }
 
     @After
-    public final void removeWindow() throws InterruptedException {
+    public final void removeWindow() {
         mInstrumentation.runOnMainSync(() -> {
             if (mContentRoot != null && mContentRoot.isAttachedToWindow()) {
                 mWm.removeViewImmediate(mContentRoot);
@@ -288,17 +286,36 @@
     }
 
     private void initHelper(ScrollPosition position) {
-        setInitialScrollPosition(mTarget, position);
         mHelper = createHelper();
-        mScrollBounds = mHelper.onComputeScrollBounds(mTarget);
-        mHelper.onPrepareForStart(mTarget, mScrollBounds);
+        mInstrumentation.runOnMainSync(() -> {
+            setInitialScrollPosition(mTarget, position);
+            mScrollBounds = mHelper.onComputeScrollBounds(mTarget);
+            mHelper.onPrepareForStart(mTarget, mScrollBounds);
+        });
     }
 
     @NonNull
-    private ScrollResult requestScrollSync(H helper, Rect scrollBounds, Rect request) {
-        helper.onPrepareForStart(mTarget, scrollBounds);
-        ScrollResult result = helper.onScrollRequested(mTarget, scrollBounds, request);
-
+    private ScrollResult requestScrollSync(H helper, Rect scrollBounds, Rect request)  {
+        AtomicReference<ScrollResult> resultRef = new AtomicReference<>();
+        CountDownLatch latch = new CountDownLatch(1);
+        mInstrumentation.runOnMainSync(() -> {
+            helper.onPrepareForStart(mTarget, scrollBounds);
+            helper.onScrollRequested(mTarget, scrollBounds, request, mCancellationSignal,
+                    (result) -> {
+                        resultRef.set(result);
+                        latch.countDown();
+                    });
+        });
+        try {
+            if (!latch.await(5, TimeUnit.SECONDS)) {
+                mCancellationSignal.cancel();
+                fail("Timeout waiting for ScrollResult");
+            }
+        } catch (InterruptedException e) {
+            mCancellationSignal.cancel();
+            fail("Interrupted!");
+        }
+        ScrollResult result = resultRef.get();
         assertNotNull(result);
         return result;
     }
diff --git a/core/tests/coretests/src/com/android/internal/view/ScrollCaptureViewSupportTest.java b/core/tests/coretests/src/com/android/internal/view/ScrollCaptureViewSupportTest.java
index 699008b..8409958 100644
--- a/core/tests/coretests/src/com/android/internal/view/ScrollCaptureViewSupportTest.java
+++ b/core/tests/coretests/src/com/android/internal/view/ScrollCaptureViewSupportTest.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -28,6 +29,8 @@
 
 import org.junit.Test;
 
+import java.util.function.Consumer;
+
 public class ScrollCaptureViewSupportTest {
 
     ScrollCaptureViewHelper<View> mViewHelper = new ScrollCaptureViewHelper<View>() {
@@ -42,9 +45,10 @@
 
         @NonNull
         @Override
-        public ScrollResult onScrollRequested(@NonNull View view, @NonNull Rect scrollBounds,
-                @NonNull Rect requestRect) {
-            return new ScrollResult();
+        public void onScrollRequested(@NonNull View view, @NonNull Rect scrollBounds,
+                @NonNull Rect requestRect, CancellationSignal signal,
+                Consumer<ScrollResult> resultConsumer) {
+            resultConsumer.accept(new ScrollResult());
         }
 
         @Override
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4c930d1..25fb223 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -501,6 +501,8 @@
         <permission name="android.permission.UPDATE_DEVICE_STATS" />
         <!-- Permission required for GTS test - PendingSystemUpdateTest -->
         <permission name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE" />
+        <!-- Permission required for GTS test - GtsAssistIntentTestCases -->
+        <permission name="android.permission.MANAGE_VOICE_KEYPHRASES" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 9573607..6e92755 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1141,6 +1141,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-863438038": {
+      "message": "Aborting Transition: %d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "-861859917": {
       "message": "Attempted to add window to a display that does not exist: %d. Aborting.",
       "level": "WARN",
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index e369acc..9af508a 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -36,22 +36,9 @@
             int format, long transactionPtr);
     private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
                                                               long frameNumber);
-    private static native void nativeSetTransactionCompleteCallback(long ptr, long frameNumber,
-            TransactionCompleteCallback callback);
     private static native long nativeGetLastAcquiredFrameNum(long ptr);
     private static native void nativeApplyPendingTransactions(long ptr, long frameNumber);
 
-    /**
-     * Callback sent to {@link #setTransactionCompleteCallback(long, TransactionCompleteCallback)}
-     */
-    public interface TransactionCompleteCallback {
-        /**
-         * Invoked when the transaction has completed.
-         * @param frameNumber The frame number of the buffer that was in that transaction
-         */
-        void onTransactionComplete(long frameNumber);
-    }
-
     /** Create a new connection with the surface flinger. */
     public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
             @PixelFormat.Format int format) {
@@ -104,16 +91,6 @@
         nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format, 0);
     }
 
-    /**
-     * Set a callback when the transaction with the frame number has been completed.
-     * @param frameNumber The frame number to get the transaction complete callback for
-     * @param completeCallback The callback that should be invoked.
-     */
-    public void setTransactionCompleteCallback(long frameNumber,
-            @Nullable TransactionCompleteCallback completeCallback) {
-        nativeSetTransactionCompleteCallback(mNativeObject, frameNumber, completeCallback);
-    }
-
     @Override
     protected void finalize() throws Throwable {
         try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 962aca1..7784665 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -48,9 +48,11 @@
 import android.view.InsetsSource;
 import android.view.InsetsState;
 import android.view.Surface;
-import android.view.WindowInsets;
+
+import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.R;
+import com.android.internal.policy.SystemBarUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -198,12 +200,13 @@
         recalcInsets(res);
     }
 
-    private void recalcInsets(Resources res) {
+    @VisibleForTesting
+    void recalcInsets(Resources res) {
         computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mInsetsState, mUiMode,
                 mNonDecorInsets, mHasNavigationBar);
         mStableInsets.set(mNonDecorInsets);
         if (mHasStatusBar) {
-            convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar);
+            convertNonDecorInsetsToStableInsets(res, mStableInsets, mCutout, mHasStatusBar);
         }
         mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight);
     }
@@ -323,12 +326,12 @@
     /**
      * Calculates the stable insets if we already have the non-decor insets.
      */
-    private static void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets,
-            int displayWidth, int displayHeight, boolean hasStatusBar) {
+    private void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets,
+            DisplayCutout cutout, boolean hasStatusBar) {
         if (!hasStatusBar) {
             return;
         }
-        int statusBarHeight = getStatusBarHeight(displayWidth > displayHeight, res);
+        int statusBarHeight = SystemBarUtils.getStatusBarHeight(res, cutout);
         inOutInsets.top = Math.max(inOutInsets.top, statusBarHeight);
     }
 
@@ -377,35 +380,6 @@
         }
     }
 
-    /**
-     * Calculates the stable insets without running a layout.
-     *
-     * @param displayRotation the current display rotation
-     * @param displayWidth the current display width
-     * @param displayHeight the current display height
-     * @param displayCutout the current display cutout
-     * @param outInsets the insets to return
-     */
-    static void computeStableInsets(Resources res, int displayRotation, int displayWidth,
-            int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode,
-            Rect outInsets, boolean hasNavigationBar, boolean hasStatusBar) {
-        outInsets.setEmpty();
-
-        // Navigation bar and status bar.
-        computeNonDecorInsets(res, displayRotation, displayWidth, displayHeight, displayCutout,
-                insetsState, uiMode, outInsets, hasNavigationBar);
-        convertNonDecorInsetsToStableInsets(res, outInsets, displayWidth, displayHeight,
-                hasStatusBar);
-    }
-
-    /** Retrieve the statusbar height from resources. */
-    static int getStatusBarHeight(boolean landscape, Resources res) {
-        return landscape ? res.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height_landscape)
-                : res.getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height_portrait);
-    }
-
     /** Calculate the DisplayCutout for a particular display size/rotation. */
     public static DisplayCutout calculateDisplayCutoutForRotation(
             DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
index 75a1dde..3f7d78d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
@@ -39,7 +39,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.R;
+import com.android.internal.policy.SystemBarUtils;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
@@ -307,12 +307,9 @@
         t.apply();
     }
 
-    private int getStatusBarHeight() {
-        final boolean isLandscape =
-                mIsDefaultPortrait ? isDisplaySizeFlipped() : !isDisplaySizeFlipped();
-        return mContext.getResources().getDimensionPixelSize(
-                isLandscape ? R.dimen.status_bar_height_landscape
-                        : R.dimen.status_bar_height_portrait);
+    @VisibleForTesting
+    int getStatusBarHeight() {
+        return SystemBarUtils.getStatusBarHeight(mContext);
     }
 
     void dump(@NonNull PrintWriter pw) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 38079af..90074371 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -689,6 +689,8 @@
         pw.println(mUserId);
         pw.print(innerPrefix + "isShortcutEnabled=");
         pw.println(isShortcutEnabled());
+        pw.print(innerPrefix + "mIsSwipeToNotificationEnabled=");
+        pw.println(mIsSwipeToNotificationEnabled);
 
         if (mBackgroundPanelOrganizer != null) {
             mBackgroundPanelOrganizer.dump(pw);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index ff333c8c..2cb7d1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -244,6 +244,8 @@
         pw.println(TAG);
         pw.print(innerPrefix + "isOneHandedModeEnable=");
         pw.println(getSettingsOneHandedModeEnabled(resolver, userId));
+        pw.print(innerPrefix + "isSwipeToNotificationEnabled=");
+        pw.println(getSettingsSwipeToNotificationEnabled(resolver, userId));
         pw.print(innerPrefix + "oneHandedTimeOut=");
         pw.println(getSettingsOneHandedModeTimeout(resolver, userId));
         pw.print(innerPrefix + "tapsAppToExit=");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 9713962..8a50f22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -144,7 +144,7 @@
         mediaControlFilter.addAction(ACTION_NEXT);
         mediaControlFilter.addAction(ACTION_PREV);
         mContext.registerReceiverForAllUsers(mMediaActionReceiver, mediaControlFilter,
-                SYSTEMUI_PERMISSION, mainHandler);
+                SYSTEMUI_PERMISSION, mainHandler, Context.RECEIVER_EXPORTED);
 
         // Creates the standard media buttons that we may show.
         mPauseAction = getDefaultRemoteAction(R.string.pip_pause,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 9686776..291cbb3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -282,6 +282,7 @@
         mMainExecutor.execute(() -> {
             mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
         });
+        mPipTransitionController.setPipOrganizer(this);
         displayController.addDisplayWindowListener(this);
     }
 
@@ -349,6 +350,10 @@
         }
     }
 
+    public ActivityManager.RunningTaskInfo getTaskInfo() {
+        return mTaskInfo;
+    }
+
     public SurfaceControl getSurfaceControl() {
         return mLeash;
     }
@@ -716,6 +721,9 @@
             mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
         }
 
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            mPipTransitionController.forceFinishTransition();
+        }
         final PipAnimationController.PipTransitionAnimator<?> animator =
                 mPipAnimationController.getCurrentAnimator();
         if (animator != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 6fec1fb..328f3ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -32,17 +32,18 @@
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
 
+import android.app.ActivityManager;
 import android.app.TaskInfo;
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
-import android.window.WindowContainerTransactionCallback;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -57,11 +58,14 @@
  */
 public class PipTransition extends PipTransitionController {
 
+    private static final String TAG = PipTransition.class.getSimpleName();
+
     private final PipTransitionState mPipTransitionState;
     private final int mEnterExitAnimationDuration;
     private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
     private Transitions.TransitionFinishCallback mFinishCallback;
     private Rect mExitDestinationBounds = new Rect();
+    private IBinder mExitTransition = null;
 
     public PipTransition(Context context,
             PipBoundsState pipBoundsState,
@@ -96,7 +100,7 @@
     public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
         if (destinationBounds != null) {
             mExitDestinationBounds.set(destinationBounds);
-            mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
+            mExitTransition = mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
         } else {
             mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this);
         }
@@ -109,14 +113,19 @@
             @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
             @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
 
-        if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) {
-            final TransitionInfo.Change change = info.getChanges().get(0);
-            mFinishCallback = finishCallback;
-            startTransaction.apply();
-            boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
-                    new Rect(mExitDestinationBounds));
-            mExitDestinationBounds.setEmpty();
-            return success;
+        if (mExitTransition == transition || info.getType() == TRANSIT_EXIT_PIP) {
+            mExitTransition = null;
+            if (info.getChanges().size() == 1) {
+                final TransitionInfo.Change change = info.getChanges().get(0);
+                mFinishCallback = finishCallback;
+                startTransaction.apply();
+                boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
+                        new Rect(mExitDestinationBounds));
+                mExitDestinationBounds.setEmpty();
+                return success;
+            } else {
+                Log.e(TAG, "Got an exit-pip transition with unexpected change-list");
+            }
         }
 
         if (info.getType() == TRANSIT_REMOVE_PIP) {
@@ -183,26 +192,58 @@
     }
 
     @Override
+    public void onTransitionMerged(@NonNull IBinder transition) {
+        if (transition != mExitTransition) {
+            return;
+        }
+        // This means an expand happened before enter-pip finished and we are now "merging" a
+        // no-op transition that happens to match our exit-pip.
+        boolean cancelled = false;
+        if (mPipAnimationController.getCurrentAnimator() != null) {
+            mPipAnimationController.getCurrentAnimator().cancel();
+            cancelled = true;
+        }
+        // Unset exitTransition AFTER cancel so that finishResize knows we are merging.
+        mExitTransition = null;
+        if (!cancelled) return;
+        final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
+        if (taskInfo != null) {
+            startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
+                    new Rect(mExitDestinationBounds));
+        }
+        mExitDestinationBounds.setEmpty();
+    }
+
+    @Override
     public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
             @PipAnimationController.TransitionDirection int direction,
-            SurfaceControl.Transaction tx) {
+            @Nullable SurfaceControl.Transaction tx) {
 
         if (isInPipDirection(direction)) {
             mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
         }
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        prepareFinishResizeTransaction(taskInfo, destinationBounds,
-                direction, tx, wct);
-        mFinishCallback.onTransitionFinished(wct, new WindowContainerTransactionCallback() {
-            @Override
-            public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) {
-                t.merge(tx);
-                t.apply();
+        // If there is an expected exit transition, then the exit will be "merged" into this
+        // transition so don't fire the finish-callback in that case.
+        if (mExitTransition == null && mFinishCallback != null) {
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            prepareFinishResizeTransaction(taskInfo, destinationBounds,
+                    direction, wct);
+            if (tx != null) {
+                wct.setBoundsChangeTransaction(taskInfo.token, tx);
             }
-        });
+            mFinishCallback.onTransitionFinished(wct, null /* wctCallback */);
+            mFinishCallback = null;
+        }
         finishResizeForMenu(destinationBounds);
     }
 
+    @Override
+    public void forceFinishTransition() {
+        if (mFinishCallback == null) return;
+        mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCallback */);
+        mFinishCallback = null;
+    }
+
     private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
             final Rect destinationBounds) {
         PipAnimationController.PipTransitionAnimator animator =
@@ -243,7 +284,7 @@
             startTransaction.merge(tx);
             startTransaction.apply();
             mPipBoundsState.setBounds(destinationBounds);
-            onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, tx);
+            onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, null /* tx */);
             sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
             mFinishCallback = null;
             mPipTransitionState.setInSwipePipToHomeTransition(false);
@@ -292,7 +333,6 @@
 
     private void prepareFinishResizeTransaction(TaskInfo taskInfo, Rect destinationBounds,
             @PipAnimationController.TransitionDirection int direction,
-            SurfaceControl.Transaction tx,
             WindowContainerTransaction wct) {
         Rect taskBounds = null;
         if (isInPipDirection(direction)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index dbf603c..376f329 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -49,6 +49,7 @@
     protected final Transitions mTransitions;
     private final Handler mMainHandler;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
+    protected PipTaskOrganizer mPipOrganizer;
 
     protected final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
             new PipAnimationController.PipAnimationCallback() {
@@ -103,6 +104,13 @@
         // Default implementation does nothing.
     }
 
+    /**
+     * Called when the transition animation can't continue (eg. task is removed during
+     * animation)
+     */
+    public void forceFinishTransition() {
+    }
+
     public PipTransitionController(PipBoundsState pipBoundsState,
             PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
             PipAnimationController pipAnimationController, Transitions transitions,
@@ -119,6 +127,10 @@
         }
     }
 
+    void setPipOrganizer(PipTaskOrganizer pto) {
+        mPipOrganizer = pto;
+    }
+
     /**
      * Registers {@link PipTransitionCallback} to receive transition callbacks.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index ec71fbe..3b75bfb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -215,10 +215,12 @@
         options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
 
         try {
+            final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+            mStageCoordinator.prepareEvictChildTasks(position, evictWct);
             final int result =
                     ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
             if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
-                mStageCoordinator.evictOccludedChildren(position);
+                mSyncQueue.queue(evictWct);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to launch task", e);
@@ -229,13 +231,15 @@
             @SplitScreen.StageType int stage, @SplitPosition int position,
             @Nullable Bundle options, UserHandle user) {
         options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
+        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+        mStageCoordinator.prepareEvictChildTasks(position, evictWct);
 
         try {
             LauncherApps launcherApps =
                     mContext.getSystemService(LauncherApps.class);
             launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
                     options, user);
-            mStageCoordinator.evictOccludedChildren(position);
+            mSyncQueue.queue(evictWct);
         } catch (ActivityNotFoundException e) {
             Slog.e(TAG, "Failed to launch shortcut", e);
         }
@@ -255,6 +259,9 @@
     private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
             @SplitScreen.StageType int stage, @SplitPosition int position,
             @Nullable Bundle options) {
+        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+        mStageCoordinator.prepareEvictChildTasks(position, evictWct);
+
         LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
             @Override
             public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
@@ -280,12 +287,11 @@
                     }
                 }
 
-                // Launching a new app into a specific split evicts tasks previously in the same
-                // split.
-                mStageCoordinator.evictOccludedChildren(position);
+                mSyncQueue.queue(evictWct);
             }
         };
-        WindowContainerTransaction wct = new WindowContainerTransaction();
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
         options = mStageCoordinator.resolveStartStage(stage, position, options, wct);
         wct.sendPendingIntent(intent, fillInIntent, options);
         mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 0cff18e..72d9880 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -394,10 +394,16 @@
                 TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this);
     }
 
-    void evictOccludedChildren(@SplitPosition int position) {
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
-        (position == mSideStagePosition ? mSideStage : mMainStage).evictOccludedChildren(wct);
-        mTaskOrganizer.applyTransaction(wct);
+    /**
+     * Collects all the current child tasks of a specific split and prepares transaction to evict
+     * them to display.
+     */
+    void prepareEvictChildTasks(@SplitPosition int position, WindowContainerTransaction wct) {
+        if (position == mSideStagePosition) {
+            mSideStage.evictAllChildren(wct);
+        } else {
+            mMainStage.evictAllChildren(wct);
+        }
     }
 
     Bundle resolveStartStage(@SplitScreen.StageType int stage,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 071badf..6f1a09d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -248,12 +248,11 @@
         wct.reorder(mChildrenTaskInfo.get(taskId).token, onTop /* onTop */);
     }
 
-    void evictOccludedChildren(WindowContainerTransaction wct) {
+    /** Collects all the current child tasks and prepares transaction to evict them to display. */
+    void evictAllChildren(WindowContainerTransaction wct) {
         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
-            if (!taskInfo.isVisible) {
-                wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
-            }
+            wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 663d647..7abda99 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -70,6 +70,7 @@
 import android.view.animation.Animation;
 import android.view.animation.Transformation;
 import android.window.TransitionInfo;
+import android.window.TransitionMetrics;
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
@@ -362,6 +363,7 @@
             }
         }
         startTransaction.apply();
+        TransitionMetrics.getInstance().reportAnimationStart(transition);
         // run finish now in-case there are no animations
         onAnimFinish.run();
         return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 2720157..c369831 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -44,6 +44,7 @@
 import android.window.RemoteTransition;
 import android.window.TransitionFilter;
 import android.window.TransitionInfo;
+import android.window.TransitionMetrics;
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
 import android.window.WindowContainerTransactionCallback;
@@ -192,6 +193,8 @@
     public void register(ShellTaskOrganizer taskOrganizer) {
         if (mPlayerImpl == null) return;
         taskOrganizer.registerTransitionPlayer(mPlayerImpl);
+        // Pre-load the instance.
+        TransitionMetrics.getInstance();
     }
 
     /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index ef399e4..b432bb6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -26,6 +26,7 @@
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiObject2
 import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.Flicker
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -47,13 +48,9 @@
     protected val notifyManager = INotificationManager.Stub.asInterface(
             ServiceManager.getService(Context.NOTIFICATION_SERVICE))
 
-    protected val packageManager = context.getPackageManager()
-    protected val uid = packageManager.getApplicationInfo(
+    protected val uid = context.packageManager.getApplicationInfo(
             testApp.component.packageName, 0).uid
 
-    protected lateinit var addBubbleBtn: UiObject2
-    protected lateinit var cancelAllBtn: UiObject2
-
     protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
 
     @JvmOverloads
@@ -67,10 +64,8 @@
                     notifyManager.setBubblesAllowed(testApp.component.packageName,
                             uid, NotificationManager.BUBBLE_PREFERENCE_ALL)
                     testApp.launchViaIntent(wmHelper)
-                    addBubbleBtn = device.wait(Until.findObject(
-                            By.text("Add Bubble")), FIND_OBJECT_TIMEOUT)
-                    cancelAllBtn = device.wait(Until.findObject(
-                            By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT)
+                    waitAndGetAddBubbleBtn()
+                    waitAndGetCancelAllBtn()
                 }
             }
 
@@ -84,6 +79,11 @@
         }
     }
 
+    protected fun Flicker.waitAndGetAddBubbleBtn(): UiObject2? = device.wait(Until.findObject(
+            By.text("Add Bubble")), FIND_OBJECT_TIMEOUT)
+    protected fun Flicker.waitAndGetCancelAllBtn(): UiObject2? = device.wait(Until.findObject(
+            By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT)
+
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
         return FlickerBuilder(instrumentation).apply {
@@ -102,7 +102,6 @@
         }
 
         const val FIND_OBJECT_TIMEOUT = 2000L
-        const val WINDOW_UPDAT_TIMEOUT = 2000L
         const val SYSTEM_UI_PACKAGE = SYSTEMUI_PACKAGE
         const val BUBBLE_RES_NAME = "bubble_view"
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
index aec6aa9..80acd3d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
@@ -18,9 +18,9 @@
 
 import android.content.Context
 import android.graphics.Point
+import android.platform.test.annotations.Postsubmit
 import android.util.DisplayMetrics
 import android.view.WindowManager
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.Until
@@ -46,18 +46,19 @@
 @Group4
 class DismissBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
 
-    val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
-    val displaySize = DisplayMetrics()
+    private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+    private val displaySize = DisplayMetrics()
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-        get() = buildTransition() {
+        get() = buildTransition {
             setup {
                 eachRun {
-                    addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Add Bubble not found")
+                    val addBubbleBtn = waitAndGetAddBubbleBtn()
+                    addBubbleBtn?.click() ?: error("Add Bubble not found")
                 }
             }
             transitions {
-                wm?.run { wm.getDefaultDisplay().getMetrics(displaySize) } ?: error("WM not found")
+                wm.run { wm.getDefaultDisplay().getMetrics(displaySize) }
                 val dist = Point((displaySize.widthPixels / 2), displaySize.heightPixels)
                 val showBubble = device.wait(Until.findObject(
                         By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), FIND_OBJECT_TIMEOUT)
@@ -65,7 +66,7 @@
             }
         }
 
-    @FlakyTest
+    @Postsubmit
     @Test
     fun testAppIsAlwaysVisible() {
         testSpec.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
index 9287e17..66520d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.bubble
 
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.Postsubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.Until
@@ -45,10 +45,11 @@
 class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-        get() = buildTransition() {
+        get() = buildTransition {
             setup {
                 test {
-                    addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Bubble widget not found")
+                    val addBubbleBtn = waitAndGetAddBubbleBtn()
+                    addBubbleBtn?.click() ?: error("Add Bubble not found")
                 }
             }
             transitions {
@@ -58,7 +59,7 @@
             }
         }
 
-    @FlakyTest
+    @Postsubmit
     @Test
     fun testAppIsAlwaysVisible() {
         testSpec.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
index 3aeb5ad..9c172a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.bubble
 
-import android.os.SystemClock
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.uiautomator.By
@@ -44,31 +43,33 @@
 class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-        get() = buildTransition() {
+        get() = buildTransition {
             setup {
                 eachRun {
+                    val addBubbleBtn = waitAndGetAddBubbleBtn()
                     addBubbleBtn?.click() ?: error("Bubble widget not found")
-                    device.sleep()
-                    SystemClock.sleep(2000)
-                    device.wakeUp()
+                    wmHelper.waitFor("noAppWindowsOnTop") {
+                        it.wmState.topVisibleAppWindow.isEmpty()
+                    }
                 }
             }
             transitions {
                 val notification = device.wait(Until.findObject(
                     By.text("BubbleChat")), FIND_OBJECT_TIMEOUT)
                 notification?.click() ?: error("Notification not found")
-                instrumentation.getUiAutomation().syncInputTransactions()
+                instrumentation.uiAutomation.syncInputTransactions()
                 val showBubble = device.wait(Until.findObject(
                         By.res("com.android.systemui", "bubble_view")), FIND_OBJECT_TIMEOUT)
                 showBubble?.click() ?: error("Bubble notify not found")
-                instrumentation.getUiAutomation().syncInputTransactions()
+                instrumentation.uiAutomation.syncInputTransactions()
+                val cancelAllBtn = waitAndGetCancelAllBtn()
                 cancelAllBtn?.click() ?: error("Cancel widget not found")
             }
         }
 
     @FlakyTest
     @Test
-    fun testAppisVisibleAtEnd() {
+    fun testAppIsVisibleAtEnd() {
         testSpec.assertLayersEnd {
             this.isVisible(testApp.component)
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
index ed68d47..97c27c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.flicker.bubble
 
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.Postsubmit
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -42,13 +42,14 @@
 class LaunchBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-        get() = buildTransition() {
+        get() = buildTransition {
             transitions {
-                addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Bubble widget not found")
+                val addBubbleBtn = waitAndGetAddBubbleBtn()
+                addBubbleBtn?.click() ?: error("Bubble widget not found")
             }
         }
 
-    @FlakyTest
+    @Postsubmit
     @Test
     fun testAppIsAlwaysVisible() {
         testSpec.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
index edddc1e..d92e047 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -17,7 +17,7 @@
 package com.android.wm.shell.flicker.bubble
 
 import android.os.SystemClock
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.Postsubmit
 import androidx.test.filters.RequiresDevice
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.Until
@@ -44,10 +44,11 @@
 class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-        get() = buildTransition() {
+        get() = buildTransition {
             setup {
                 test {
                     for (i in 1..3) {
+                        val addBubbleBtn = waitAndGetAddBubbleBtn()
                         addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Add Bubble not found")
                     }
                     val showBubble = device.wait(Until.findObject(
@@ -66,7 +67,7 @@
             }
         }
 
-    @FlakyTest
+    @Postsubmit
     @Test
     fun testAppIsAlwaysVisible() {
         testSpec.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
index 88e754c..0ffa5b3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
@@ -21,9 +21,14 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -35,8 +40,12 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
+import com.android.internal.policy.SystemBarUtils;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.MockitoSession;
 
 /**
  * Tests for {@link DisplayLayout}.
@@ -46,29 +55,48 @@
  */
 @SmallTest
 public class DisplayLayoutTest {
+    private MockitoSession mMockitoSession;
+
+    @Before
+    public void setup() {
+        mMockitoSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(SystemBarUtils.class)
+                .startMocking();
+    }
+
+    @After
+    public void tearDown() {
+        mMockitoSession.finishMocking();
+    }
 
     @Test
     public void testInsets() {
-        Resources res = createResources(40, 50, false, 30, 40);
+        Resources res = createResources(40, 50, false);
         // Test empty display, no bars or anything
         DisplayInfo info = createDisplayInfo(1000, 1500, 0, ROTATION_0);
         DisplayLayout dl = new DisplayLayout(info, res, false, false);
+        when(SystemBarUtils.getStatusBarHeight(eq(res), any())).thenReturn(40);
+        dl.recalcInsets(res);
         assertEquals(new Rect(0, 0, 0, 0), dl.stableInsets());
         assertEquals(new Rect(0, 0, 0, 0), dl.nonDecorInsets());
 
         // Test with bars
         dl = new DisplayLayout(info, res, true, true);
+        dl.recalcInsets(res);
         assertEquals(new Rect(0, 40, 0, 50), dl.stableInsets());
         assertEquals(new Rect(0, 0, 0, 50), dl.nonDecorInsets());
 
         // Test just cutout
         info = createDisplayInfo(1000, 1500, 60, ROTATION_0);
         dl = new DisplayLayout(info, res, false, false);
+        dl.recalcInsets(res);
         assertEquals(new Rect(0, 60, 0, 0), dl.stableInsets());
         assertEquals(new Rect(0, 60, 0, 0), dl.nonDecorInsets());
 
         // Test with bars and cutout
         dl = new DisplayLayout(info, res, true, true);
+        dl.recalcInsets(res);
         assertEquals(new Rect(0, 60, 0, 50), dl.stableInsets());
         assertEquals(new Rect(0, 60, 0, 50), dl.nonDecorInsets());
     }
@@ -76,27 +104,30 @@
     @Test
     public void testRotate() {
         // Basic rotate utility
-        Resources res = createResources(40, 50, false, 30, 40);
+        Resources res = createResources(40, 50, false);
         DisplayInfo info = createDisplayInfo(1000, 1500, 60, ROTATION_0);
         DisplayLayout dl = new DisplayLayout(info, res, true, true);
+        when(SystemBarUtils.getStatusBarHeight(eq(res), any())).thenReturn(40);
+        dl.recalcInsets(res);
         assertEquals(new Rect(0, 60, 0, 50), dl.stableInsets());
         assertEquals(new Rect(0, 60, 0, 50), dl.nonDecorInsets());
 
         // Rotate to 90
+        when(SystemBarUtils.getStatusBarHeight(eq(res), any())).thenReturn(30);
         dl.rotateTo(res, ROTATION_90);
         assertEquals(new Rect(60, 30, 0, 40), dl.stableInsets());
         assertEquals(new Rect(60, 0, 0, 40), dl.nonDecorInsets());
 
         // Rotate with moving navbar
-        res = createResources(40, 50, true, 30, 40);
+        res = createResources(40, 50, true);
         dl = new DisplayLayout(info, res, true, true);
+        when(SystemBarUtils.getStatusBarHeight(eq(res), any())).thenReturn(30);
         dl.rotateTo(res, ROTATION_270);
         assertEquals(new Rect(40, 30, 60, 0), dl.stableInsets());
         assertEquals(new Rect(40, 0, 60, 0), dl.nonDecorInsets());
     }
 
-    private Resources createResources(
-            int navLand, int navPort, boolean navMoves, int statusLand, int statusPort) {
+    private Resources createResources(int navLand, int navPort, boolean navMoves) {
         Configuration cfg = new Configuration();
         cfg.uiMode = UI_MODE_TYPE_NORMAL;
         Resources res = mock(Resources.class);
@@ -108,8 +139,6 @@
         doReturn(navPort).when(res).getDimensionPixelSize(R.dimen.navigation_bar_height);
         doReturn(navLand).when(res).getDimensionPixelSize(R.dimen.navigation_bar_width);
         doReturn(navMoves).when(res).getBoolean(R.bool.config_navBarCanMove);
-        doReturn(statusLand).when(res).getDimensionPixelSize(R.dimen.status_bar_height_landscape);
-        doReturn(statusPort).when(res).getDimensionPixelSize(R.dimen.status_bar_height_portrait);
         doReturn(cfg).when(res).getConfiguration();
         return res;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
index 3c124ba..078e2b6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
@@ -48,7 +48,6 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.R;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
@@ -124,6 +123,7 @@
 
     @Test
     public void testEnableHideDisplayCutout() {
+        doReturn(mFakeStatusBarHeightPortrait).when(mOrganizer).getStatusBarHeight();
         mOrganizer.enableHideDisplayCutout();
 
         verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
@@ -154,8 +154,7 @@
         doReturn(mFakeDefaultBounds).when(mOrganizer).getDisplayBoundsOfNaturalOrientation();
         doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
                 .getDisplayCutoutInsetsOfNaturalOrientation();
-        mContext.getOrCreateTestableResources().addOverride(
-                R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait);
+        doReturn(mFakeStatusBarHeightPortrait).when(mOrganizer).getStatusBarHeight();
         doReturn(Surface.ROTATION_0).when(mDisplayLayout).rotation();
         mOrganizer.enableHideDisplayCutout();
 
@@ -173,8 +172,7 @@
         doReturn(mFakeDefaultBounds).when(mOrganizer).getDisplayBoundsOfNaturalOrientation();
         doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
                 .getDisplayCutoutInsetsOfNaturalOrientation();
-        mContext.getOrCreateTestableResources().addOverride(
-                R.dimen.status_bar_height_landscape, mFakeStatusBarHeightLandscape);
+        doReturn(mFakeStatusBarHeightLandscape).when(mOrganizer).getStatusBarHeight();
         doReturn(Surface.ROTATION_90).when(mDisplayLayout).rotation();
         mOrganizer.enableHideDisplayCutout();
 
@@ -192,8 +190,7 @@
         doReturn(mFakeDefaultBounds).when(mOrganizer).getDisplayBoundsOfNaturalOrientation();
         doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
                 .getDisplayCutoutInsetsOfNaturalOrientation();
-        mContext.getOrCreateTestableResources().addOverride(
-                R.dimen.status_bar_height_landscape, mFakeStatusBarHeightLandscape);
+        doReturn(mFakeStatusBarHeightLandscape).when(mOrganizer).getStatusBarHeight();
         doReturn(Surface.ROTATION_270).when(mDisplayLayout).rotation();
         mOrganizer.enableHideDisplayCutout();
 
@@ -211,8 +208,7 @@
         doReturn(mFakeDefaultBounds).when(mOrganizer).getDisplayBoundsOfNaturalOrientation();
         doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
                 .getDisplayCutoutInsetsOfNaturalOrientation();
-        mContext.getOrCreateTestableResources().addOverride(
-                R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait);
+        doReturn(mFakeStatusBarHeightPortrait).when(mOrganizer).getStatusBarHeight();
         mOrganizer.enableHideDisplayCutout();
 
         // disable hide display cutout
@@ -230,8 +226,7 @@
         doReturn(200).when(mDisplayLayout).height();
         doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
                 .getDisplayCutoutInsetsOfNaturalOrientation();
-        mContext.getOrCreateTestableResources().addOverride(
-                R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait);
+        doReturn(mFakeStatusBarHeightPortrait).when(mOrganizer).getStatusBarHeight();
         doReturn(Surface.ROTATION_0).when(mDisplayLayout).rotation();
         mOrganizer.enableHideDisplayCutout();
         assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(0, 15, 100, 200));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index a5746a4..3ed72e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -21,6 +21,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -31,6 +33,7 @@
 import android.os.SystemProperties;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.window.WindowContainerTransaction;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -50,7 +53,7 @@
 /**
  * Tests for {@link StageTaskListener}
  * Build/Install/Run:
- *  atest WMShellUnitTests:StageTaskListenerTests
+ * atest WMShellUnitTests:StageTaskListenerTests
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -58,11 +61,16 @@
     private static final boolean ENABLE_SHELL_TRANSITIONS =
             SystemProperties.getBoolean("persist.debug.shell_transit", false);
 
-    @Mock private ShellTaskOrganizer mTaskOrganizer;
-    @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
-    @Mock private SyncTransactionQueue mSyncQueue;
-    @Mock private StageTaskUnfoldController mStageTaskUnfoldController;
-    @Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
+    @Mock
+    private ShellTaskOrganizer mTaskOrganizer;
+    @Mock
+    private StageTaskListener.StageListenerCallbacks mCallbacks;
+    @Mock
+    private SyncTransactionQueue mSyncQueue;
+    @Mock
+    private StageTaskUnfoldController mStageTaskUnfoldController;
+    @Captor
+    private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
     private SurfaceSession mSurfaceSession = new SurfaceSession();
     private SurfaceControl mSurfaceControl;
     private ActivityManager.RunningTaskInfo mRootTask;
@@ -167,4 +175,18 @@
         mStageTaskListener.onTaskInfoChanged(childTask);
         verify(mCallbacks).onNoLongerSupportMultiWindow();
     }
+
+    @Test
+    public void testEvictAllChildren() {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        mStageTaskListener.evictAllChildren(wct);
+        assertTrue(wct.isEmpty());
+
+        final ActivityManager.RunningTaskInfo childTask =
+                new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
+        mStageTaskListener.onTaskAppeared(childTask, mSurfaceControl);
+
+        mStageTaskListener.evictAllChildren(wct);
+        assertFalse(wct.isEmpty());
+    }
 }
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 0cde3d1..22904a0 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -25,6 +25,7 @@
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
 #include "androidfw/ResourceUtils.h"
 #include "androidfw/Util.h"
 #include "utils/ByteOrder.h"
@@ -600,6 +601,7 @@
     return base::unexpected(result.error());
   }
 
+  bool overlaid = false;
   if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) {
     for (const auto& id_map : package_group.overlays_) {
       auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
@@ -616,6 +618,27 @@
         if (UNLIKELY(logging_enabled)) {
           last_resolution_.steps.push_back(
               Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, String8(), result->cookie});
+          if (auto path = apk_assets_[result->cookie]->GetPath()) {
+            const std::string overlay_path = path->data();
+            if (IsFabricatedOverlay(overlay_path)) {
+              // FRRO don't have package name so we use the creating package here.
+              String8 frro_name = String8("FRRO");
+              // Get the first part of it since the expected one should be like
+              // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
+              // under /data/resource-cache/.
+              const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
+              const size_t end = name.find('-');
+              if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
+                frro_name.append(base::StringPrintf(" created by %s",
+                                                    name.substr(0 /* pos */,
+                                                                end).c_str()).c_str());
+              }
+              last_resolution_.best_package_name = frro_name;
+            } else {
+              last_resolution_.best_package_name = result->package_name->c_str();
+            }
+          }
+          overlaid = true;
         }
         continue;
       }
@@ -646,6 +669,9 @@
         last_resolution_.steps.push_back(
             Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(),
                              overlay_result->cookie});
+        last_resolution_.best_package_name =
+            overlay_result->package_name->c_str();
+        overlaid = true;
       }
     }
   }
@@ -654,6 +680,10 @@
     last_resolution_.cookie = result->cookie;
     last_resolution_.type_string_ref = result->type_string_ref;
     last_resolution_.entry_string_ref = result->entry_string_ref;
+    last_resolution_.best_config_name = result->config.toString();
+    if (!overlaid) {
+      last_resolution_.best_package_name = result->package_name->c_str();
+    }
   }
 
   return result;
@@ -671,8 +701,6 @@
   uint32_t best_offset = 0U;
   uint32_t type_flags = 0U;
 
-  std::vector<Resolution::Step> resolution_steps;
-
   // If `desired_config` is not the same as the set configuration or the caller will accept a value
   // from any configuration, then we cannot use our filtered list of types since it only it contains
   // types matched to the set configuration.
@@ -725,7 +753,7 @@
         resolution_type = Resolution::Step::Type::OVERLAID;
       } else {
         if (UNLIKELY(logging_enabled)) {
-          resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED,
+          last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED,
                                                       this_config.toString(),
                                                       cookie});
         }
@@ -742,7 +770,7 @@
 
       if (!offset.has_value()) {
         if (UNLIKELY(logging_enabled)) {
-          resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
+          last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
                                                       this_config.toString(),
                                                       cookie});
         }
@@ -806,6 +834,8 @@
   last_resolution_.steps.clear();
   last_resolution_.type_string_ref = StringPoolRef();
   last_resolution_.entry_string_ref = StringPoolRef();
+  last_resolution_.best_config_name.clear();
+  last_resolution_.best_package_name.clear();
 }
 
 void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
@@ -865,6 +895,10 @@
     }
   }
 
+  log_stream << "\nBest matching is from "
+             << (last_resolution_.best_config_name.isEmpty() ? "default"
+                                                   : last_resolution_.best_config_name)
+             << " configuration of " << last_resolution_.best_package_name;
   return log_stream.str();
 }
 
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 7d01395..a3b42df 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -486,6 +486,12 @@
 
     // Steps taken to resolve last resource.
     std::vector<Step> steps;
+
+    // The configuration name of the best resource found.
+    String8 best_config_name;
+
+    // The package name of the best resource found.
+    String8 best_package_name;
   };
 
   // Record of the last resolved resource's resolution path.
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 3c4ee4e..4394740 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -766,7 +766,9 @@
   auto result = assetmanager.GetLastResourceResolution();
   EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
             "\tFor config - de\n"
-            "\tFound initial: basic/basic.apk", result);
+            "\tFound initial: basic/basic.apk\n"
+            "Best matching is from default configuration of com.android.basic",
+            result);
 }
 
 TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
@@ -787,7 +789,9 @@
   EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
             "\tFor config - de\n"
             "\tFound initial: basic/basic.apk\n"
-            "\tFound better: basic/basic_de_fr.apk - de", result);
+            "\tFound better: basic/basic_de_fr.apk - de\n"
+            "Best matching is from de configuration of com.android.basic",
+            result);
 }
 
 TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
diff --git a/libs/androidfw/tests/PosixUtils_test.cpp b/libs/androidfw/tests/PosixUtils_test.cpp
index c7b3eba..8c49350 100644
--- a/libs/androidfw/tests/PosixUtils_test.cpp
+++ b/libs/androidfw/tests/PosixUtils_test.cpp
@@ -30,14 +30,14 @@
   const auto result = ExecuteBinary({"/bin/date", "--help"});
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, 0);
-  ASSERT_EQ(result->stdout_str.find("usage: date "), 0);
+  ASSERT_GE(result->stdout_str.find("usage: date "), 0);
 }
 
 TEST(PosixUtilsTest, RelativePathToBinary) {
   const auto result = ExecuteBinary({"date", "--help"});
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, 0);
-  ASSERT_EQ(result->stdout_str.find("usage: date "), 0);
+  ASSERT_GE(result->stdout_str.find("usage: date "), 0);
 }
 
 TEST(PosixUtilsTest, BadParameters) {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 99ecaa8..38b5715 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -510,6 +510,8 @@
     Frame frame = mRenderPipeline->getFrame();
     SkRect windowDirty = computeDirtyRect(frame, &dirty);
 
+    ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty));
+
     bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
                                       mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
                                       &(profiler()));
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 669bfc5..d061bd3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5233,21 +5233,6 @@
         }
     }
 
-    /**
-     * @hide
-     * Notifies AudioService that it is connected to an A2DP device that supports absolute volume,
-     * so that AudioService can send volume change events to the A2DP device, rather than handling
-     * them.
-     */
-    public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
-        final IAudioService service = getService();
-        try {
-            service.avrcpSupportsAbsoluteVolume(address, support);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
      /**
       * {@hide}
       */
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 067f8215..5b05cd3 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -180,8 +180,6 @@
 
     int getEncodedSurroundMode(int targetSdkVersion);
 
-    oneway void avrcpSupportsAbsoluteVolume(String address, boolean support);
-
     void setSpeakerphoneOn(IBinder cb, boolean on);
 
     boolean isSpeakerphoneOn();
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index b59c71f..742207b 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -39,6 +39,7 @@
     MediaRouterClientState getState(IMediaRouterClient client);
     boolean isPlaybackActive(IMediaRouterClient client);
 
+    void setBluetoothA2dpOn(IMediaRouterClient client, boolean on);
     void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan);
     void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
     void requestSetVolume(IMediaRouterClient client, String routeId, int volume);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 83bc38b2..1077275 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -158,18 +158,10 @@
  *         the user supplied callback method OnErrorListener.onError() will be
  *         invoked by the internal player engine and the object will be
  *         transfered to the <em>Error</em> state. </li>
- *         <li>It is also recommended that once
- *         a MediaPlayer object is no longer being used, call {@link #release()} immediately
- *         so that resources used by the internal player engine associated with the
- *         MediaPlayer object can be released immediately. Resource may include
- *         singleton resources such as hardware acceleration components and
- *         failure to call {@link #release()} may cause subsequent instances of
- *         MediaPlayer objects to fallback to software implementations or fail
- *         altogether. Once the MediaPlayer
- *         object is in the <em>End</em> state, it can no longer be used and
- *         there is no way to bring it back to any other state. </li>
- *         <li>Furthermore,
- *         the MediaPlayer objects created using <code>new</code> is in the
+ *         <li>You must call {@link #release()} once you have finished using an instance to release
+ *         acquired resources, such as memory and codecs. Once you have called {@link #release}, you
+ *         must no longer interact with the released instance.
+ *         <li>MediaPlayer objects created using <code>new</code> is in the
  *         <em>Idle</em> state, while those created with one
  *         of the overloaded convenient <code>create</code> methods are <em>NOT</em>
  *         in the <em>Idle</em> state. In fact, the objects are in the <em>Prepared</em>
@@ -407,7 +399,7 @@
  * <tr><td>release </p></td>
  *     <td>any </p></td>
  *     <td>{} </p></td>
- *     <td>After {@link #release()}, the object is no longer available. </p></td></tr>
+ *     <td>After {@link #release()}, you must not interact with the object. </p></td></tr>
  * <tr><td>reset </p></td>
  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
  *         PlaybackCompleted, Error}</p></td>
@@ -657,11 +649,13 @@
     private ProvisioningThread mDrmProvisioningThread;
 
     /**
-     * Default constructor. Consider using one of the create() methods for
-     * synchronously instantiating a MediaPlayer from a Uri or resource.
-     * <p>When done with the MediaPlayer, you should call  {@link #release()},
-     * to free the resources. If not released, too many MediaPlayer instances may
-     * result in an exception.</p>
+     * Default constructor.
+     *
+     * <p>Consider using one of the create() methods for synchronously instantiating a MediaPlayer
+     * from a Uri or resource.
+     *
+     * <p>You must call {@link #release()} when you are finished using the instantiated instance.
+     * Doing so frees any resources you have previously acquired.
      */
     public MediaPlayer() {
         this(AudioSystem.AUDIO_SESSION_ALLOCATE);
@@ -873,9 +867,10 @@
     /**
      * Convenience method to create a MediaPlayer for a given Uri.
      * On success, {@link #prepare()} will already have been called and must not be called again.
-     * <p>When done with the MediaPlayer, you should call  {@link #release()},
-     * to free the resources. If not released, too many MediaPlayer instances will
-     * result in an exception.</p>
+     *
+     * <p>You must call {@link #release()} when you are finished using the created instance. Doing
+     * so frees any resources you have previously acquired.
+     *
      * <p>Note that since {@link #prepare()} is called automatically in this method,
      * you cannot change the audio
      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
@@ -892,9 +887,10 @@
     /**
      * Convenience method to create a MediaPlayer for a given Uri.
      * On success, {@link #prepare()} will already have been called and must not be called again.
-     * <p>When done with the MediaPlayer, you should call  {@link #release()},
-     * to free the resources. If not released, too many MediaPlayer instances will
-     * result in an exception.</p>
+     *
+     * <p>You must call {@link #release()} when you are finished using the created instance. Doing
+     * so frees any resources you have previously acquired.
+     *
      * <p>Note that since {@link #prepare()} is called automatically in this method,
      * you cannot change the audio
      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
@@ -955,9 +951,10 @@
     /**
      * Convenience method to create a MediaPlayer for a given resource id.
      * On success, {@link #prepare()} will already have been called and must not be called again.
-     * <p>When done with the MediaPlayer, you should call  {@link #release()},
-     * to free the resources. If not released, too many MediaPlayer instances will
-     * result in an exception.</p>
+     *
+     * <p>You must call {@link #release()} when you are finished using the created instance. Doing
+     * so frees any resources you have previously acquired.
+     *
      * <p>Note that since {@link #prepare()} is called automatically in this method,
      * you cannot change the audio
      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
@@ -2157,21 +2154,8 @@
 
     /**
      * Releases resources associated with this MediaPlayer object.
-     * It is considered good practice to call this method when you're
-     * done using the MediaPlayer. In particular, whenever an Activity
-     * of an application is paused (its onPause() method is called),
-     * or stopped (its onStop() method is called), this method should be
-     * invoked to release the MediaPlayer object, unless the application
-     * has a special need to keep the object around. In addition to
-     * unnecessary resources (such as memory and instances of codecs)
-     * being held, failure to call this method immediately if a
-     * MediaPlayer object is no longer needed may also lead to
-     * continuous battery consumption for mobile devices, and playback
-     * failure for other applications if no multiple instances of the
-     * same codec are supported on a device. Even if multiple instances
-     * of the same codec are supported, some performance degradation
-     * may be expected when unnecessary multiple instances are used
-     * at the same time.
+     *
+     * <p>You must call this method once the instance is no longer required.
      */
     public void release() {
         baseRelease();
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index e432eb6..13c1498 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -1078,7 +1078,8 @@
                 && (types & ROUTE_TYPE_LIVE_AUDIO) != 0
                 && (route.isBluetooth() || route.isDefault())) {
             try {
-                sStatic.mAudioService.setBluetoothA2dpOn(route.isBluetooth());
+                sStatic.mMediaRouterService.setBluetoothA2dpOn(sStatic.mClient,
+                        route.isBluetooth());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error changing Bluetooth A2DP state", e);
             }
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index fa3dc72..6112290 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -504,8 +504,22 @@
         /**
          * Sets target mix role of the mixing rule.
          *
-         * <p>The mix role indicates playback streams will be captured or recording source will be
-         * injected. If not specified, the mix role will be decided automatically when
+         * As each mixing rule is intended to be associated with an {@link AudioMix},
+         * explicitly setting the role of a mixing rule allows this {@link Builder} to
+         * verify validity of the mixing rules to be validated.<br>
+         * The mix role allows distinguishing between:
+         * <ul>
+         * <li>audio framework mixers that will mix / sample-rate convert / reformat the audio
+         *     signal of any audio player (e.g. a {@link android.media.MediaPlayer}) that matches
+         *     the selection rules defined in the object being built. Use
+         *     {@link AudioMixingRule#MIX_ROLE_PLAYERS} for such an {@code AudioMixingRule}</li>
+         * <li>audio framework mixers that will be used as the injection point (after sample-rate
+         *     conversion and reformatting of the audio signal) into any audio recorder (e.g. a
+         *     {@link android.media.AudioRecord}) that matches the selection rule defined in the
+         *     object being built. Use {@link AudioMixingRule#MIX_ROLE_INJECTOR} for such an
+         *     {@code AudioMixingRule}.</li>
+         * </ul>
+         * <p>If not specified, the mix role will be decided automatically when
          * {@link #addRule(AudioAttributes, int)} or {@link #addMixRule(int, Object)} be called.
          *
          * @param mixRole integer value of {@link #MIX_ROLE_PLAYERS} or {@link #MIX_ROLE_INJECTOR}
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.aidl b/media/java/android/media/tv/BroadcastInfoRequest.aidl
new file mode 100644
index 0000000..26cdcc5
--- /dev/null
+++ b/media/java/android/media/tv/BroadcastInfoRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.media.tv;
+
+parcelable BroadcastInfoRequest;
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java
new file mode 100644
index 0000000..6077f04
--- /dev/null
+++ b/media/java/android/media/tv/BroadcastInfoRequest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.media.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import android.annotation.NonNull;
+
+/** @hide */
+public final class BroadcastInfoRequest implements Parcelable {
+    public static final @NonNull Parcelable.Creator<BroadcastInfoRequest> CREATOR =
+            new Parcelable.Creator<BroadcastInfoRequest>() {
+                @Override
+                public BroadcastInfoRequest createFromParcel(Parcel source) {
+                    return new BroadcastInfoRequest(source);
+                }
+
+                @Override
+                public BroadcastInfoRequest[] newArray(int size) {
+                    return new BroadcastInfoRequest[size];
+                }
+            };
+
+    int requestId;
+
+    public BroadcastInfoRequest(int requestId) {
+        this.requestId = requestId;
+    }
+
+    private BroadcastInfoRequest(Parcel source) {
+        requestId = source.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(requestId);
+    }
+}
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.aidl b/media/java/android/media/tv/BroadcastInfoResponse.aidl
new file mode 100644
index 0000000..65e65b2
--- /dev/null
+++ b/media/java/android/media/tv/BroadcastInfoResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.media.tv;
+
+parcelable BroadcastInfoResponse;
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java
new file mode 100644
index 0000000..64c884e
--- /dev/null
+++ b/media/java/android/media/tv/BroadcastInfoResponse.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.media.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import android.annotation.NonNull;
+
+/** @hide */
+public final class BroadcastInfoResponse implements Parcelable {
+    public static final @NonNull Parcelable.Creator<BroadcastInfoResponse> CREATOR =
+            new Parcelable.Creator<BroadcastInfoResponse>() {
+                @Override
+                public BroadcastInfoResponse createFromParcel(Parcel source) {
+                    return new BroadcastInfoResponse(source);
+                }
+
+                @Override
+                public BroadcastInfoResponse[] newArray(int size) {
+                    return new BroadcastInfoResponse[size];
+                }
+            };
+
+    int requestId;
+
+    public BroadcastInfoResponse(int requestId) {
+        this.requestId = requestId;
+    }
+
+    private BroadcastInfoResponse(Parcel source) {
+        requestId = source.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(requestId);
+    }
+}
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 72f8b57..5dad633 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -22,6 +22,7 @@
 import android.media.tv.TvTrackInfo;
 import android.os.Bundle;
 import android.view.InputChannel;
+import android.media.tv.BroadcastInfoResponse;
 
 /**
  * Interface a client of the ITvInputManager implements, to identify itself and receive information
@@ -48,4 +49,7 @@
     void onTuned(int seq, in Uri channelUri);
     void onRecordingStopped(in Uri recordedProgramUri, int seq);
     void onError(int error, int seq);
+
+    // For broadcast info
+    void onBroadcastInfoResponse(in BroadcastInfoResponse response, int seq);
 }
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index d8d1ba13..eaf89ba 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -35,6 +35,7 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.view.Surface;
+import android.media.tv.BroadcastInfoRequest;
 
 /**
  * Interface to the TV input manager service.
@@ -97,6 +98,9 @@
     void pauseRecording(in IBinder sessionToken, in Bundle params, int userId);
     void resumeRecording(in IBinder sessionToken, in Bundle params, int userId);
 
+    // For broadcast info
+    void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId);
+
     // For TV input hardware binding
     List<TvInputHardwareInfo> getHardwareList();
     ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 158cf21..1eab650 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -22,6 +22,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.view.Surface;
+import android.media.tv.BroadcastInfoRequest;
 
 /**
  * Sub-interface of ITvInputService which is created per session and has its own context.
@@ -60,4 +61,7 @@
     void stopRecording();
     void pauseRecording(in Bundle params);
     void resumeRecording(in Bundle params);
+
+    // For broadcast info
+    void requestBroadcastInfo(in BroadcastInfoRequest request);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index af76f90..d857c00 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -20,6 +20,7 @@
 import android.net.Uri;
 import android.media.tv.TvTrackInfo;
 import android.os.Bundle;
+import android.media.tv.BroadcastInfoResponse;
 
 /**
  * Helper interface for ITvInputSession to allow the TV input to notify the system service when a
@@ -45,4 +46,7 @@
     void onTuned(in Uri channelUri);
     void onRecordingStopped(in Uri recordedProgramUri);
     void onError(int error);
+
+    // For broadcast info
+    void onBroadcastInfoResponse(in BroadcastInfoResponse response);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index abccf8d..4257145 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -70,6 +70,7 @@
     private static final int DO_STOP_RECORDING = 21;
     private static final int DO_PAUSE_RECORDING = 22;
     private static final int DO_RESUME_RECORDING = 23;
+    private static final int DO_REQUEST_BROADCAST_INFO = 24;
 
     private final boolean mIsRecordingSession;
     private final HandlerCaller mCaller;
@@ -234,6 +235,10 @@
                 mTvInputRecordingSessionImpl.resumeRecording((Bundle) msg.obj);
                 break;
             }
+            case DO_REQUEST_BROADCAST_INFO: {
+                mTvInputSessionImpl.requestBroadcastInfo((BroadcastInfoRequest) msg.obj);
+                break;
+            }
             default: {
                 Log.w(TAG, "Unhandled message code: " + msg.what);
                 break;
@@ -383,6 +388,11 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RESUME_RECORDING, params));
     }
 
+    @Override
+    public void requestBroadcastInfo(BroadcastInfoRequest request) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REQUEST_BROADCAST_INFO, request));
+    }
+
     private final class TvInputEventReceiver extends InputEventReceiver {
         public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 34e4609..f22b7dc 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -656,6 +656,13 @@
          */
         void onError(Session session, @TvInputManager.RecordingError int error) {
         }
+
+        /**
+         * @param session
+         * @param response
+         */
+        public void onBroadcastInfoResponse(Session session, BroadcastInfoResponse response) {
+        }
     }
 
     private static final class SessionCallbackRecord {
@@ -835,6 +842,15 @@
                 }
             });
         }
+
+        void postBroadcastInfoResponse(final BroadcastInfoResponse response) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onBroadcastInfoResponse(mSession, response);
+                }
+            });
+        }
     }
 
     /**
@@ -1233,6 +1249,18 @@
                     record.postError(error);
                 }
             }
+
+            @Override
+            public void onBroadcastInfoResponse(BroadcastInfoResponse response, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postBroadcastInfoResponse(response);
+                }
+            }
         };
         ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
             @Override
@@ -2839,6 +2867,19 @@
             }
         }
 
+        public void requestBroadcastInfo(BroadcastInfoRequest request) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.requestBroadcastInfo(mToken, request, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+
+        }
+
         private final class InputEventHandler extends Handler {
             public static final int MSG_SEND_INPUT_EVENT = 1;
             public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 77fb2b2..f312f64 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -769,6 +769,29 @@
             });
         }
 
+        /**
+         * Notifies response for broadcast info.
+         *
+         * @param response
+         * @hide
+         */
+        public void notifyBroadcastInfoResponse(@NonNull final BroadcastInfoResponse response) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifyBroadcastInfoResponse");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onBroadcastInfoResponse(response);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyBroadcastInfoResponse", e);
+                    }
+                }
+            });
+        }
+
         private void notifyTimeShiftStartPositionChanged(final long timeMs) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
@@ -917,6 +940,13 @@
         public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
 
         /**
+         * @param request broadcast info request
+         * @hide
+         */
+        public void onRequestBroadcastInfo(@NonNull BroadcastInfoRequest request) {
+        }
+
+        /**
          * Tunes to a given channel.
          *
          * <p>No video will be displayed until {@link #notifyVideoAvailable()} is called.
@@ -1506,6 +1536,10 @@
             }
         }
 
+        void requestBroadcastInfo(BroadcastInfoRequest request) {
+            onRequestBroadcastInfo(request);
+        }
+
         /**
          * Takes care of dispatching incoming input events and tells whether the event was handled.
          */
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
new file mode 100644
index 0000000..4197934
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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 android.media.tv.interactive;
+
+/**
+ * Interface a client of the ITvIAppManager implements, to identify itself and receive information
+ * about changes to the state of each TV interactive application service.
+ * @hide
+ */
+oneway interface ITvIAppClient {
+    void onSessionCreated(in String iAppServiceId, IBinder token, int seq);
+    void onSessionReleased(int seq);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index ef7b1cd..a435e20 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -16,10 +16,15 @@
 
 package android.media.tv.interactive;
 
+import android.media.tv.interactive.ITvIAppClient;
+
 /**
  * Interface to the TV interactive app service.
  * @hide
  */
 interface ITvIAppManager {
     void startIApp(in IBinder sessionToken, int userId);
+    void createSession(
+            in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
+    void releaseSession(in IBinder sessionToken, int userId);
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
new file mode 100644
index 0000000..c4f82eb
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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 android.media.tv.interactive;
+
+import android.media.tv.interactive.ITvIAppSessionCallback;
+
+/**
+ * Top-level interface to a TV IApp component (implemented in a Service). It's used for
+ * TvIAppManagerService to communicate with TvIAppService.
+ * @hide
+ */
+oneway interface ITvIAppService {
+    void createSession(in ITvIAppSessionCallback callback, in String iAppServiceId, int type);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl
new file mode 100644
index 0000000..8d49bc2
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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 android.media.tv.interactive;
+
+/**
+ * Helper interface for ITvIAppService to allow the TvIAppService to notify the
+ * TvIAppManagerService.
+ * @hide
+ */
+oneway interface ITvIAppServiceCallback {
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
index 970afc6..0cbdc8e 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
@@ -17,9 +17,10 @@
 package android.media.tv.interactive;
 
 /**
- * Sub-interface of ITvIAppService which is created per session and has its own context.
+ * Sub-interface of ITvIAppService.aidl which is created per session and has its own context.
  * @hide
  */
 oneway interface ITvIAppSession {
     void startIApp();
+    void release();
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
new file mode 100644
index 0000000..b3b317e
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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 android.media.tv.interactive;
+
+import android.media.tv.interactive.ITvIAppSession;
+
+/**
+ * Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when
+ * there is a related event.
+ * @hide
+ */
+oneway interface ITvIAppSessionCallback {
+    void onSessionCreated(in ITvIAppSession session);
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index 8f01f79..8b8c8c9 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -16,10 +16,16 @@
 
 package android.media.tv.interactive;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemService;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.Preconditions;
 
 /**
  * Central system API to the overall TV interactive application framework (TIAF) architecture, which
@@ -33,23 +39,108 @@
     private final ITvIAppManager mService;
     private final int mUserId;
 
+    // A mapping from the sequence number of a session to its SessionCallbackRecord.
+    private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
+            new SparseArray<>();
+
+    // A sequence number for the next session to be created. Should be protected by a lock
+    // {@code mSessionCallbackRecordMap}.
+    private int mNextSeq;
+
+    private final ITvIAppClient mClient;
+
     public TvIAppManager(ITvIAppManager service, int userId) {
         mService = service;
         mUserId = userId;
+        mClient = new ITvIAppClient.Stub() {
+            @Override
+            public void onSessionCreated(String iAppServiceId, IBinder token, int seq) {
+                // TODO: use InputChannel for input events
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for " + token);
+                        return;
+                    }
+                    Session session = null;
+                    if (token != null) {
+                        session = new Session(token, mService, mUserId, seq,
+                                mSessionCallbackRecordMap);
+                    } else {
+                        mSessionCallbackRecordMap.delete(seq);
+                    }
+                    record.postSessionCreated(session);
+                }
+            }
+
+            @Override
+            public void onSessionReleased(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    mSessionCallbackRecordMap.delete(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq:" + seq);
+                        return;
+                    }
+                    record.mSession.releaseInternal();
+                    record.postSessionReleased();
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates a {@link Session} for a given TV interactive application.
+     *
+     * <p>The number of sessions that can be created at the same time is limited by the capability
+     * of the given interactive application.
+     *
+     * @param iAppServiceId The ID of the interactive application.
+     * @param type the type of the interactive application.
+     * @param callback A callback used to receive the created session.
+     * @param handler A {@link Handler} that the session creation will be delivered to.
+     * @hide
+     */
+    public void createSession(@NonNull String iAppServiceId, int type,
+            @NonNull final SessionCallback callback, @NonNull Handler handler) {
+        createSessionInternal(iAppServiceId, type, callback, handler);
+    }
+
+    private void createSessionInternal(String iAppServiceId, int type, SessionCallback callback,
+            Handler handler) {
+        Preconditions.checkNotNull(iAppServiceId);
+        Preconditions.checkNotNull(callback);
+        Preconditions.checkNotNull(handler);
+        SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
+        synchronized (mSessionCallbackRecordMap) {
+            int seq = mNextSeq++;
+            mSessionCallbackRecordMap.put(seq, record);
+            try {
+                mService.createSession(mClient, iAppServiceId, type, seq, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 
     /**
      * The Session provides the per-session functionality of interactive app.
      */
     public static final class Session {
-        private final IBinder mToken;
         private final ITvIAppManager mService;
         private final int mUserId;
+        private final int mSeq;
+        private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
 
-        private Session(IBinder token, ITvIAppManager service, int userId) {
+        private IBinder mToken;
+
+        private Session(IBinder token, ITvIAppManager service, int userId, int seq,
+                SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
             mToken = token;
             mService = service;
             mUserId = userId;
+            mSeq = seq;
+            mSessionCallbackRecordMap = sessionCallbackRecordMap;
         }
 
         void startIApp() {
@@ -63,5 +154,83 @@
                 throw e.rethrowFromSystemServer();
             }
         }
+
+        /**
+         * Releases this session.
+         */
+        public void release() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.releaseSession(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+
+            releaseInternal();
+        }
+
+        private void releaseInternal() {
+            mToken = null;
+            synchronized (mSessionCallbackRecordMap) {
+                mSessionCallbackRecordMap.delete(mSeq);
+            }
+        }
+    }
+
+    private static final class SessionCallbackRecord {
+        private final SessionCallback mSessionCallback;
+        private final Handler mHandler;
+        private Session mSession;
+
+        SessionCallbackRecord(SessionCallback sessionCallback, Handler handler) {
+            mSessionCallback = sessionCallback;
+            mHandler = handler;
+        }
+
+        void postSessionCreated(final Session session) {
+            mSession = session;
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSessionCreated(session);
+                }
+            });
+        }
+
+        void postSessionReleased() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSessionReleased(mSession);
+                }
+            });
+        }
+    }
+
+    /**
+     * Interface used to receive the created session.
+     * @hide
+     */
+    public abstract static class SessionCallback {
+        /**
+         * This is called after {@link TvIAppManager#createSession} has been processed.
+         *
+         * @param session A {@link TvIAppManager.Session} instance created. This can be {@code null}
+         *                if the creation request failed.
+         */
+        public void onSessionCreated(@Nullable Session session) {
+        }
+
+        /**
+         * This is called when {@link TvIAppManager.Session} is released.
+         * This typically happens when the process hosting the session has crashed or been killed.
+         *
+         * @param session the {@link TvIAppManager.Session} instance released.
+         */
+        public void onSessionReleased(@NonNull Session session) {
+        }
     }
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index 2a851f39..f363728 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -16,9 +16,23 @@
 
 package android.media.tv.interactive;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
 import android.view.KeyEvent;
 
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * The TvIAppService class represents a TV interactive applications RTE.
  * @hide
@@ -27,19 +41,87 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "TvIAppService";
 
+    private final Handler mServiceHandler = new ServiceHandler();
+
+    /**
+     * This is the interface name that a service implementing an environment to run Tv IApp should
+     * say that it support -- that is, this is the action it uses for its intent filter. To be
+     * supported, the service must also require the BIND_TV_IAPP permission so that other
+     * applications cannot abuse it.
+     */
+    public static final String SERVICE_INTERFACE = "android.media.tv.TvIAppService";
+
+    @Override
+    public final IBinder onBind(Intent intent) {
+        ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() {
+
+            @Override
+            public void createSession(ITvIAppSessionCallback cb, String iAppServiceId, int type) {
+                if (cb == null) {
+                    return;
+                }
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = cb;
+                args.arg2 = iAppServiceId;
+                args.arg3 = type;
+                mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args)
+                        .sendToTarget();
+            }
+        };
+        return tvIAppServiceBinder;
+    }
+
+
+    /**
+     * Returns a concrete implementation of {@link Session}.
+     *
+     * <p>May return {@code null} if this TV IApp service fails to create a session for some
+     * reason.
+     *
+     * @param iAppServiceId The ID of the TV IApp associated with the session.
+     * @param type The type of the TV IApp associated with the session.
+     */
+    @Nullable
+    public abstract Session onCreateSession(@NonNull String iAppServiceId, int type);
+
     /**
      * Base class for derived classes to implement to provide a TV interactive app session.
      */
     public abstract static class Session implements KeyEvent.Callback {
+        private final Object mLock = new Object();
+        // @GuardedBy("mLock")
+        private ITvIAppSessionCallback mSessionCallback;
+        // @GuardedBy("mLock")
+        private final List<Runnable> mPendingActions = new ArrayList<>();
+
         /**
          * Starts TvIAppService session.
          */
         public void onStartIApp() {
         }
 
+        /**
+         * Releases TvIAppService session.
+         */
+        public void onRelease() {
+        }
+
         void startIApp() {
             onStartIApp();
         }
+        void release() {
+            onRelease();
+        }
+
+        private void initialize(ITvIAppSessionCallback callback) {
+            synchronized (mLock) {
+                mSessionCallback = callback;
+                for (Runnable runnable : mPendingActions) {
+                    runnable.run();
+                }
+                mPendingActions.clear();
+            }
+        }
     }
 
     /**
@@ -56,5 +138,69 @@
         public void startIApp() {
             mSessionImpl.startIApp();
         }
+
+        @Override
+        public void release() {
+            mSessionImpl.release();
+        }
+    }
+
+    @SuppressLint("HandlerLeak")
+    private final class ServiceHandler extends Handler {
+        private static final int DO_CREATE_SESSION = 1;
+        private static final int DO_NOTIFY_SESSION_CREATED = 2;
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DO_CREATE_SESSION: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg1;
+                    String iAppServiceId = (String) args.arg2;
+                    int type = (int) args.arg3;
+                    args.recycle();
+                    Session sessionImpl = onCreateSession(iAppServiceId, type);
+                    if (sessionImpl == null) {
+                        try {
+                            // Failed to create a session.
+                            cb.onSessionCreated(null);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "error in onSessionCreated", e);
+                        }
+                        return;
+                    }
+                    ITvIAppSession stub = new ITvIAppSessionWrapper(sessionImpl);
+
+                    SomeArgs someArgs = SomeArgs.obtain();
+                    someArgs.arg1 = sessionImpl;
+                    someArgs.arg2 = stub;
+                    someArgs.arg3 = cb;
+                    mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED,
+                            someArgs).sendToTarget();
+                    return;
+                }
+                case DO_NOTIFY_SESSION_CREATED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Session sessionImpl = (Session) args.arg1;
+                    ITvIAppSession stub = (ITvIAppSession) args.arg2;
+                    ITvIAppSessionCallback cb = (ITvIAppSessionCallback) args.arg3;
+                    try {
+                        cb.onSessionCreated(stub);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "error in onSessionCreated", e);
+                    }
+                    if (sessionImpl != null) {
+                        sessionImpl.initialize(cb);
+                    }
+                    args.recycle();
+                    return;
+                }
+                default: {
+                    Log.w(TAG, "Unhandled message code: " + msg.what);
+                    return;
+                }
+            }
+        }
+
     }
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index 9af9dae..f56ea0a 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -17,6 +17,9 @@
 package android.media.tv.interactive;
 
 import android.content.Context;
+import android.media.tv.interactive.TvIAppManager.Session;
+import android.media.tv.interactive.TvIAppManager.SessionCallback;
+import android.os.Handler;
 import android.util.Log;
 import android.view.ViewGroup;
 
@@ -28,11 +31,14 @@
     private static final String TAG = "TvIAppView";
     private static final boolean DEBUG = false;
 
-    // TODO: create session
-    private TvIAppManager.Session mSession;
+    private final TvIAppManager mTvIAppManager;
+    private final Handler mHandler = new Handler();
+    private Session mSession;
+    private MySessionCallback mSessionCallback;
 
     public TvIAppView(Context context) {
         super(context, /* attrs = */null, /* defStyleAttr = */0);
+        mTvIAppManager = (TvIAppManager) getContext().getSystemService("tv_interactive_app");
     }
 
     @Override
@@ -44,14 +50,74 @@
     }
 
     /**
+     * Prepares the interactive application.
+     */
+    public void prepareIApp(String iAppServiceId, int type) {
+        // TODO: document and handle the cases that this method is called multiple times.
+        if (DEBUG) {
+            Log.d(TAG, "prepareIApp");
+        }
+        mSessionCallback = new MySessionCallback(iAppServiceId, type);
+        if (mTvIAppManager != null) {
+            mTvIAppManager.createSession(iAppServiceId, type, mSessionCallback, mHandler);
+        }
+    }
+
+    /**
      * Starts the interactive application.
      */
     public void startIApp() {
         if (DEBUG) {
-            Log.d(TAG, "start");
+            Log.d(TAG, "startIApp");
         }
         if (mSession != null) {
             mSession.startIApp();
         }
     }
+
+    private class MySessionCallback extends SessionCallback {
+        final String mIAppServiceId;
+        int mType;
+
+        MySessionCallback(String iAppServiceId, int type) {
+            mIAppServiceId = iAppServiceId;
+            mType = type;
+        }
+
+        @Override
+        public void onSessionCreated(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionCreated()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSessionCreated - session already created");
+                // This callback is obsolete.
+                if (session != null) {
+                    session.release();
+                }
+                return;
+            }
+            mSession = session;
+            if (session != null) {
+                // TODO: handle SurfaceView and InputChannel.
+            } else {
+                // Failed to create
+                // Todo: forward error to Tv App
+                mSessionCallback = null;
+            }
+        }
+
+        @Override
+        public void onSessionReleased(Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionReleased()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSessionReleased - session not created");
+                return;
+            }
+            mSessionCallback = null;
+            mSession = null;
+        }
+    }
 }
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 51e954f..f3fb48f 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-oudio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD oudio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Gehoortoestelle"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Gekoppel aan gehoortoestelle"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Gekoppel aan LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Gekoppel aan media-oudio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Gekoppel aan foonoudio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Gekoppel aan lêeroordragbediener"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gebruik vir lêeroordrag"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gebruik vir invoer"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gebruik vir gehoortoestelle"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Gebruik vir LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Bind saam"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"BIND SAAM"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Kanselleer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b0ce875..647d052 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"ኤችዲ ኦዲዮ፦ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"ኤችዲ ኦዲዮ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"አጋዥ መስሚያዎች"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ከአጋዥ መስሚያዎች ጋር ተገናኝቷል"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"ከLE_AUDIO ጋር ተገናኝቷል"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ወደ ማህደረ  መረጃ  አውዲዮ ተያይዟል"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ወደ ስልክ አውዲዮ ተያይዟል"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ወደ ፋይል ዝውውር አገልጋይ ተያይዟል"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ለፋይል ዝውውር ተጠቀም"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ለውፅአት ተጠቀም"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ለአጋዥ መስሚያዎች ይጠቀሙ"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"ለLE_AUDIO ይጠቀሙ"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"አጣምር"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"አጣምር"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ይቅር"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index ba6bd70..662ffdd 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"صوت عالي الدقة: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"صوت عالي الدقة"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"سماعات الأذن الطبية"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"تمّ التوصيل بسماعات الأذن الطبية"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"‏متصل بـ LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"متصل بالإعدادات الصوتية للوسائط"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"متصل بالإعدادات الصوتية للهاتف"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"متصل بخادم نقل الملف"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"استخدامه لنقل الملفات"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"استخدام للإدخال"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"استخدام سماعات الأذن الطبية"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"‏الاستخدام لـ LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"إقران"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"إقران"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"إلغاء"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index d0fb2d4..dfe708d 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -100,14 +100,14 @@
     <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"সংযোগ কৰা হ’ল (মিডিয়া নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"সংযোগ কৰা হ’ল (বাৰ্তাত প্ৰৱেশাধিকাৰ নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"সংযোগ কৰা হ’ল (কোনো ফ\'ন বা মিডিয়া নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"সংযোগ কৰা হ’ল, বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"সংযোগ কৰা হ’ল (ফ\'ন নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"সংযোগ কৰা হ’ল (মিডিয়া নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"সংযোগ কৰা হ’ল (কোনো ফ\'ন বা মিডিয়া নাই), বেটাৰিৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"সক্ৰিয়, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰি"</string>
-    <string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"সক্ৰিয়, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> বেটাৰি, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> বেটাৰি"</string>
+    <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"সংযোগ কৰা হ’ল, বেটাৰীৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"সংযোগ কৰা হ’ল (ফ\'ন নাই), বেটাৰীৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"সংযোগ কৰা হ’ল (মিডিয়া নাই), বেটাৰীৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"সংযোগ কৰা হ’ল (কোনো ফ\'ন বা মিডিয়া নাই), বেটাৰীৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"সক্ৰিয়, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰী"</string>
+    <string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"সক্ৰিয়, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> বেটাৰী, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> বেটাৰী"</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰী"</string>
-    <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> বেটাৰি, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> বেটাৰি"</string>
+    <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> বেটাৰী, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> বেটাৰী"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"সক্ৰিয়"</string>
     <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"মিডিয়াৰ অডিঅ’"</string>
     <string name="bluetooth_profile_headset" msgid="5395952236133499331">"ফ\'ন কলসমূহ"</string>
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"এইচ্ছডি অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"এইচ্ছডি অডিঅ’"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"শ্ৰৱণ যন্ত্ৰ"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"শ্ৰৱণ যন্ত্ৰলৈ সংযোগ কৰা হৈছে"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIOৰ সৈতে সংযোগ কৰক"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"মিডিয়া অডিঅ’লৈ সংযোগ হৈছে"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ফ’ন অডিঅ\'ৰ লগত সংযোগ কৰা হ’ল"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ফাইল ট্ৰান্সফাৰ ছাৰ্ভাৰৰ সৈতে সংযোজিত হৈ আছে"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ফাইল স্থানান্তৰ কৰিবলৈ ব্যৱহাৰ কৰক"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ইনপুটৰ বাবে ব্যৱহাৰ কৰক"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"শ্ৰৱণ যন্ত্ৰৰ বাবে ব্যৱহাৰ কৰক"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIOৰ বাবে ব্যৱহাৰ কৰক"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"যোৰা লগাওক"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"যোৰা লগাওক"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"বাতিল কৰক"</string>
@@ -157,8 +160,8 @@
     <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ব্লুটুথ"</string>
     <string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"বাওঁফালৰ শ্ৰৱণ যন্ত্ৰটো যোৰ পতোৱা হৈছে…"</string>
     <string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"সোঁফালৰ শ্ৰৱণ যন্ত্ৰটো যোৰ পতোৱা হৈছে…"</string>
-    <string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰি বাকী আছে"</string>
-    <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"সোঁ - বেটাৰি <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> বেটাৰী বাকী আছে"</string>
+    <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"সোঁ - বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="accessibility_wifi_off" msgid="1195445715254137155">"ৱাই-ফাই অফ হৈ আছে।"</string>
     <string name="accessibility_no_wifi" msgid="5297119459491085771">"ৱাইফাই সংযোগ বিচ্ছিন্ন হৈ আছে।"</string>
     <string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"ৱাই-ফাই এদাল দণ্ড।"</string>
@@ -316,7 +319,7 @@
     <string name="wifi_unmetered_label" msgid="6174142840934095093">"নিৰিখ অনিৰ্দিষ্ট"</string>
     <string name="select_logd_size_title" msgid="1604578195914595173">"লগাৰৰ বাফাৰৰ আকাৰ"</string>
     <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"প্ৰতিটো লগ বাফাৰত ল\'গাৰৰ আকাৰ বাছনি কৰক"</string>
-    <string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"লগাৰৰ স্থায়ী সঞ্চয়াগাৰৰ বস্তুবোৰ মচিবনে?"</string>
+    <string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"লগাৰৰ স্থায়ী ষ্ট’ৰেজৰ বস্তুবোৰ মচিবনে?"</string>
     <string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"পাৰ্ছিছটেণ্ট লগাৰ ব্যৱহাৰ কৰ নিৰীক্ষণ নকৰাৰ সময়ত, আমি আপোনাৰ ডিভাইচত থকা লগাৰ ডেটা নিৱাসীক মচা দৰকাৰ।"</string>
     <string name="select_logpersist_title" msgid="447071974007104196">"ডিভাইচটোত লগাৰৰ ডেটা নিৰবচ্ছিন্নভাৱে সঞ্চয় কৰক"</string>
     <string name="select_logpersist_dialog_title" msgid="7745193591195485594">"ডিভাইচত স্থায়ীভাৱে সঞ্চয় কৰিবলৈ লগ বাফাৰবোৰ বাছনি কৰক"</string>
@@ -402,7 +405,7 @@
     <string name="show_notification_channel_warnings" msgid="3448282400127597331">"জাননী চ্চেনেলৰ সকীয়নিসমূহ দেখুৱাওক"</string>
     <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"কোনো এপে বৈধ চ্চেনেল নোহোৱাকৈ কোনো জাননী প\'ষ্ট কৰিলে স্ক্ৰীনত সকীয়নি প্ৰদৰ্শন হয়"</string>
     <string name="force_allow_on_external" msgid="9187902444231637880">"বাহ্যিক সঞ্চয়াগাৰত এপক বলেৰে অনুমতি দিয়ক"</string>
-    <string name="force_allow_on_external_summary" msgid="8525425782530728238">"মেনিফেষ্টৰ মান যিয়েই নহওক, বাহ্যিক সঞ্চয়াগাৰত লিখিবলৈ যিকোনো এপক উপযুক্ত কৰি তোলে"</string>
+    <string name="force_allow_on_external_summary" msgid="8525425782530728238">"মেনিফেষ্টৰ মান যিয়েই নহওক, বাহ্যিক ষ্ট’ৰেজত লিখিবলৈ যিকোনো এপক উপযুক্ত কৰি তোলে"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"বলেৰে কাৰ্যকলাপসমূহৰ আকাৰ সলনি কৰিব পৰা কৰক"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"মেনিফেষ্টৰ মান যিয়েই নহওক, মাল্টি-ৱিণ্ডৰ বাবে সকলো কাৰ্যকলাপৰ আকাৰ সলনি কৰিব পৰা কৰক।"</string>
     <string name="enable_freeform_support" msgid="7599125687603914253">"ফ্ৰিফৰ্ম ৱিণ্ড\'জ সক্ষম কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index d6c4c88..335e982e 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Eşitmə cihazları"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Eşitmə Aparatlarına qoşuldu"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Media audioya birləşdirilib"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Telefon audiosuna qoşulu"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Fayl transfer serverinə qoşulu"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Fayl transferi üçün istifadə edin"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Daxiletmə üçün istifadə edin"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Eşitmə Aparatları üçün istifadə edin"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Qoşulsun"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"QOŞULSUN"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Ləğv edin"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index da65227..5af3006 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD zvuk"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Slušni aparati"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Povezano sa slušnim aparatima"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Povezano sa LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezano sa zvukom medija"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezano sa zvukom telefona"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Povezano sa serverom za prenos datoteka"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Korišćenje za prenos datoteka"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Koristi za ulaz"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Koristi za slušne aparate"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Koristite za LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Upari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"UPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Otkaži"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 3126f52..d1b6226 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Аўдыя ў HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Аўдыя ў HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слыхавыя апараты"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Падключана да слыхавых апаратаў"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Падключана да LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Падключана да аўдыё медыа"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Падключана да аўдыё тэлефона"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Падключаны да серверу перадачы файлаў"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Выкарыстоўваць для перадачы файлаў"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Выкарыстоўваць для ўводу"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Выкарыстоўваць для слыхавых апаратаў"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Выкарыстоўваць для LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Спалучыць"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"СПАЛУЧЫЦЬ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Скасаваць"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 5a57d44..375879a 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Висококачествено аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Висококачествено аудио"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слухови апарати"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Установена е връзка със слухов апарат"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Свързано с(ъс) LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Установена е връзка с медийно аудио"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Връзка със звука на телефона"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Установена е връзка със сървър за трансфер на файлове"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Използване на за пренос на файлове"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Да се използва за въвеждане"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Използване за слухови апарати"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Използване за LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Сдвояване"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"СДВОЯВАНЕ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Отказ"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index bac2f36..2e614fa 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD অডিও: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD অডিও"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"হিয়ারিং এড"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"হিয়ারিং এডের সাথে কানেক্ট করা হয়েছে"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"মিডিয়া অডিওতে কানেক্ট রয়েছে"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ফোন অডিওতে কানেক্ট"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ফাইল স্থানান্তর সার্ভারের সঙ্গে কানেক্ট"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ফাইল স্থানান্তরের জন্য ব্যবহার করুন"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ইনপুটের জন্য ব্যবহার করুন"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"হিয়ারিং এডের জন্য ব্যবহার করুন"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"যুক্ত করুন"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"যুক্ত করুন"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"বাতিল করুন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 483d3b6..e33978f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Slušni aparati"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Povezan na slušne aparate"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Povezano sa: LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezano sa zvukom medija"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezano na zvuk telefona"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Povezano sa serverom za prijenos podataka"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Koristi za prijenos fajlova"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Koristi kao ulaz"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Korištenje za slušne aparate"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Koristi za: LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Upari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"UPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Otkaži"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 1cffe78..2b10a30 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Àudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Àudio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audiòfons"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"S\'ha connectat als audiòfons"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Connectat a LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connectat a l\'àudio del mitjà"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connectat a àudio del telèfon"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Connectat al servidor de transferència de fitxers"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utilitza per a la transferència de fitxers"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilitza per a entrada"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utilitza per als audiòfons"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Utilitza per a LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Vincula"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"VINCULA"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancel·la"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index dd41dbc..7a94bb4 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD zvuk"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Naslouchátka"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Připojeno k naslouchátkům"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Připojeno k LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Připojeno ke zvukovému médiu"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Připojeno k náhlavní soupravě"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Připojeno k serveru pro přenos dat"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Použít pro přenos souborů"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Použít pro vstup"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Použít pro naslouchátka"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Používat pro LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Spárovat"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SPÁROVAT"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Zrušit"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index dde780a..ff5c6ae 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-lyd"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Høreapparater"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Forbundet til høreapparater"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Forbundet med LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Forbundet til medielyd"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Forbundet til telefonlyd"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Forbundet til filoverførselsserver"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Brug til filoverførsel"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Brug til input"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Brug til høreapparater"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Brug med LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Par"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ACCEPTÉR PARRING"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annuller"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 520ac98..a75d19b 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-Audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hörhilfen"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Mit Hörhilfen verbunden"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Verbunden mit Medien-Audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Verbunden mit Telefon-Audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Mit Dateiübertragungsserver verbunden"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Für Dateiübertragung verwenden"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Für Eingabe verwenden"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Für Hörhilfen verwenden"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Koppeln"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"KOPPELN"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Abbrechen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index d95f5b5..402e489 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Ήχος HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Ήχος HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Βοηθήματα ακοής"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Έγινε σύνδεση σε βοηθήματα ακοής"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Συνδέθηκε σε LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Συνδέθηκε σε ήχο πολυμέσων"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Συνδεδεμένο στον ήχο τηλεφώνου"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Συνδεδεμένο σε διακομιστή μεταφοράς αρχείων"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Χρήση για τη μεταφορά αρχείων"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Χρήση για είσοδο"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Χρήση για βοηθήματα ακοής"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Χρήση για LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Σύζευξη"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ΣΥΖΕΥΞΗ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Ακύρωση"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 1273a19..fb3bc7a 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Connected to LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Connected to file-transfer server"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancel"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 70fdaac..e33af37 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Connected to LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Connected to file-transfer server"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancel"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 1273a19..fb3bc7a 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Connected to LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Connected to file-transfer server"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancel"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 1273a19..fb3bc7a 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Connected to LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Connected to file-transfer server"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancel"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 56267bb..2c58236 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎HD audio: ‎‏‎‎‏‏‎<xliff:g id="CODEC_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎HD audio‎‏‎‎‏‎"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‎Hearing Aids‎‏‎‎‏‎"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎LE_AUDIO‎‏‎‎‏‎"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎Connected to Hearing Aids‎‏‎‎‏‎"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎Connected to LE_AUDIO‎‏‎‎‏‎"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎‏‎Connected to media audio‎‏‎‎‏‎"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎Connected to phone audio‎‏‎‎‏‎"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎Connected to file transfer server‎‏‎‎‏‎"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎Use for file transfer‎‏‎‎‏‎"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎Use for input‎‏‎‎‏‎"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‎Use for Hearing Aids‎‏‎‎‏‎"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎Use for LE_AUDIO‎‏‎‎‏‎"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‎Pair‎‏‎‎‏‎"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎PAIR‎‏‎‎‏‎"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‎Cancel‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 30294ce..471e715 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio en HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audífonos"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a audífonos"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Conectado a LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado al audio multimedia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado al audio del dispositivo"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Conectado al servidor de transferencia de archivo"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utilizar para la transferencia de archivos"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizar para entrada"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar para audífonos"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar para LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Vincular"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SINCRONIZAR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index fbdb37a..f28d062 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audífonos"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a audífonos"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Conectado a LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado al audio del medio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado al audio del teléfono"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Conectado con el servidor de transferencia de archivos"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Uso de la transferencia de archivos"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Usar para entrada"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar con audífonos"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar en LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Emparejar"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"EMPAREJAR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index ca83bf8..aaf3201 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-heli: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-heli"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Kuuldeaparaadid"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Kuuldeaparaatidega ühendatud"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Ühendatud üksusega LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Ühendatud meediumiheliga"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Ühendatud telefoniheliga"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Ühendatud failiedastuse serveriga"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Kasutage failide edastamiseks"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Kasutage sisendi jaoks"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Kasuta kuulmisaparaatide puhul"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Üksuse LE_AUDIO kasutamine"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Seo"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SEO"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Tühista"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 915db9c..0f929de 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Kalitate handiko audioa: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Kalitate handiko audioa"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audifonoak"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Audifonoetara konektatuta"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Konektatu LE_AUDIO-ra"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Euskarriaren audiora konektatuta"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Telefonoaren audiora konektatuta"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Fitxategi-transferentziako zerbitzarira konektatuta"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Erabili fitxategi-transferentziarako"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Erabili idazketarako"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Erabili audifonoak"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Erabili LE_AUDIO-rako"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parekatu"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAREKATU"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Utzi"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 6ceea96..a5a11eb 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"‏صدای HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"‏صدای HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"سمعک‌ها"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"به سمعک متصل شد"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"‏متصل به LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"به رسانه صوتی متصل شد"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"به تلفن صوتی متصل شد"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"به سرور انتقال فایل متصل شد"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"استفاده برای انتقال فایل"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"استفاده برای چاپ"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"استفاده برای سمعک‌ها"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"‏استفاده برای LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"مرتبط‌سازی"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"مرتبط‌سازی"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"لغو"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 57780e1..373f6a4 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-ääni: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-ääni"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Kuulolaitteet"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Yhdistetty kuulolaitteisiin"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO yhdistetty"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Yhdistetty median ääneen"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Yhdistetty puhelimen ääneen"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Yhdistetty tiedostonsiirtopalvelimeen"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Käytä tiedostojen siirtoon"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Käytä syöttöön"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Käytä kuulolaitteilla"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Käyttö: LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Muodosta laitepari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"MUODOSTA LAITEPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Peru"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index dbd3f71..b00ea16 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD : <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Prothèses auditives"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connecté aux prothèses auditives"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connecté aux paramètres audio du média"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connecté à l\'audio du téléphone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Connexion au serveur de transfert de fichiers"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utiliser pour le transfert de fichiers"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utiliser comme entrée"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utiliser avec les prothèses auditives"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Associer"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ASSOCIER"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annuler"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index ce00a30..5f635b0 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD : <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Appareils auditifs"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connexion établie avec les appareils auditifs"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Connecté à LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connecté aux paramètres audio du média"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connecté aux paramètres audio du téléphone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Connexion au serveur de transfert de fichiers"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utiliser pour le transfert de fichiers"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utiliser comme entrée"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utiliser pour les appareils auditifs"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Utiliser pour LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Associer"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ASSOCIER"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annuler"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 7de607f..109cdc6 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio en HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audiófonos"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a audiófonos"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado ao audio multimedia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado ao audio do teléfono"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Conectado ao servidor de transferencia de ficheiros"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utilízase para a transferencia de ficheiros"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilízase para a entrada"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utilizar para audiófonos"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Vincular"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"VINCULAR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index f8b1123..5bb68b7 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ઑડિયો: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ઑડિયો"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"શ્રવણ યંત્રો"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"શ્રવણ યંત્રો સાથે કનેક્ટ કરેલું છે"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"મીડિયા ઑડિઓ સાથે કનેક્ટ કર્યુ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ફોન ઑડિઓ સાથે કનેક્ટ થયાં"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ફાઇલ સ્થાનાંતરણ સેવાથી કનેક્ટ થયાં"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ફાઇલ સ્થાનાંતર માટે ઉપયોગ કરો"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ઇનપુટ માટે ઉપયોગ કરો"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"શ્રવણ યંત્રો માટે ઉપયોગ કરો"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"જોડી"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"જોડી કરો"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"રદ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 0c115ca..fcf04bc 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"एचडी ऑडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"एचडी ऑडियो"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"कान की मशीन"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"सुनने में मदद करने वाले डिवाइस से कनेक्ट है"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO से कनेक्ट किया गया"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मीडिया ऑडियो से कनेक्‍ट किया गया"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फ़ोन ऑडियो से कनेक्‍ट किया गया"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फ़ाइल स्‍थानांतरण सर्वर से कनेक्‍ट किया गया"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फ़ाइल स्‍थानांतरण के लिए उपयोग करें"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुट के लिए उपयोग करें"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"सुनने में मदद करने वाले डिवाइस के लिए इस्तेमाल करें"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO के लिए इस्तेमाल करें"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"जोड़ें"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"जोड़ें"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"रद्द करें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f671e8c..1ef94fb 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Slušni aparati"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Povezano sa Slušnim aparatima"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Povezano s profilom LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezano s medijskim zvukom"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezano sa telefonskim zvukom"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Povezano s poslužiteljem za prijenos datoteka"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Koristi za prijenos datoteke"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Upotrijebi za ulaz"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Upotrijebi za Slušne aparate"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Upotrebljavajte za LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Upari"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"UPARI"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Odustani"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 3a703a9..deb0f81 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hallókészülékek"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Hallókészülékhez csatlakoztatva"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Csatlakoztatva ehhez: LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Csatlakoztatva az eszköz hangjához"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Csatlakoztatva a telefon hangjához"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Csatlakozva a fájlküldő szerverhez"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Felhasználás fájlátvitelre"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Használat beviteli eszközként"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Hallókészülékkel való használat"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Használat ehhez: LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Párosítás"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PÁROSÍTÁS"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Mégse"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 10f05b0..485af54 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD աուդիո՝ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD աուդիո"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Լսողական ապարատ"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Լսողական ապարատը միացված է"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Միացած է LE_AUDIO-ին"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Միացված է մեդիա աուդիոյին"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Միացված է հեռախոսի ձայնային տվյալներին"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Միացված է ֆայլերի փոխանցման սերվերին"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Օգտագործել ֆայլի փոխանցման համար"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Օգտագործել ներմուծման համար"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Օգտագործել լսողական ապարատի համար"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Օգտագործել LE_AUDIO-ի համար"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Զուգակցել"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"Զուգակցել"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Չեղարկել"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 9eacc6c..10f57df 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Alat Bantu Dengar"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Terhubung ke Alat Bantu Dengar"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Terhubung ke LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Terhubung ke media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Terhubung ke audio ponsel"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Sambungkan ke server transfer file"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gunakan untuk transfer file"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gunakan untuk masukan"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gunakan untuk Alat Bantu Dengar"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Digunakan untuk LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Sambungkan"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SAMBUNGKAN"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Batal"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index d67b872..a43eb14 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-hljóð: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-hljóð"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Heyrnartæki"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Tengt við heyrnartæki"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Tengt við LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Tengt við hljóðspilun efnis"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Tengt við hljóð símans"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Tengt við skráaflutningsþjón"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Nota við skráaflutning"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Nota fyrir inntak"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Nota fyrir heyrnartæki"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Nota fyrir LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Para"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PARA"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Hætta við"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 27f5c3c..173588b 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Apparecchi acustici"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connessione con gli apparecchi acustici stabilita"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Connesso a LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Collegato ad audio media"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Collegato ad audio telefono"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Collegato al server di trasferimento file"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Usa per trasferimento file"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizza per l\'input"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utilizza per gli apparecchi acustici"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usa per LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Accoppia"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ACCOPPIA"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annulla"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 371d20a..f969f45 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"‏אודיו באיכות HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"‏אודיו באיכות HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"מכשירי שמיעה"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"מחובר אל מכשירי שמיעה"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"‏מחובר אל LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"מחובר לאודיו של מדיה"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"מחובר לאודיו של הטלפון"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"מחובר לשרת העברת קבצים"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"לצורך העברת קבצים"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"שימוש כקלט"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"שימוש בשביל מכשירי שמיעה"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"‏לשימוש עבור LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"התאמה"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"התאמה"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ביטול"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 0d85974..620aca6 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD オーディオ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD オーディオ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"補聴器"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"補聴器に接続"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO に接続"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"メディアの音声に接続"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"携帯電話の音声に接続"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ファイル転送サーバーに接続"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ファイル転送に使用"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"入力に使用"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"補聴器に使用"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO の使用"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ペア設定する"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ペア設定する"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"キャンセル"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 572d109..daa0fae 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD აუდიო: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD აუდიო"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"სმენის მოწყობილობები"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"დაკავშირებულია სმენის მოწყობილობებთან"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"დაკავშირებულია შემდეგთან: LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"დაკავშირებულია აუდიო მულტიმედიურ სისტემასთან"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"დაკავშირებულია ტელეფონის აუდიო მოწყობილობასთან"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"დაკავშირებულია ფაილების გადაცემის სერვერთან"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ფაილების ტრანსფერისათვის გამოყენება"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"შეტანისთვის გამოყენება"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"გამოყენება სმენის მოწყობილობებისთვის"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"გამოიყენება შემდეგისთვის: LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"დაწყვილება"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"დაწყვილება"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"გაუქმება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 8a54e52..28675ac 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD форматты аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD форматты аудио"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Есту аппараттары"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Есту аппараттарына жалғанған"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Медиа аудиосына жалғанған"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Телефон аудиосына қосылған"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Файл жіберу серверіне жалғанған"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Файлды жіберу үшін қолдану"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Кіріс үшін қолдану"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Есту аппараттары үшін пайдалану"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Жұптау"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ЖҰПТАУ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Бас тарту"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index efc422c..e58ed68 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"សំឡេងកម្រិត HD៖ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"សំឡេងកម្រិត HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ឧបករណ៍​ជំនួយការ​ស្ដាប់"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"បាន​ភ្ជាប់ទៅ​ឧបករណ៍​ជំនួយការ​ស្ដាប់"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"បានភ្ជាប់​ទៅ LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"បា​ន​ភ្ជាប់​ទៅ​អូឌីយ៉ូ​មេឌៀ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"តភ្ជាប់​ទៅ​អូឌីយ៉ូ​ទូរស័ព្ទ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"បាន​តភ្ជាប់​ទៅ​ម៉ាស៊ីន​មេ​ផ្ទេរ​ឯកសារ"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ប្រើ​សម្រាប់​ផ្ទេរ​ឯកសារ"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ប្រើ​សម្រាប់​បញ្ចូល"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ប្រើ​សម្រាប់​ឧបករណ៍​ជំនួយការ​ស្តាប់"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"ប្រើ​សម្រាប់ LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ផ្គូផ្គង"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ផ្គូផ្គង"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"បោះ​បង់​"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 91ee100..73f6a21 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ಆಡಿಯೋ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ಆಡಿಯೋ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ಶ್ರವಣ ಸಾಧನಗಳಿಗೆ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO ಗೆ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ಮಾಧ್ಯಮ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ಫೋನ್ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ಫೈಲ್ ವರ್ಗಾವಣೆ ಸರ್ವರ್‌ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ಫೈಲ್‌ ವರ್ಗಾವಣೆಗಾಗಿ ಬಳಸು"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ಇನ್‌ಪುಟ್‌ಗಾಗಿ ಬಳಸು"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ಶ್ರವಣ ಸಾಧನಗಳಿಗಾಗಿ ಬಳಸಿ"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ಗೆ ಬಳಸಿ"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ಜೋಡಿಸಿ"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ಜೋಡಿ ಮಾಡು"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ರದ್ದುಮಾಡಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 7d7b9a8..1ff18c6 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD 오디오: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD 오디오"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"보청기"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"보청기에 연결됨"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO에 연결됨"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"미디어 오디오에 연결됨"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"휴대전화 오디오에 연결됨"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"파일 전송 서버에 연결됨"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"파일 전송에 사용"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"입력에 사용"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"보청기로 사용"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO에 사용"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"페어링"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"페어링"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"취소"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 319e61a..58e3107 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD форматындагы аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD форматындагы аудио"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Угуу аппараттары"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Угуу аппараттарына туташып турат"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO менен туташты"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Медиа аудиого туташты"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Телефон аудиосуна туташты"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Файл өткөрүү серверине туташты"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Файл өткөрүү үчүн колдонулсун"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Киргизүү үчүн колдонулсун"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Угуу аппараттары үчүн колдонуу"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO үчүн колдонуу"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Байланыштыруу"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ЖУПТАШТЫРУУ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Жок"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 5490543..a2b8c6b 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"ສຽງ HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"ສຽງ HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ອຸປະກອນຊ່ວຍຟັງ"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ເຊື່ອມຕໍ່ຫາອຸປະກອນຊ່ວຍຟັງແລ້ວ"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"ເຊື່ອມຕໍ່ຫາ LE_AUDIO ແລ້ວ"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ເຊື່ອມຕໍ່ກັບສື່ດ້ານສຽງແລ້ວ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ເຊື່ອມຕໍ່ກັບສຽງໂທລະສັບແລ້ວ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ເຊື່ອມຕໍ່ກັບເຊີບເວີໂອນຍ້າຍໄຟລ໌ແລ້ວ"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ໃຊ້ເພື່ອໂອນຍ້າຍໄຟລ໌"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ໃຊ້ສຳລັບການປ້ອນຂໍ້ມູນ"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ໃຊ້ສຳລັບອຸປະກອນຊ່ວຍຟັງ"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"ໃຊ້ສຳລັບ LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ຈັບຄູ່"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ຈັບຄູ່"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ຍົກເລີກ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index b53e1b0..d8c1a45 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD garsas: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD garsas"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Klausos aparatai"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Prisijungta prie klausos aparatų"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Prisijungta prie LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Prijungta prie medijos garso įrašo"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Prijungta prie telefono garso"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Prijungta prie failų perkėlimo serverio"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Naudoti failų perkėlimui"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Naudoti įvedant"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Naudoti su klausos aparatais"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Naudoti LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Susieti"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SUSIETI"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Atšaukti"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 053e97e..9ac8c65 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Dzirdes aparāti"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO profils"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Izveidots savienojums ar dzirdes aparātiem"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Izveidots savienojums ar LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Savienots ar multivides audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Savienots ar tālruņa audio"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Savienots ar failu pārsūtīšanas serveri"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Izmantot faila pārsūtīšanai"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Izmantot ievadei"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Izmantot dzirdes aparātiem"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Izmantot LE_AUDIO profilam"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Izveidot pāri"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SAVIENOT PĀRĪ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Atcelt"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 5d348b6..c08fd24 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-аудио"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слушни помагала"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Поврзано со слушни помагала"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Поврзано на LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Поврзан со аудио на медиуми"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Поврзан со аудио на телефон"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Поврзан со сервер за пренос на датотеки"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Користи за пренос на датотеки"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Користи за внес"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Користи за слушни помагала"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Користи за LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Спари"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"СПАРИ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Откажи"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index b9e719b3..b2b6a40 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ഓഡിയോ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ഓഡിയോ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ശ്രവണ സഹായികൾ"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ശ്രവണ സഹായികളിലേക്ക് കണക്‌റ്റ് ചെയ്‌തു"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO-യിലേക്ക് കണക്റ്റ് ചെയ്തു"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"മീഡിയ ഓഡിയോയിലേക്ക് കണ‌ക്റ്റുചെയ്‌തു"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ഫോൺ ഓഡിയോയിൽ കണ‌ക്റ്റുചെ‌യ്‌തു"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ഫയൽ കൈമാറ്റ സെർവറിലേക്ക് കണ‌ക്റ്റുചെ‌യ്‌തു"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ഫയൽ കൈമാറ്റത്തിനായി ഉപയോഗിക്കുന്നു"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ഇൻപുട്ടിനായി ഉപയോഗിക്കുക"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ശ്രവണ സഹായികൾക്കായി ഉപയോഗിക്കുക"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ഉപയോഗിക്കുക"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ജോടിയാക്കുക"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ജോടിയാക്കുക"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"റദ്ദാക്കുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index d905541..b62d103 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD аудио"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Сонсголын төхөөрөмж"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_АУДИО"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Сонсголын төхөөрөмжтэй холбосон"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_АУДИОНД холбогдлоо"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Медиа аудиод холбогдсон"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Утасны аудид холбогдсон"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Файл дамжуулах серверт холбогдсон"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Файл дамжуулахад ашиглах"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Оруулахад ашиглах"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Сонсголын төхөөрөмжид ашиглах"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_АУДИОНД ашиглах"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Хослуулах"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ХОСЛУУЛАХ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Цуцлах"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 4365b20..a816f49 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ऑडिओ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ऑडिओ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"श्रवणयंत्रे"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"श्रवण यंत्रांशी कनेक्ट केले आहे"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO शी कनेक्ट केले आहे"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मीडिया ऑडिओवर कनेक्ट केले"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फोन ऑडिओ वर कनेक्ट केले"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फाइल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाइल स्थानांतरणासाठी वापरा"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुट साठी वापरा"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"श्रवण यंत्रांसाठी वापरा"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO साठी वापरले आहे"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"पेअर करा"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"पेअर करा"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"रद्द करा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index e054bd0..0841328 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Alat Bantu Dengar"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Disambungkan pada Alat Bantu Dengar"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Disambungkan kepada LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Disambungkan ke audio media"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Disambungkan ke audio telefon"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Bersambung ke pelayan pemindahan fail"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gunakan untuk pemindahan fail"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gunakan untuk input"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gunakan untuk Alat Bantu Dengar"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Gunakan untuk LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Gandingkan"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"JADIKAN PASANGAN"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Batal"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index ca7ffc9..d5fbdaf 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD အသံ- <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD အသံ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"နားကြားကိရိယာ"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"နားကြားကိရိယာနှင့် ချိတ်ဆက်ပြီးပါပြီ"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO နှင့် ချိတ်ဆက်ထားသည်"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"မီဒီယာအသံအား ချိတ်ဆက်ရန်"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ဖုန်းအသံအား ချိတ်ဆက်ရန်"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ဖိုင်လွှဲပြောင်းမည့်ဆာဗာနှင့် ချိတ်ဆက်ထားပြီး"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ဖိုင်လွဲပြောင်းရန်အတွက်အသုံးပြုရန်"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ထည့်သွင်းရန်အသုံးပြုသည်"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"နားကြားကိရိယာအတွက် အသုံးပြုသည်"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO အတွက် သုံးသည်"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"တွဲချိတ်ရန်"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"တွဲချိတ်ရန်"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"မလုပ်တော့"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index d355ae5..e7f70d1 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-lyd"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Høreapparater"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Koblet til høreapparater"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Koblet til LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Koblet til medielyd"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Koblet til telefonlyd"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Koblet til tjener for filoverføring"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Bruk til filoverføring"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Bruk for inndata"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Bruk for høreapparater"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Bruk for LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Koble til"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"KOBLE TIL"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Avbryt"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 508222f..8fc329f8 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD अडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD अडियो"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"श्रवण यन्त्रहरू"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"श्रवण यन्त्रहरूमा जडान गरियो"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO मा कनेक्ट गरिएको छ"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मिडिया अडियोसँग जडित"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फोन अडियोमा जडान गरियो"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फाइल ट्रान्सफर सर्भरमा जडान गरियो"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाइल ट्रान्सफरका लागि प्रयोग गर्नुहोस्"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुटको लागि प्रयोग गर्नुहोस्"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"श्रवण यन्त्रहरूका लागि प्रयोग गर्नुहोस्"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO मा प्रयोग गर्नुहोस्"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"कनेक्ट गर्नुहोस्"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"जोडी"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"रद्द गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index daef7e1..35e651b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hoortoestellen"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Verbonden met hoortoestellen"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Verbonden met LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Verbonden met audio van medium"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Verbonden met audio van telefoon"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Verbonden met server voor bestandsoverdracht"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gebruiken voor bestandsoverdracht"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gebruiken voor invoer"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gebruiken voor hoortoestellen"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Gebruiken voor LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Koppelen"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"KOPPELEN"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annuleren"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 6faf614..2200567 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ଅଡିଓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ଅଡିଓ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ଶ୍ରବଣ ଯନ୍ତ୍ରକୁ ସଂଯୋଗ ହୋଇଛି"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ମିଡିଆ ଅଡିଓ ସହ ସଂଯୁକ୍ତ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ଫୋନ୍‌ ଅଡିଓ ସହିତ ସଂଯୁକ୍ତ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌ ସର୍ଭର୍‌ ସହ ସଂଯୁକ୍ତ"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ଇନ୍‌ପୁଟ୍‌ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ପେୟାର୍‌"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ପେୟାର୍‌"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ବାତିଲ୍‌ କରନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 475ce35..3c1cfcb 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ਆਡੀਓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ਆਡੀਓ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ਮੀਡੀਆ  ਆਡੀਓ  ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ਫ਼ੋਨ ਔਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ਫਾਈਲ ਟ੍ਰਾਂਸਫ਼ਰ ਸਰਵਰ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਲਈ ਵਰਤੋ"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ਇਨਪੁਟ ਲਈ ਵਰਤੋ"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਲਈ ਵਰਤੋ"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ਜੋੜਾਬੱਧ ਕਰੋ"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ਜੋੜਾਬੱਧ ਕਰੋ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ਰੱਦ ਕਰੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 7c656afe..1eb2dd9 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Dźwięk HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Dźwięk HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparaty słuchowe"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Połączono z aparatami słuchowymi"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Połączono z LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Połączono z funkcją audio multimediów"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Połączono z funkcją audio telefonu"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Połączono z serwerem transferu plików"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Użyj do transferu plików"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Użyj do wprowadzania"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Użyj z aparatami słuchowymi"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Używaj dla LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Sparuj"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SPARUJ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Anuluj"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 2356f41..38d637f 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Áudio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparelhos auditivos"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a aparelhos auditivos"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Conectado a LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado ao áudio da mídia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado ao áudio do smartphone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Conectado ao servidor de transferência de arquivo"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Usado para transferência de arquivo"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Usar para entrada"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar para aparelhos auditivos"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar para LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parear"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAREAR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 43155a7..ebe5753 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Áudio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparelhos auditivos"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Ligado a aparelhos auditivos"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Ligado a LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Ligado ao áudio de multimédia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Ligado ao áudio do telefone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Ligado ao servidor de transferência de ficheiros"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utilizar para transferência de ficheiros"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizar para entrada"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utilizar para aparelhos auditivos"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Utilizar para LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Sincr."</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SINCRONIZAR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 2356f41..38d637f 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Áudio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparelhos auditivos"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a aparelhos auditivos"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Conectado a LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado ao áudio da mídia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado ao áudio do smartphone"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Conectado ao servidor de transferência de arquivo"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Usado para transferência de arquivo"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Usar para entrada"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar para aparelhos auditivos"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar para LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parear"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAREAR"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 083c2b9..ce58f1c 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparate auditive"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectat la aparatul auditiv"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Conectat la LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectat la profilul pentru conținut media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectat la componenta audio a telefonului"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Conectat la serverul de transfer de fișiere"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utilizați pentru transferul de fișiere"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizați pentru introducere date"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosiți pentru aparatele auditive"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Folosiți pentru LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Asociați"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"CONECTAȚI"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Anulați"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 0a0fce34..c158a14 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD Audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слуховые аппараты"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Слуховой аппарат подключен"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Подключено к LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Подключено к мультимедийному аудиоустройству"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Подключено к аудиоустройству телефона"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Установлено подключение к серверу передачи файлов"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Используется для передачи файлов"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Использовать для ввода"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Использовать для слухового аппарата"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Использовать для LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Добавить"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ДОБАВИТЬ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Отмена"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 5b30c81..41f10c9 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ශ්‍රව්‍යය: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ශ්‍රව්‍යය"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ශ්‍රවණාධාරක"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ශ්‍රවණාධාරක වෙත සම්බන්ධ කළා"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO වෙත සම්බන්ධ විය"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"මාධ්‍ය ශ්‍රව්‍යට සම්බන්ධ විය"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"දුරකතනයේ ශ්‍රව්‍යට සම්බන්ධ විය"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ගොනු හුවමාරු සේවාදායකය සමග සම්බන්ධ විය"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ගොනු හුවමාරුව සඳහා භාවිතා කරන්න"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ආදානය සඳහා භාවිතා කරන්න"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ශ්‍රවණාධාර සඳහා භාවිත කරන්න"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO සඳහා භාවිත කරන්න"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"යුගල කරන්න"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"යුගල කරන්න"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"අවලංගු කරන්න"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 0d929db..801c213 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD zvuk"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Načúvadlá"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Pripojené k načúvadlám"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Pripojené k profilu LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Pripojené ku zvukovému médiu"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Pripojené ku zvuku telefónu"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Pripojené na server pre prenos údajov"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Použiť na prenos súborov"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Použiť pre vstup"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Použiť pre načúvadlá"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Používať s profilom LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Párovať"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PÁROVAŤ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Zrušiť"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 7a559fc..a331018 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Zvok visoke kakovosti: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Zvok visoke kakovosti"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Slušni pripomočki"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Povezava s slušnimi pripomočki je vzpostavljena"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Povezano s profilom LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezan s profilom za predstavnostni zvok"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezava s profilom za zvok telefona vzpostavljena"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Povezava s strežnikom za prenos datotek je vzpostavljena"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Uporabi za prenos datotek"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Uporabi za vnos"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Uporabi za slušne pripomočke"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Uporaba za profil LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Seznani"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SEZNANI"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Prekliči"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 4a1eeff..cb9bea8 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparatet e dëgjimit"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Lidhur me aparatet e dëgjimit"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Lidhur me LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"U lidh me audion e medias"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"U lidh me audion e telefonit"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"U lidh me serverin e transferimit të skedarëve"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Përdor për transferimin e skedarëve"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Përdore për hyrjen"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Përdore për aparatet e dëgjimit"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Përdor për LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Çifto"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ÇIFTO"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Anulo"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 0359833..9e90333 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD звук: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD звук"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слушни апарати"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Повезано са слушним апаратима"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Повезано са LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Повезано са звуком медија"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Повезано са звуком телефона"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Повезано са сервером за пренос датотека"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Коришћење за пренос датотека"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Користи за улаз"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Користи за слушне апарате"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Користите за LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Упари"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"УПАРИ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Откажи"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index a0b92a2..215d9ad 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-ljud: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-ljud"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hörapparater"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Ansluten till hörapparater"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Ansluten till LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Ansluten till medialjud"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Ansluten till telefonens ljud"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Ansluten till filöverföringsserver"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Använd för filöverföring"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Använd för inmatning"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Använd med hörapparater"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Använd för LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parkoppla"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PARKOPPLA"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Avbryt"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 76136c8..f51825b 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Sauti ya HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Sauti ya HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Vifaa vya Kusaidia Kusikia"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Imeunganishwa kwenye Vifaa vya Kusaidia Kusikia"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Imeunganishwa kwenye LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Imeunganishwa kwenye sikika ya njia ya mawasiliano"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Imeunganishwa kwenye sauti ya simu"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Imeunganishwa kwenye seva ya kuhamisha faili"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Tumia kwa hali faili"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Tumia kwa kuingiza"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Tumia kwenye Vifaa vya Kusaidia Kusikia"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Tumia kwa ajili ya LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Oanisha"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"OANISHA"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Ghairi"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 185104f..8128683 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ஆடியோ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ஆடியோ"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"செவித்துணை கருவிகள்"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"செவித்துணை கருவிகளுடன் இணைக்கப்பட்டது"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO உடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"மீடியா ஆடியோவுடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"மொபைல் ஆடியோவுடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ஃபைலைப் பரிமாற்றும் சேவையகத்துடன் இணைக்கப்பட்டது"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ஃபைல் பரிமாற்றத்திற்காகப் பயன்படுத்து"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"உள்ளீட்டுக்குப் பயன்படுத்து"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"செவித்துணை கருவிகளுக்குப் பயன்படுத்தவும்"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIOவிற்குப் பயன்படுத்தும்"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"இணை"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"இணை"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ரத்துசெய்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index d3c13a9..298f850 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ఆడియో: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ఆడియో"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"వినికిడి మద్దతు ఉపకరణాలు"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"వినికిడి మద్దతు ఉపకరణాలకు కనెక్ట్ చేయబడింది"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIOకు కనెక్ట్ చేయబడింది"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"మీడియా ఆడియోకు కనెక్ట్ చేయబడింది"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ఫోన్ ఆడియోకు కనెక్ట్ చేయబడింది"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ఫైల్ బదిలీ సర్వర్‌కు కనెక్ట్ చేయబడింది"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ఫైల్ బదిలీ కోసం ఉపయోగించు"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ఇన్‌పుట్ కోసం ఉపయోగించు"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"వినికిడి మద్దతు ఉపకరణాలకు ఉపయోగించండి"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO కోసం ఉపయోగించండి"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"జత చేయి"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"జత చేయి"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"రద్దు చేయి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 51a0420..e18c649 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"เสียง HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"เสียง HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"เครื่องช่วยฟัง"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"เชื่อมต่อกับเครื่องช่วยฟังแล้ว"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"เชื่อมต่อกับ LE_AUDIO แล้ว"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"เชื่อมต่อกับระบบเสียงของสื่อแล้ว"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"เชื่อมต่อกับระบบเสียงของโทรศัพท์แล้ว"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"เชื่อมต่อกับเซิร์ฟเวอร์สำหรับโอนไฟล์แล้ว"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ใช้สำหรับการโอนไฟล์"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ใช้สำหรับการป้อนข้อมูล"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ใช้สำหรับเครื่องช่วยฟัง"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"ใช้สำหรับ LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"จับคู่อุปกรณ์"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"จับคู่อุปกรณ์"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ยกเลิก"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 1bc84af..628bacc 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Mga Hearing Aid"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Nakakonekta sa Mga Hearing Aid"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Nakakonekta sa LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Konektado sa media audio"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Nakakonekta sa audio ng telepono"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Nakakonekta sa server sa paglilipat ng file"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Ginagamit para sa paglilipat ng file"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gamitin para sa input"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gamitin para sa Mga Hearing Aid"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Gamitin para sa LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Ipares"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"IPARES"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Kanselahin"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index a00457a..3e30d43 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ses: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ses"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"İşitme Cihazları"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"İşitme Cihazlarına Bağlandı"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"LE_AUDIO\'ya bağlandı"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Medya sesine bağlanıldı"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Telefon sesine bağlandı"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Dosya aktarım sunucusuna bağlandı"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Dosya aktarımı için kullan"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Giriş için kullan"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"İşitme Cihazları için kullan"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO kullan"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Eşle"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"EŞLE"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"İptal"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 6aac2932..8683953 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-аудіо: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-аудіо"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слухові апарати"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Підключено до слухових апаратів"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Підключено до LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Підключено до аудіоджерела"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Підключено до звуку телеф."</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Підключ. до сервера передачі файлів"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Викор. для перед. файлів"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Викор. для введ."</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Використовувати для слухових апаратів"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Використовувати для LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Підключити"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ПІДКЛЮЧИТИСЯ"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Скасувати"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 14cea9a..09e50f6 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"‏HD آڈیو: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"‏HD آڈیو"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"سماعتی آلات"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"سماعتی آلات سے منسلک ہے"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"‏LE_AUDIO سے منسلک ہے"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"میڈیا آڈیو سے مربوط"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"فون آڈیو سے مربوط"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"فائل منتقلی سرور سے مربوط ہو گیا ہے"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"فائل منتقل کرنے کیلئے استعمال کریں"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ان پٹ کیلئے استعمال"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"سماعتی آلات کے لیے استعمال کریں"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"‏LE_AUDIO کے لیے استعمال کریں"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"جوڑا بنائیں"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"جوڑا بنائیں"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"منسوخ کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 47c828d..d0b9506 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Eshitish apparatlari"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Eshitish apparatlariga ulangan"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Ulandi: LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Audio qurilmasiga ulangan"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Telefon karnayiga ulanildi"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Fayl almashinish serveriga ulanildi"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Fayl almashinish uchun foydalanish"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Kiritish qurilmasi sifatida foydalanish"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Eshitish apparatlari uchun foydalanish"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO uchun foydalanish"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"OK"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ULANISH"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Bekor qilish"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index aaaaada..9eebd02 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Âm thanh HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Âm thanh HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Thiết bị trợ thính"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Đã kết nối với Thiết bị trợ thính"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Đã kết nối với LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Đã kết nối với âm thanh nội dung nghe nhìn"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Đã kết nối với âm thanh điện thoại"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Đã kết nối với máy chủ chuyển tệp"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Sử dụng để chuyển tệp"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Sử dụng để nhập"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Dùng cho Thiết bị trợ thính"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Dùng cho LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Ghép nối"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"GHÉP NỐI"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Hủy"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index c85dba0..3ed1860 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD 音频:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD 音频"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"助听器"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"已连接到助听器"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"已连接到 LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"已连接到媒体音频"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"已连接到手机音频"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"已连接到文件传输服务器"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"用于文件传输"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"用于输入"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"用于助听器"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"用于 LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"配对"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"配对"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"取消"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index dccd1df..965e78f 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"高清音訊:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"高清音訊"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"助聽器"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"已連接助聽器"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"已連接 LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"已連接媒體音頻裝置"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"已連接手機耳機"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"已連線至檔案傳輸伺服器"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"用於傳輸檔案"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"用於輸入"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"用於助聽器"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"用於 LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"配對"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"配對"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"取消"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index afa27aa..5b74ab0 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -122,7 +122,11 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD 高解析音訊:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD 高解析音訊"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"助聽器"</string>
+    <!-- no translation found for bluetooth_profile_le_audio (5158149987518342036) -->
+    <skip />
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"已連接到助聽器"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_connected (3162538609379333442) -->
+    <skip />
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"連接至媒體音訊"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"連接至電話音訊"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"已連線到檔案傳輸伺服器"</string>
@@ -140,6 +144,8 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"用於傳輸檔案"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"用於輸入"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"用於助聽器"</string>
+    <!-- no translation found for bluetooth_le_audio_profile_summary_use_for (2778318636027348572) -->
+    <skip />
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"配對"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"配對"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"取消"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 9b3063d..1af1fa1 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -122,7 +122,9 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Umsindo we-HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Umsindo we-HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Izinsiza zokuzwa"</string>
+    <string name="bluetooth_profile_le_audio" msgid="5158149987518342036">"I-LE_AUDIO"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Kuxhumeke kwizinsiza zokuzwa"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="3162538609379333442">"Kuxhunywe ku-LE_AUDIO"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Ixhume emsindweni wemidiya"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Ixhunywe kumsindo wefoni"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Ixhunywe kwiseva yokudlulisa ifayela"</string>
@@ -140,6 +142,7 @@
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Sebenziselwa ukudlulisa ifayela"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Isetshenziselwa okufakwayo"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Sebenzisa izinsiza zokuzwa"</string>
+    <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Sebenzisela i-LE_AUDIO"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Bhangqa"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"BHANGQA"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Khansela"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index dd8f604..aede665 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -21,6 +21,7 @@
 import static android.media.MediaRoute2Info.TYPE_GROUP;
 import static android.media.MediaRoute2Info.TYPE_HDMI;
 import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
+import static android.media.MediaRoute2Info.TYPE_BLE_HEADSET;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
 import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
@@ -482,6 +483,7 @@
                 break;
             case TYPE_HEARING_AID:
             case TYPE_BLUETOOTH_A2DP:
+            case TYPE_BLE_HEADSET:
                 final BluetoothDevice device =
                         BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getAddress());
                 final CachedBluetoothDevice cachedDevice =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 22001c9..865c2f0b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -38,6 +38,7 @@
 import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LeAudioProfile;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -440,6 +441,7 @@
     private boolean isActiveDevice(CachedBluetoothDevice device) {
         boolean isActiveDeviceA2dp = false;
         boolean isActiveDeviceHearingAid = false;
+        boolean isActiveLeAudio = false;
         final A2dpProfile a2dpProfile = mLocalBluetoothManager.getProfileManager().getA2dpProfile();
         if (a2dpProfile != null) {
             isActiveDeviceA2dp = device.getDevice().equals(a2dpProfile.getActiveDevice());
@@ -453,7 +455,15 @@
             }
         }
 
-        return isActiveDeviceA2dp || isActiveDeviceHearingAid;
+        if (!isActiveDeviceA2dp && !isActiveDeviceHearingAid) {
+            final LeAudioProfile leAudioProfile = mLocalBluetoothManager.getProfileManager()
+                    .getLeAudioProfile();
+            if (leAudioProfile != null) {
+                isActiveLeAudio = leAudioProfile.getActiveDevices().contains(device.getDevice());
+            }
+        }
+
+        return isActiveDeviceA2dp || isActiveDeviceHearingAid || isActiveLeAudio;
     }
 
     private Collection<DeviceCallback> getCallbacks() {
@@ -479,9 +489,9 @@
         @Override
         public void onDeviceListAdded(List<MediaDevice> devices) {
             synchronized (mMediaDevicesLock) {
+                Collections.sort(devices, COMPARATOR);
                 mMediaDevices.clear();
                 mMediaDevices.addAll(devices);
-                Collections.sort(devices, COMPARATOR);
                 // Add disconnected bluetooth devices only when phone output device is available.
                 for (MediaDevice device : devices) {
                     final int type = device.getDeviceType();
@@ -526,7 +536,7 @@
                 if (cachedDevice != null) {
                     if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
                             && !cachedDevice.isConnected()
-                            && isA2dpOrHearingAidDevice(cachedDevice)) {
+                            && isMediaDevice(cachedDevice)) {
                         deviceCount++;
                         cachedBluetoothDeviceList.add(cachedDevice);
                         if (deviceCount >= MAX_DISCONNECTED_DEVICE_NUM) {
@@ -550,9 +560,10 @@
             return new ArrayList<>(mDisconnectedMediaDevices);
         }
 
-        private boolean isA2dpOrHearingAidDevice(CachedBluetoothDevice device) {
+        private boolean isMediaDevice(CachedBluetoothDevice device) {
             for (LocalBluetoothProfile profile : device.getConnectableProfiles()) {
-                if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile) {
+                if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile ||
+                        profile instanceof LeAudioProfile) {
                     return true;
                 }
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index f21c359..a49d7f6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -29,6 +29,7 @@
 import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static android.media.MediaRoute2Info.TYPE_BLE_HEADSET;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -122,6 +123,7 @@
                 break;
             case TYPE_HEARING_AID:
             case TYPE_BLUETOOTH_A2DP:
+            case TYPE_BLE_HEADSET:
                 mType = MediaDeviceType.TYPE_BLUETOOTH_DEVICE;
                 break;
             case TYPE_UNKNOWN:
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index e887c450..f50802a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -52,6 +52,7 @@
 
         when(mDevice.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
         when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
+        when(mDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
 
         mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null);
     }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7d06620..41193e6a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -583,6 +583,9 @@
     <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
     <uses-permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE" />
 
+    <!-- Permission required to run GtsAssistantTestCases -->
+    <uses-permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index eb5ad25..95ce423 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -215,8 +215,11 @@
     <!-- TV picture-in-picture -->
     <uses-permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE" />
 
-    <!-- DND access -->
+    <!-- notifications & DND access -->
     <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
+    <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
+    <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
+    <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
 
     <!-- It's like, reality, but, you know, virtual -->
     <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index f7e0d58..3ccf5e4 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -209,8 +209,13 @@
         val heightRatio = state.height.toFloat() / ghostedViewState.height
         val scale = min(widthRatio, heightRatio)
 
+        if (ghostedView.parent is ViewGroup) {
+            // Recalculate the matrix in case the ghosted view moved. We ensure that the ghosted
+            // view is still attached to a ViewGroup, otherwise calculateMatrix will throw.
+            GhostView.calculateMatrix(ghostedView, launchContainer, ghostViewMatrix)
+        }
+
         launchContainer.getLocationOnScreen(launchContainerLocation)
-        GhostView.calculateMatrix(ghostedView, launchContainer, ghostViewMatrix)
         ghostViewMatrix.postScale(
             scale, scale,
             ghostedViewState.centerX - launchContainerLocation[0],
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
index a3d924f..8063483 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
@@ -197,26 +197,6 @@
         return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
     }
 
-    /**
-     * Interpolate alpha for notifications background scrim during shade expansion.
-     * @param fraction Shade expansion fraction
-     * @param forUiContent If we want the alpha of the scrims, or ui that's on top of them.
-     */
-    public static float getNotificationScrimAlpha(float fraction, boolean forUiContent) {
-        if (forUiContent) {
-            fraction = MathUtils.constrainedMap(0f, 1f, 0.3f, 1f, fraction);
-        } else {
-            fraction = MathUtils.constrainedMap(0f, 1f, 0f, 0.5f, fraction);
-        }
-        fraction = fraction * 1.2f - 0.2f;
-        if (fraction <= 0) {
-            return 0;
-        } else {
-            final float oneMinusFrac = 1f - fraction;
-            return (float) (1f - 0.5f * (1f - Math.cos(3.14159f * oneMinusFrac * oneMinusFrac)));
-        }
-    }
-
     // Create the default emphasized interpolator
     private static PathInterpolator createEmphasizedInterpolator() {
         Path path = new Path();
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt
new file mode 100644
index 0000000..0ee2bfe
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt
@@ -0,0 +1,37 @@
+package com.android.systemui.animation
+
+import android.util.MathUtils
+
+object ShadeInterpolation {
+
+    /**
+     * Interpolate alpha for notification background scrim during shade expansion.
+     * @param fraction Shade expansion fraction
+     */
+    @JvmStatic
+    fun getNotificationScrimAlpha(fraction: Float): Float {
+        val mappedFraction = MathUtils.constrainedMap(0f, 1f, 0f, 0.5f, fraction)
+        return interpolateEaseInOut(mappedFraction)
+    }
+
+    /**
+     * Interpolate alpha for shade content during shade expansion.
+     * @param fraction Shade expansion fraction
+     */
+    @JvmStatic
+    fun getContentAlpha(fraction: Float): Float {
+        val mappedFraction = MathUtils.constrainedMap(0f, 1f, 0.3f, 1f, fraction)
+        return interpolateEaseInOut(mappedFraction)
+    }
+
+    private fun interpolateEaseInOut(fraction: Float): Float {
+        val mappedFraction = fraction * 1.2f - 0.2f
+        return if (mappedFraction <= 0) {
+            0f
+        } else {
+            val oneMinusFrac = 1f - mappedFraction
+            (1f - 0.5f * (1f - Math.cos((3.14159f * oneMinusFrac * oneMinusFrac).toDouble())))
+                    .toFloat()
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 4adb546..6114728 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -37,9 +37,6 @@
 -keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
     *;
 }
--keep class com.android.systemui.util.InjectionInflationController$ViewInstanceCreator {
-    *;
-}
 -keep class androidx.core.app.CoreComponentFactory
 
 -keep public class * extends com.android.systemui.SystemUI {
diff --git a/packages/SystemUI/res/anim/fp_to_unlock.xml b/packages/SystemUI/res-keyguard/drawable/fp_to_unlock.xml
similarity index 87%
rename from packages/SystemUI/res/anim/fp_to_unlock.xml
rename to packages/SystemUI/res-keyguard/drawable/fp_to_unlock.xml
index a5f75b6..b93ccc6 100644
--- a/packages/SystemUI/res/anim/fp_to_unlock.xml
+++ b/packages/SystemUI/res-keyguard/drawable/fp_to_unlock.xml
@@ -19,15 +19,15 @@
       <group android:name="_R_G">
         <group android:name="_R_G_L_1_G_N_7_T_0" android:translateX="-27" android:translateY="-17.5">
           <group android:name="_R_G_L_1_G" android:translateX="30.75" android:translateY="25.75">
-            <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " />
-            <path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " />
-            <path android:name="_R_G_L_1_G_D_2_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " />
-            <path android:name="_R_G_L_1_G_D_3_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " />
+            <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#FF000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " />
+            <path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#FF000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " />
+            <path android:name="_R_G_L_1_G_D_2_P_0" android:strokeColor="#FF000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " />
+            <path android:name="_R_G_L_1_G_D_3_P_0" android:strokeColor="#FF000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " />
           </group>
         </group>
         <group android:name="_R_G_L_0_G_N_7_T_0" android:translateX="-27" android:translateY="-17.5">
           <group android:name="_R_G_L_0_G" android:translateX="47.357" android:translateY="53.25" android:pivotX="2.75" android:pivotY="2.75" android:scaleX="1.41866" android:scaleY="1.41866">
-            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#b7f29f" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c " />
+            <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#FF000000" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c " />
           </group>
         </group>
       </group>
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_fingerprint.xml b/packages/SystemUI/res-keyguard/drawable/ic_fingerprint.xml
new file mode 100644
index 0000000..2063d21
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_fingerprint.xml
@@ -0,0 +1,48 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="65dp"
+        android:width="46dp"
+        android:viewportHeight="65"
+        android:viewportWidth="46">
+    <group android:name="_R_G_L_0_G" android:translateX="3.75" android:translateY="8.25">
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2.5"
+            android:pathData="M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " />
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2.5"
+            android:pathData="M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " />
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2.5"
+            android:pathData="M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " />
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2.5"
+            android:pathData="M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_lock.xml b/packages/SystemUI/res-keyguard/drawable/ic_lock.xml
new file mode 100644
index 0000000..14a8d0b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_lock.xml
@@ -0,0 +1,95 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="65dp"
+        android:width="46dp"
+        android:viewportHeight="65"
+        android:viewportWidth="46">
+    <group android:name="_R_G">
+        <group android:name="_R_G_L_2_G_N_10_N_11_T_0"
+               android:translateX="-27.5"
+               android:translateY="-17.5">
+            <group android:name="_R_G_L_2_G_N_10_T_1"
+                   android:translateX="50.25"
+                   android:translateY="61">
+                <group android:name="_R_G_L_2_G_N_10_T_0"
+                       android:translateX="-13.75"
+                       android:translateY="-7.5">
+                    <group android:name="_R_G_L_2_G"
+                           android:translateX="-0.375"
+                           android:translateY="-22.375">
+                        <path android:name="_R_G_L_2_G_D_0_P_0"
+                              android:strokeColor="#FF000000"
+                              android:strokeLineCap="round"
+                              android:strokeLineJoin="round"
+                              android:strokeWidth="2"
+                              android:strokeAlpha="1"
+                              android:pathData=" M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c "/>
+                    </group>
+                </group>
+            </group>
+        </group>
+        <group android:name="_R_G_L_1_G_N_10_N_11_T_0"
+               android:translateX="-27.5"
+               android:translateY="-17.5">
+            <group android:name="_R_G_L_1_G_N_10_T_1"
+                   android:translateX="50.25"
+                   android:translateY="61">
+                <group android:name="_R_G_L_1_G_N_10_T_0"
+                       android:translateX="-13.75"
+                       android:translateY="-7.5">
+                    <group android:name="_R_G_L_1_G"
+                           android:translateX="5"
+                           android:translateY="-22.5">
+                        <path android:name="_R_G_L_1_G_D_0_P_0"
+                              android:strokeColor="#FF000000"
+                              android:strokeLineCap="round"
+                              android:strokeLineJoin="round"
+                              android:strokeWidth="2"
+                              android:strokeAlpha="1"
+                              android:pathData=" M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 "/>
+                    </group>
+                </group>
+            </group>
+        </group>
+        <group android:name="_R_G_L_0_G_N_10_N_11_T_0"
+               android:translateX="-27.5"
+               android:translateY="-17.5">
+            <group android:name="_R_G_L_0_G_N_10_T_1"
+                   android:translateX="50.25"
+                   android:translateY="61">
+                <group android:name="_R_G_L_0_G_N_10_T_0"
+                       android:translateX="-13.75"
+                       android:translateY="-7.5">
+                    <group android:name="_R_G_L_0_G"
+                           android:translateX="11"
+                           android:translateY="-0.25"
+                           android:pivotX="2.75"
+                           android:pivotY="2.75"
+                           android:scaleX="1"
+                           android:scaleY="1">
+                        <path android:name="_R_G_L_0_G_D_0_P_0"
+                              android:fillColor="#FF000000"
+                              android:fillAlpha="1"
+                              android:fillType="nonZero"
+                              android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c "/>
+                    </group>
+                </group>
+            </group>
+        </group>
+    </group>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_lock_aod.xml b/packages/SystemUI/res-keyguard/drawable/ic_lock_aod.xml
new file mode 100644
index 0000000..cdae306
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_lock_aod.xml
@@ -0,0 +1,41 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="65dp"
+        android:width="46dp"
+        android:viewportHeight="65"
+        android:viewportWidth="46">
+    <group android:name="_R_G_L_2_G" android:translateX="23" android:translateY="32.125">
+        <path
+            android:fillColor="#FF000000"
+            android:fillAlpha="1"
+            android:fillType="nonZero"
+            android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " />
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="1.5"
+            android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " />
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="1.5"
+            android:pathData=" M4.38 -2.62 C4.38,-2.62 4.38,-7.1 4.38,-7.1 C4.38,-9.46 2.42,-11.37 0,-11.37 C-2.42,-11.37 -4.37,-9.46 -4.37,-7.1 C-4.37,-7.1 -4.37,-2.62 -4.37,-2.62 " />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_unlocked.xml b/packages/SystemUI/res-keyguard/drawable/ic_unlocked.xml
new file mode 100644
index 0000000..54242781
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_unlocked.xml
@@ -0,0 +1,48 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="65dp"
+        android:width="46dp"
+        android:viewportHeight="65"
+        android:viewportWidth="46">
+    <group android:translateX="8.625"
+           android:translateY="13.625">
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2.5"
+            android:pathData="M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c "/>
+    </group>
+    <group android:translateX="14"
+           android:translateY="13.5">
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2.5"
+            android:pathData="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 "/>
+    </group>
+    <group android:translateX="20"
+           android:translateY="35.75">
+        <path
+            android:fillColor="#FF000000"
+            android:fillAlpha="1"
+            android:fillType="nonZero"
+            android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c "/>
+    </group>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/lock_aod_to_ls.xml b/packages/SystemUI/res-keyguard/drawable/lock_aod_to_ls.xml
new file mode 100644
index 0000000..d35f695
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/lock_aod_to_ls.xml
@@ -0,0 +1,151 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+                 xmlns:android="http://schemas.android.com/apk/res/android">
+    <aapt:attr name="android:drawable">
+        <vector android:height="65dp"
+                android:width="46dp"
+                android:viewportHeight="65"
+                android:viewportWidth="46">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G"
+                       android:translateX="23"
+                       android:translateY="32.125">
+                    <path android:name="_R_G_L_2_G_D_0_P_0"
+                          android:fillColor="#FF000000"
+                          android:fillAlpha="1"
+                          android:fillType="nonZero"
+                          android:pathData=" M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c "/>
+                </group>
+                <group android:name="_R_G_L_1_G"
+                       android:translateX="23"
+                       android:translateY="32.125">
+                    <path android:name="_R_G_L_1_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="1.5"
+                          android:strokeAlpha="1"
+                          android:pathData=" M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c "/>
+                </group>
+                <group android:name="_R_G_L_0_G"
+                       android:translateX="23"
+                       android:translateY="32.125">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="1.5"
+                          android:strokeAlpha="1"
+                          android:pathData=" M4.38 -2.62 C4.38,-2.62 4.38,-7.1 4.38,-7.1 C4.38,-9.46 2.42,-11.37 0,-11.37 C-2.42,-11.37 -4.37,-9.46 -4.37,-7.1 C-4.37,-7.1 -4.37,-2.62 -4.37,-2.62 "/>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c "
+                                android:valueTo="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="1.5"
+                                android:valueTo="2"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.386,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c "
+                                android:valueTo="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="1.5"
+                                android:valueTo="2"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.307,0 0.386,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M4.38 -2.62 C4.38,-2.62 4.38,-7.1 4.38,-7.1 C4.38,-9.46 2.42,-11.37 0,-11.37 C-2.42,-11.37 -4.37,-9.46 -4.37,-7.1 C-4.37,-7.1 -4.37,-2.62 -4.37,-2.62 "
+                                android:valueTo="M5.88 -3.87 C5.88,-3.87 5.88,-10.2 5.88,-10.2 C5.88,-13.54 3.08,-16.25 -0.37,-16.25 C-3.83,-16.25 -6.62,-13.54 -6.62,-10.2 C-6.62,-10.2 -6.62,-3.87 -6.62,-3.87 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.372,0 0.203,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="517"
+                                android:startOffset="0"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/lock_ls_to_aod.xml b/packages/SystemUI/res-keyguard/drawable/lock_ls_to_aod.xml
new file mode 100644
index 0000000..8a728ee
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/lock_ls_to_aod.xml
@@ -0,0 +1,151 @@
+<?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.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+                 xmlns:android="http://schemas.android.com/apk/res/android">
+    <aapt:attr name="android:drawable">
+        <vector android:height="65dp"
+                android:width="46dp"
+                android:viewportHeight="65"
+                android:viewportWidth="46">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G"
+                       android:translateX="23"
+                       android:translateY="32.125">
+                    <path android:name="_R_G_L_2_G_D_0_P_0"
+                          android:fillColor="#FF000000"
+                          android:fillAlpha="1"
+                          android:fillType="nonZero"
+                          android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c "/>
+                </group>
+                <group android:name="_R_G_L_1_G"
+                       android:translateX="23"
+                       android:translateY="32.125">
+                    <path android:name="_R_G_L_1_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="1"
+                          android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c "/>
+                </group>
+                <group android:name="_R_G_L_0_G"
+                       android:translateX="23"
+                       android:translateY="32.125">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="1"
+                          android:pathData=" M5.88 -3.87 C5.88,-3.87 5.88,-10.2 5.88,-10.2 C5.88,-13.54 3.08,-16.25 -0.37,-16.25 C-3.83,-16.25 -6.62,-13.54 -6.62,-10.2 C-6.62,-10.2 -6.62,-3.87 -6.62,-3.87 "/>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c "
+                                android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="2"
+                                android:valueTo="1.5"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.38,0 0.274,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c "
+                                android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeWidth"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="2"
+                                android:valueTo="1.5"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.38,0 0.274,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="333"
+                                android:startOffset="0"
+                                android:valueFrom="M5.88 -3.87 C5.88,-3.87 5.88,-10.2 5.88,-10.2 C5.88,-13.54 3.08,-16.25 -0.37,-16.25 C-3.83,-16.25 -6.62,-13.54 -6.62,-10.2 C-6.62,-10.2 -6.62,-3.87 -6.62,-3.87 "
+                                android:valueTo="M4.38 -2.62 C4.38,-2.62 4.38,-7.1 4.38,-7.1 C4.38,-9.46 2.42,-11.37 0,-11.37 C-2.42,-11.37 -4.37,-9.46 -4.37,-7.1 C-4.37,-7.1 -4.37,-2.62 -4.37,-2.62 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="517"
+                                android:startOffset="0"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
diff --git a/packages/SystemUI/res/anim/lock_to_unlock.xml b/packages/SystemUI/res-keyguard/drawable/lock_to_unlock.xml
similarity index 91%
rename from packages/SystemUI/res/anim/lock_to_unlock.xml
rename to packages/SystemUI/res-keyguard/drawable/lock_to_unlock.xml
index 76f7a05..ab7e9d9 100644
--- a/packages/SystemUI/res/anim/lock_to_unlock.xml
+++ b/packages/SystemUI/res-keyguard/drawable/lock_to_unlock.xml
@@ -21,7 +21,7 @@
           <group android:name="_R_G_L_2_G_N_10_T_1" android:translateX="50.25" android:translateY="61">
             <group android:name="_R_G_L_2_G_N_10_T_0" android:translateX="-13.75" android:translateY="-7.5">
               <group android:name="_R_G_L_2_G" android:translateX="-0.375" android:translateY="-22.375">
-                <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c " />
+                <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#FF000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c " />
               </group>
             </group>
           </group>
@@ -30,7 +30,7 @@
           <group android:name="_R_G_L_1_G_N_10_T_1" android:translateX="50.25" android:translateY="61">
             <group android:name="_R_G_L_1_G_N_10_T_0" android:translateX="-13.75" android:translateY="-7.5">
               <group android:name="_R_G_L_1_G" android:translateX="5" android:translateY="-22.5">
-                <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " />
+                <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#FF000000" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " />
               </group>
             </group>
           </group>
@@ -39,7 +39,7 @@
           <group android:name="_R_G_L_0_G_N_10_T_1" android:translateX="50.25" android:translateY="61">
             <group android:name="_R_G_L_0_G_N_10_T_0" android:translateX="-13.75" android:translateY="-7.5">
               <group android:name="_R_G_L_0_G" android:translateX="11" android:translateY="-0.25" android:pivotX="2.75" android:pivotY="2.75" android:scaleX="1" android:scaleY="1">
-                <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#b7f29f" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c " />
+                <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#FF000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c " />
               </group>
             </group>
           </group>
diff --git a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
new file mode 100644
index 0000000..7f0f68f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
@@ -0,0 +1,82 @@
+<?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
+  -->
+<animated-selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <!--
+        State corresponds with the following icons:
+            state_first => lock icon
+            state_middle => fingerprint icon
+            state_last => unlocked icon
+
+        state_single
+            = true => AOD
+            = false => LS
+    -->
+
+    <item
+        android:id="@+id/locked"
+        android:drawable="@drawable/ic_lock"
+        android:state_first="true"
+        android:state_single="false"/>
+
+    <item
+        android:id="@+id/locked_fp"
+        android:state_middle="true"
+        android:state_single="false"
+        android:drawable="@drawable/ic_fingerprint" />
+
+    <item
+        android:id="@+id/unlocked"
+        android:state_last="true"
+        android:state_single="false"
+        android:drawable="@drawable/ic_unlocked" />
+
+    <item
+        android:id="@+id/locked_aod"
+        android:state_first="true"
+        android:state_single="true"
+        android:drawable="@drawable/ic_lock_aod" />
+
+    <item
+        android:id="@+id/no_icon"
+        android:drawable="@color/transparent" />
+
+    <transition
+        android:fromId="@id/locked"
+        android:toId="@id/unlocked"
+        android:drawable="@drawable/lock_to_unlock" />
+
+    <transition
+        android:fromId="@id/locked_fp"
+        android:toId="@id/unlocked"
+        android:drawable="@drawable/fp_to_unlock" />
+
+    <transition
+        android:fromId="@id/unlocked"
+        android:toId="@id/locked_fp"
+        android:drawable="@drawable/unlock_to_fp" />
+
+    <transition
+        android:fromId="@id/locked_aod"
+        android:toId="@id/locked"
+        android:drawable="@drawable/lock_aod_to_ls" />
+
+    <transition
+        android:fromId="@id/locked"
+        android:toId="@id/locked_aod"
+        android:drawable="@drawable/lock_ls_to_aod" />
+</animated-selector>
diff --git a/packages/SystemUI/res-keyguard/drawable/unlock_to_fp.xml b/packages/SystemUI/res-keyguard/drawable/unlock_to_fp.xml
new file mode 100644
index 0000000..620c71a
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlock_to_fp.xml
@@ -0,0 +1,298 @@
+<?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
+  -->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+                 xmlns:android="http://schemas.android.com/apk/res/android">
+    <aapt:attr name="android:drawable">
+        <vector android:height="65dp"
+                android:width="46dp"
+                android:viewportHeight="65"
+                android:viewportWidth="46">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G"
+                       android:translateX="3.75"
+                       android:translateY="8.25">
+                    <path android:name="_R_G_L_1_G_D_0_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="1"
+                          android:pathData=" M30.64 30.14 C30.64,30.14 30.64,38.14 30.64,38.14 C30.64,38.77 30.36,39.32 29.91,39.69 C29.57,39.97 29.12,40.14 28.64,40.14 C28.64,40.14 10.14,40.14 10.14,40.14 C9.04,40.14 8.14,39.25 8.14,38.14 C8.14,38.14 8.14,30.14 8.14,30.14 "/>
+                    <path android:name="_R_G_L_1_G_D_1_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="0"
+                          android:pathData=" M19.42 31.53 C18.15,31.52 18.11,30.33 18.11,30.33 C18.11,29.59 18.66,28.98 19.4,28.98 C20.13,28.98 20.69,29.59 20.69,30.33 C20.69,30.33 20.69,30.37 20.69,30.37 C20.69,30.64 20.49,30.87 20.25,30.87 C20.07,30.87 19.91,30.74 19.84,30.55 C19.84,30.55 19.69,30.14 19.69,30.14 C19.63,29.94 19.46,29.82 19.28,29.82 C19.04,29.82 18.61,30.02 18.61,30.29 C18.61,30.43 18.6,30.75 18.76,31.03 C18.87,31.21 19.21,31.77 19.96,31.41 C20.69,31.01 20.69,30.34 20.69,30.34 "/>
+                    <path android:name="_R_G_L_1_G_D_2_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="1"
+                          android:pathData=" M8.14 30.22 C8.14,30.22 8.14,22.22 8.14,22.22 C8.14,21.71 8.33,21.25 8.64,20.9 C9,20.48 9.54,20.22 10.14,20.22 C10.14,20.22 28.64,20.22 28.64,20.22 C29.75,20.22 30.64,21.11 30.64,22.22 C30.64,22.22 30.64,30.14 30.64,30.14 "/>
+                    <path android:name="_R_G_L_1_G_D_3_P_0"
+                          android:strokeColor="#FF000000"
+                          android:strokeLineCap="round"
+                          android:strokeLineJoin="round"
+                          android:strokeWidth="2"
+                          android:strokeAlpha="1"
+                          android:pathData=" M37.91 20.05 C37.91,20.05 37.89,14.16 37.89,14.16 C37.89,10.79 35.15,8.05 31.86,8.03 C28.46,8.01 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 "/>
+                </group>
+                <group android:name="_R_G_L_0_G"
+                       android:translateX="20.357"
+                       android:translateY="35.75"
+                       android:pivotX="2.75"
+                       android:pivotY="2.75"
+                       android:scaleX="1"
+                       android:scaleY="1">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:fillColor="#FF000000"
+                          android:fillAlpha="1"
+                          android:fillType="nonZero"
+                          android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c "/>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="183"
+                                android:startOffset="0"
+                                android:valueFrom="M30.64 30.14 C30.64,30.14 30.64,38.14 30.64,38.14 C30.64,38.77 30.36,39.32 29.91,39.69 C29.57,39.97 29.12,40.14 28.64,40.14 C28.64,40.14 10.14,40.14 10.14,40.14 C9.04,40.14 8.14,39.25 8.14,38.14 C8.14,38.14 8.14,30.14 8.14,30.14 "
+                                android:valueTo="M30.64 30.14 C30.64,30.14 30.64,38.14 30.64,38.14 C30.64,38.77 30.36,39.32 29.91,39.69 C29.57,39.97 29.12,40.14 28.64,40.14 C28.64,40.14 10.14,40.14 10.14,40.14 C9.04,40.14 8.14,39.25 8.14,38.14 C8.14,38.14 8.14,30.14 8.14,30.14 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.15,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="133"
+                                android:startOffset="183"
+                                android:valueFrom="M30.64 30.14 C30.64,30.14 30.64,38.14 30.64,38.14 C30.64,38.77 30.36,39.32 29.91,39.69 C29.57,39.97 29.12,40.14 28.64,40.14 C28.64,40.14 10.14,40.14 10.14,40.14 C9.04,40.14 8.14,39.25 8.14,38.14 C8.14,38.14 8.14,30.14 8.14,30.14 "
+                                android:valueTo="M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.15,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="strokeAlpha"
+                                android:duration="183"
+                                android:startOffset="0"
+                                android:valueFrom="0"
+                                android:valueTo="0"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="strokeAlpha"
+                                android:duration="33"
+                                android:startOffset="183"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="183"
+                                android:startOffset="0"
+                                android:valueFrom="M19.42 31.53 C18.15,31.52 18.11,30.33 18.11,30.33 C18.11,29.59 18.66,28.98 19.4,28.98 C20.13,28.98 20.69,29.59 20.69,30.33 C20.69,30.33 20.69,30.37 20.69,30.37 C20.69,30.64 20.49,30.87 20.25,30.87 C20.07,30.87 19.91,30.74 19.84,30.55 C19.84,30.55 19.69,30.14 19.69,30.14 C19.63,29.94 19.46,29.82 19.28,29.82 C19.04,29.82 18.61,30.02 18.61,30.29 C18.61,30.43 18.6,30.75 18.76,31.03 C18.87,31.21 19.21,31.77 19.96,31.41 C20.69,31.01 20.69,30.34 20.69,30.34 "
+                                android:valueTo="M19.42 31.53 C18.15,31.52 18.11,30.33 18.11,30.33 C18.11,29.59 18.66,28.98 19.4,28.98 C20.13,28.98 20.69,29.59 20.69,30.33 C20.69,30.33 20.69,30.37 20.69,30.37 C20.69,30.64 20.49,30.87 20.25,30.87 C20.07,30.87 19.91,30.74 19.84,30.55 C19.84,30.55 19.69,30.14 19.69,30.14 C19.63,29.94 19.46,29.82 19.28,29.82 C19.04,29.82 18.61,30.02 18.61,30.29 C18.61,30.43 18.6,30.75 18.76,31.03 C18.87,31.21 19.21,31.77 19.96,31.41 C20.69,31.01 20.69,30.34 20.69,30.34 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.15,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="133"
+                                android:startOffset="183"
+                                android:valueFrom="M19.42 31.53 C18.15,31.52 18.11,30.33 18.11,30.33 C18.11,29.59 18.66,28.98 19.4,28.98 C20.13,28.98 20.69,29.59 20.69,30.33 C20.69,30.33 20.69,30.37 20.69,30.37 C20.69,30.64 20.49,30.87 20.25,30.87 C20.07,30.87 19.91,30.74 19.84,30.55 C19.84,30.55 19.69,30.14 19.69,30.14 C19.63,29.94 19.46,29.82 19.28,29.82 C19.04,29.82 18.61,30.02 18.61,30.29 C18.61,30.43 18.6,30.75 18.76,31.03 C18.87,31.21 19.21,31.77 19.96,31.41 C20.69,31.01 20.69,30.34 20.69,30.34 "
+                                android:valueTo="M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.15,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_2_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="183"
+                                android:startOffset="0"
+                                android:valueFrom="M8.14 30.22 C8.14,30.22 8.14,22.22 8.14,22.22 C8.14,21.71 8.33,21.25 8.64,20.9 C9,20.48 9.54,20.22 10.14,20.22 C10.14,20.22 28.64,20.22 28.64,20.22 C29.75,20.22 30.64,21.11 30.64,22.22 C30.64,22.22 30.64,30.14 30.64,30.14 "
+                                android:valueTo="M8.14 30.22 C8.14,30.22 8.14,22.22 8.14,22.22 C8.14,21.71 8.33,21.25 8.64,20.9 C9,20.48 9.54,20.22 10.14,20.22 C10.14,20.22 28.64,20.22 28.64,20.22 C29.75,20.22 30.64,21.11 30.64,22.22 C30.64,22.22 30.64,30.14 30.64,30.14 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.15,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="133"
+                                android:startOffset="183"
+                                android:valueFrom="M8.14 30.22 C8.14,30.22 8.14,22.22 8.14,22.22 C8.14,21.71 8.33,21.25 8.64,20.9 C9,20.48 9.54,20.22 10.14,20.22 C10.14,20.22 28.64,20.22 28.64,20.22 C29.75,20.22 30.64,21.11 30.64,22.22 C30.64,22.22 30.64,30.14 30.64,30.14 "
+                                android:valueTo="M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.15,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="150"
+                                android:startOffset="0"
+                                android:valueFrom="M37.91 20.05 C37.91,20.05 37.89,14.16 37.89,14.16 C37.89,10.79 35.15,8.05 31.86,8.03 C28.46,8.01 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 "
+                                android:valueTo="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.261,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="33"
+                                android:startOffset="150"
+                                android:valueFrom="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 "
+                                android:valueTo="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.123,0 0.261,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="pathData"
+                                android:duration="133"
+                                android:startOffset="183"
+                                android:valueFrom="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 "
+                                android:valueTo="M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 "
+                                android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.123,0 0.15,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="fillAlpha"
+                                android:duration="200"
+                                android:startOffset="0"
+                                android:valueFrom="1"
+                                android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="fillAlpha"
+                                android:duration="17"
+                                android:startOffset="200"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="scaleX"
+                                android:duration="183"
+                                android:startOffset="0"
+                                android:valueFrom="1"
+                                android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.596,0 0.018,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY"
+                                android:duration="183"
+                                android:startOffset="0"
+                                android:valueFrom="1"
+                                android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.596,0 0.018,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleX"
+                                android:duration="67"
+                                android:startOffset="183"
+                                android:valueFrom="1"
+                                android:valueTo="1.4186600000000003"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.596,0 0.018,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY"
+                                android:duration="67"
+                                android:startOffset="183"
+                                android:valueFrom="1"
+                                android:valueTo="1.4186600000000003"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.596,0 0.018,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="translateX"
+                                android:duration="433"
+                                android:startOffset="0"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
diff --git a/packages/SystemUI/res-product/values-te/strings.xml b/packages/SystemUI/res-product/values-te/strings.xml
index 1773f90..511e095 100644
--- a/packages/SystemUI/res-product/values-te/strings.xml
+++ b/packages/SystemUI/res-product/values-te/strings.xml
@@ -38,8 +38,8 @@
     <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పు ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలలో విఫలమైతే, కార్యాలయ ప్రొఫైల్ తీసివేయబడుతుంది, దీని వలన ప్రొఫైల్ డేటా మొత్తం తొలగించబడుతుంది."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"మీరు టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పు ప్రయత్నాలు చేశారు. కార్యాలయ ప్రొఫైల్ తీసివేయబడుతుంది, దీని వలన ప్రొఫైల్ డేటా మొత్తం తొలగించబడుతుంది."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పు ప్రయత్నాలు చేశారు. కార్యాలయ ప్రొఫైల్ తీసివేయబడుతుంది, దీని వలన ప్రొఫైల్ డేటా మొత్తం తొలగించబడుతుంది."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలలో విఫలమైతే, మీరు ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టాబ్లెట్‌ను అన్‌లాక్ చేయాల్సి వస్తుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలలో విఫలమైతే, మీరు ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్‌ను అన్‌లాక్ చేయాల్సి వస్తుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలలో విఫలమైతే, మీరు ఈమెయిల్‌ ఖాతాను ఉపయోగించి మీ టాబ్లెట్‌ను అన్‌లాక్ చేయాల్సి వస్తుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> ప్రయత్నాలలో విఫలమైతే, మీరు ఈమెయిల్‌ ఖాతాను ఉపయోగించి మీ ఫోన్‌ను అన్‌లాక్ చేయాల్సి వస్తుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"మరిన్ని ఆప్షన్‌ల కోసం మీ ఫోన్‌ను అన్‌లాక్ చేయండి"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"మరిన్ని ఆప్షన్‌ల కోసం మీ టాబ్లెట్‌ను అన్‌లాక్ చేయండి"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"మరిన్ని ఆప్షన్‌ల కోసం మీ పరికరాన్ని అన్‌లాక్ చేయండి"</string>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index c1d7308b..79ac737 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -323,6 +323,46 @@
                 </FrameLayout>
             </LinearLayout>
 
+            <LinearLayout
+                android:id="@+id/wifi_scan_notify_layout"
+                style="@style/InternetDialog.Network"
+                android:orientation="vertical"
+                android:layout_height="wrap_content"
+                android:paddingBottom="4dp"
+                android:clickable="false"
+                android:focusable="false">
+
+                <LinearLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:minWidth="56dp"
+                    android:gravity="start|top"
+                    android:orientation="horizontal"
+                    android:paddingEnd="12dp"
+                    android:paddingTop="16dp"
+                    android:paddingBottom="4dp">
+                    <ImageView
+                        android:src="@drawable/ic_info_outline"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:tint="?android:attr/textColorTertiary"/>
+                </LinearLayout>
+
+                <LinearLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical">
+                    <TextView
+                        android:id="@+id/wifi_scan_notify_text"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:paddingTop="16dp"
+                        android:paddingBottom="8dp"
+                        android:textColor="?android:attr/textColorSecondary"
+                        android:clickable="true"/>
+                </LinearLayout>
+            </LinearLayout>
+
             <FrameLayout
                 android:id="@+id/done_layout"
                 android:layout_width="67dp"
diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
index 720e47b..f91ab6f 100644
--- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
@@ -31,8 +31,7 @@
         android:id="@+id/privacy_dot_left_container"
         android:layout_height="@dimen/status_bar_height"
         android:layout_width="wrap_content"
-        android:layout_marginTop="@dimen/status_bar_padding_top"
-        android:layout_marginLeft="0dp"
+        android:paddingTop="@dimen/status_bar_padding_top"
         android:layout_gravity="left|bottom"
         android:visibility="invisible" >
         <ImageView
@@ -51,12 +50,12 @@
         android:tint="#ff000000"
         android:layout_gravity="right|bottom"
         android:src="@drawable/rounded_corner_bottom"/>
+
     <FrameLayout
         android:id="@+id/privacy_dot_right_container"
         android:layout_height="@dimen/status_bar_height"
         android:layout_width="wrap_content"
-        android:layout_marginTop="@dimen/status_bar_padding_top"
-        android:layout_marginRight="0dp"
+        android:paddingTop="@dimen/status_bar_padding_top"
         android:layout_gravity="right|bottom"
         android:visibility="invisible" >
         <ImageView
diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml
index 6abe406..819a9a4e9 100644
--- a/packages/SystemUI/res/layout/rounded_corners_top.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_top.xml
@@ -29,10 +29,9 @@
 
     <FrameLayout
         android:id="@+id/privacy_dot_left_container"
-        android:layout_height="@*android:dimen/status_bar_height_portrait"
+        android:layout_height="@dimen/status_bar_height"
         android:layout_width="wrap_content"
-        android:layout_marginTop="@dimen/status_bar_padding_top"
-        android:layout_marginLeft="0dp"
+        android:paddingTop="@dimen/status_bar_padding_top"
         android:layout_gravity="left|top"
         android:visibility="invisible" >
         <ImageView
@@ -54,10 +53,9 @@
 
     <FrameLayout
         android:id="@+id/privacy_dot_right_container"
-        android:layout_height="@*android:dimen/status_bar_height_portrait"
+        android:layout_height="@dimen/status_bar_height"
         android:layout_width="wrap_content"
-        android:layout_marginTop="@dimen/status_bar_padding_top"
-        android:layout_marginRight="0dp"
+        android:paddingTop="@dimen/status_bar_padding_top"
         android:layout_gravity="right|top"
         android:visibility="invisible" >
         <ImageView
@@ -67,8 +65,6 @@
             android:layout_gravity="center_vertical|left"
             android:src="@drawable/system_animation_ongoing_dot"
             android:visibility="visible" />
-
     </FrameLayout>
 
-
 </com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index e02a1767..b28cb2f 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -81,6 +81,7 @@
 
     <!-- Keyguard messages -->
     <LinearLayout
+        android:id="@+id/keyguard_message_area_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 427af68..f310a7f 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan programme en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporing-instellings verander. "<annotation id="link">"Verander"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil die volgende teël by Kitsinstellings voeg"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Moenie teël byvoeg nie"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a158d7f..ffdfba6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi ለአሁን በራስ-ሰር አይገናኝም"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"አውታረ መረቦችን ለመቀየር፣ የኢተርኔት ግንኙነት ያቋርጡ"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"የመሣሪያ ተሞክሮን ለማሻሻል፣ መተግበሪያዎች እና አገልግሎቶች አሁንም በማንኛውም ጊዜ የWi-Fi አውታረ መረቦችን መቃኘት ይችላሉ፣ Wi-Fi ጠፍቶ ቢሆንም እንኳ። ይህንን በ Wi‑Fi ቅኝት ቅንብሮች ውስጥ መቀየር ይችላሉ። "<annotation id="link">"ቀይር"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> የሚከተለውን ሰቅ ወደ ፈጣን ቅንብሮች ማከል ይፈልጋል"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ሰቅ አክል"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ሰቅ አታክል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ae4125b..48e101b 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1203,6 +1203,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"‏لن يتم الاتصال بشبكة Wi-Fi تلقائيًا في الوقت الحالي."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"للتبديل بين الشبكات، يجب فصل إيثرنت."</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"‏لتحسين تجربتك على الجهاز، يظل بإمكان التطبيقات والخدمات البحث عن شبكات Wi‑Fi في أي وقت، حتى عند إيقاف شبكة Wi‑Fi. وبإمكانك تغيير هذا الخيار في إعدادات البحث عن شبكات Wi-Fi. "<annotation id="link">"تغيير"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"يريد تطبيق <xliff:g id="APPNAME">%1$s</xliff:g> إضافة المربّع التالي إلى \"الإعدادات السريعة\""</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"إضافة مربّع"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"عدم إضافة مربّع"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 9da6246..f9cb599 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -24,19 +24,19 @@
     <string name="status_bar_no_notifications_title" msgid="7812479124981107507">"কোনো জাননী নাই"</string>
     <string name="status_bar_ongoing_events_title" msgid="3986169317496615446">"চলিত"</string>
     <string name="status_bar_latest_events_title" msgid="202755896454005436">"জাননীসমূহ"</string>
-    <string name="battery_low_title" msgid="6891106956328275225">"বেটাৰি অতি সোনকালে শেষ হ\'ব পাৰে"</string>
+    <string name="battery_low_title" msgid="6891106956328275225">"বেটাৰী অতি সোনকালে শেষ হ\'ব পাৰে"</string>
     <string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> বাকী আছে"</string>
     <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> অৱশিষ্ট আছে, আপোনাৰ ব্যৱহাৰক ভিত্তি কৰি প্ৰায় <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
     <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> অৱশিষ্ট আছে, প্ৰায় <xliff:g id="TIME">%2$s</xliff:g> বাকী"</string>
-    <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"<xliff:g id="PERCENTAGE">%s</xliff:g> বাকী আছে। বেটাৰি সঞ্চয়কাৰী অন হৈ আছে।"</string>
+    <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"<xliff:g id="PERCENTAGE">%s</xliff:g> বাকী আছে। বেটাৰী সঞ্চয়কাৰী অন হৈ আছে।"</string>
     <string name="invalid_charger" msgid="4370074072117767416">"ইউএছবি জৰিয়তে চ্চাৰ্জ কৰিব নোৱাৰি। আপোনাৰ ডিভাইচৰ লগত পোৱা চ্চাৰ্জাৰটো ব্যৱহাৰ কৰক।"</string>
     <string name="invalid_charger_title" msgid="938685362320735167">"ইউএছবি জৰিয়তে চ্চাৰ্জ কৰিব নোৱাৰি"</string>
     <string name="invalid_charger_text" msgid="2339310107232691577">"আপোনাৰ ডিভাইচৰ লগত পোৱা চ্চাৰ্জাৰটো ব্যৱহাৰ কৰক।"</string>
     <string name="battery_low_why" msgid="2056750982959359863">"ছেটিং"</string>
-    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"বেটাৰি সঞ্চয়কাৰী অন কৰেনে?"</string>
+    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"বেটাৰী সঞ্চয়কাৰী অন কৰেনে?"</string>
     <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"বেটাৰী সঞ্চয়কাৰীৰ বিষয়ে"</string>
     <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"অন কৰক"</string>
-    <string name="battery_saver_start_action" msgid="4553256017945469937">"বেটাৰি সঞ্চয়কাৰী অন কৰক"</string>
+    <string name="battery_saver_start_action" msgid="4553256017945469937">"বেটাৰী সঞ্চয়কাৰী অন কৰক"</string>
     <string name="status_bar_settings_settings_button" msgid="534331565185171556">"ছেটিং"</string>
     <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"ৱাই-ফাই"</string>
     <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string>
@@ -193,11 +193,11 @@
     <string name="accessibility_compatibility_zoom_example" msgid="2617218726091234073">"স্ক্ৰীনৰ আকাৰ ডাঙৰ কৰিবলৈ জুম কৰক।"</string>
     <string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ব্লুটুথ সংযোগ হ’ল।"</string>
     <string name="accessibility_bluetooth_disconnected" msgid="7195823280221275929">"ব্লুটুথ সংযোগ বিচ্ছিন্ন কৰা হ’ল।"</string>
-    <string name="accessibility_no_battery" msgid="3789287732041910804">"বেটাৰি শেষ"</string>
-    <string name="accessibility_battery_one_bar" msgid="8868347318237585329">"বেটাৰিৰ এডাল দণ্ড।"</string>
-    <string name="accessibility_battery_two_bars" msgid="7895789999668425551">"বেটাৰিৰ দুডাল দণ্ড।"</string>
-    <string name="accessibility_battery_three_bars" msgid="118341923832368291">"বেটাৰিৰ তিনিডাল দণ্ড।"</string>
-    <string name="accessibility_battery_full" msgid="1480463938961288494">"বেটাৰি পূৰাকৈ চ্চাৰ্জ হৈছে।"</string>
+    <string name="accessibility_no_battery" msgid="3789287732041910804">"বেটাৰী শেষ"</string>
+    <string name="accessibility_battery_one_bar" msgid="8868347318237585329">"বেটাৰীৰ এডাল দণ্ড।"</string>
+    <string name="accessibility_battery_two_bars" msgid="7895789999668425551">"বেটাৰীৰ দুডাল দণ্ড।"</string>
+    <string name="accessibility_battery_three_bars" msgid="118341923832368291">"বেটাৰীৰ তিনিডাল দণ্ড।"</string>
+    <string name="accessibility_battery_full" msgid="1480463938961288494">"বেটাৰী পূৰাকৈ চাৰ্জ হৈছে।"</string>
     <string name="accessibility_battery_unknown" msgid="1807789554617976440">"বেটাৰীৰ চাৰ্জৰ শতাংশ অজ্ঞাত।"</string>
     <string name="accessibility_wifi_name" msgid="4863440268606851734">"<xliff:g id="WIFI">%s</xliff:g>ৰ লগত সংযোগ কৰা হ’ল।"</string>
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>ৰ লগত সংযোগ কৰা হ’ল।"</string>
@@ -229,10 +229,10 @@
     <string name="accessibility_airplane_mode" msgid="1899529214045998505">"এয়াৰপ্লে’ন ম’ড।"</string>
     <string name="accessibility_vpn_on" msgid="8037549696057288731">"ভিপিএন অন অৱস্থাত আছে।"</string>
     <string name="accessibility_no_sims" msgid="5711270400476534667">"কোনো ছিম কাৰ্ড নাই"</string>
-    <string name="accessibility_battery_details" msgid="6184390274150865789">"বেটাৰিৰ বিৱৰণসমূহ খোলক"</string>
-    <string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> শতাংশ বেটাৰি।"</string>
+    <string name="accessibility_battery_details" msgid="6184390274150865789">"বেটাৰীৰ বিৱৰণসমূহ খোলক"</string>
+    <string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> শতাংশ বেটাৰী।"</string>
     <string name="accessibility_battery_level_with_estimate" msgid="4843119982547599452">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰী <xliff:g id="PERCENTAGE">%1$s</xliff:g> শতাংশ, প্ৰায় <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
-    <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"বেটাৰি চাৰ্জ হৈ আছে, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> শতাংশ।"</string>
+    <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"বেটাৰী চাৰ্জ হৈ আছে, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> শতাংশ।"</string>
     <string name="accessibility_settings_button" msgid="2197034218538913880">"ছিষ্টেমৰ ছেটিং৷"</string>
     <string name="accessibility_notifications_button" msgid="3960913924189228831">"জাননীসমূহ।"</string>
     <string name="accessibility_overflow_action" msgid="8555835828182509104">"সকলো জাননীবোৰ চাওক"</string>
@@ -258,7 +258,7 @@
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ৱাই-ফাই অফ কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"ৱাই-ফাই অন কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"ম’বাইল <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
-    <string name="accessibility_quick_settings_battery" msgid="533594896310663853">"বেটাৰি <xliff:g id="STATE">%s</xliff:g>।"</string>
+    <string name="accessibility_quick_settings_battery" msgid="533594896310663853">"বেটাৰী <xliff:g id="STATE">%s</xliff:g>।"</string>
     <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"এয়াৰপ্লেইন ম\'ড অফ হৈ আছে৷"</string>
     <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"এয়াৰপ্লেইন ম\'ড অন হৈ আছে৷"</string>
     <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"এয়াৰপ্লেইন ম\'ড অফ কৰা হ’ল।"</string>
@@ -338,7 +338,7 @@
     <string name="quick_settings_bluetooth_multiple_devices_label" msgid="6595808498429809855">"ব্লুটুথ (<xliff:g id="NUMBER">%d</xliff:g>টা ডিভাইচ)"</string>
     <string name="quick_settings_bluetooth_off_label" msgid="6375098046500790870">"ব্লুটুথ বন্ধ অৱস্থাত আছে"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"কোনো যোৰা লগোৱা ডিভাইচ উপলব্ধ নহয়।"</string>
-    <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰি <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string>
@@ -391,7 +391,7 @@
     <string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন কৰা হ’ল"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"বন্ধ কৰক"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"সংযোগ কৰা হ’ল"</string>
-    <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"সংযুক্ত, বেটাৰি <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"সংযুক্ত, বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="2381969772953268809">"সংযোগ কৰি থকা হৈছে..."</string>
     <string name="quick_settings_tethering_label" msgid="5257299852322475780">"টেডাৰ কৰি থকা হৈছে"</string>
     <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"হটস্পট"</string>
@@ -504,9 +504,9 @@
     <string name="user_remove_user_title" msgid="9124124694835811874">"ব্যৱহাৰকাৰীক আঁতৰাবনে?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"এই ব্যৱহাৰকাৰীৰ সকলো এপ্ আৰু ডেটা মচা হ\'ব।"</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"আঁতৰাওক"</string>
-    <string name="battery_saver_notification_title" msgid="8419266546034372562">"বেটাৰি সঞ্চয়কাৰী অন হৈ আছে"</string>
+    <string name="battery_saver_notification_title" msgid="8419266546034372562">"বেটাৰী সঞ্চয়কাৰী অন হৈ আছে"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"কাৰ্যদক্ষতা আৰু নেপথ্য ডেটা হ্ৰাস কৰে"</string>
-    <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"বেটাৰি সঞ্চয়কাৰী অফ কৰক"</string>
+    <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"বেটাৰী সঞ্চয়কাৰী অফ কৰক"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>এ আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকর্ডিং অথবা কাষ্টিঙৰ সময়ত আপোনাৰ ডিভাইচত প্লে\' কৰা সকলো তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱর্ড, পৰিশোধৰ সবিশেষ, ফট\', বার্তাসমূহ আৰু আপুনি প্লে\' কৰা অডিঅ\'ৰ দৰে তথ্য অন্তর্ভুক্ত হয়।"</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"এই সুবিধাটো প্ৰদান কৰা সেৱাটোৱে আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকর্ডিং অথবা কাষ্টিংৰ সময়ত আপোনাৰ ডিভাইচত প্লে\' কৰা সকলো তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱর্ড, পৰিশোধৰ সবিশেষ, ফট\', বার্তাসমূহ আৰু আপুনি প্লে\' কৰা অডিঅ\'ৰ দৰে তথ্য অন্তর্ভুক্ত হয়।"</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ৰেকর্ডিং অথবা কাষ্টিং আৰম্ভ কৰিবনে?"</string>
@@ -657,8 +657,8 @@
     <string name="output_service_wifi" msgid="9003667810868222134">"ৱাই-ফাই"</string>
     <string name="output_service_bt_wifi" msgid="7186882540475524124">"ব্লুটুথ আৰু ৱাই-ফাই"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
-    <string name="show_battery_percentage" msgid="6235377891802910455">"সংযুক্ত বেটাৰিৰ কিমান শতাংশ বাকী আছে দেখুওৱাক"</string>
-    <string name="show_battery_percentage_summary" msgid="9053024758304102915">"চাৰ্জ হৈ নথকা অৱস্থাত বেটাৰি কিমান শতাংশ বাকী স্থিতি দণ্ডৰ ভিতৰত দেখুৱাওক"</string>
+    <string name="show_battery_percentage" msgid="6235377891802910455">"সংযুক্ত বেটাৰীৰ কিমান শতাংশ বাকী আছে দেখুওৱাক"</string>
+    <string name="show_battery_percentage_summary" msgid="9053024758304102915">"চাৰ্জ হৈ নথকা অৱস্থাত বেটাৰী কিমান শতাংশ বাকী স্থিতি দণ্ডৰ ভিতৰত দেখুৱাওক"</string>
     <string name="quick_settings" msgid="6211774484997470203">"ক্ষিপ্ৰ ছেটিং"</string>
     <string name="status_bar" msgid="4357390266055077437">"স্থিতি দণ্ড"</string>
     <string name="overview" msgid="3522318590458536816">"অৱলোকন"</string>
@@ -696,7 +696,7 @@
     <string name="remove_from_settings_prompt" msgid="551565437265615426">"ছেটিঙৰ পৰা System UI Tuner আঁতৰাই ইয়াৰ সুবিধাসমূহ ব্যৱহাৰ কৰাটো বন্ধ কৰিবনে?"</string>
     <string name="activity_not_found" msgid="8711661533828200293">"আপোনাৰ ডিভাইচত এপ্লিকেশ্বনটো ইনষ্টল কৰা হোৱা নাই"</string>
     <string name="clock_seconds" msgid="8709189470828542071">"ঘড়ীৰ ছেকেণ্ড দেখুৱাওক"</string>
-    <string name="clock_seconds_desc" msgid="2415312788902144817">"স্থিতি দণ্ডত ঘড়ীৰ ছেকেণ্ড দেখুৱাওক। এই কার্যই বেটাৰিৰ অৱস্থাত প্ৰভাৱ পেলাব পাৰে।"</string>
+    <string name="clock_seconds_desc" msgid="2415312788902144817">"স্থিতি দণ্ডত ঘড়ীৰ ছেকেণ্ড দেখুৱাওক। এই কার্যই বেটাৰীৰ জীৱনকালত প্ৰভাৱ পেলাব পাৰে।"</string>
     <string name="qs_rearrange" msgid="484816665478662911">"ক্ষিপ্ৰ ছেটিং পুনৰ সজাওক"</string>
     <string name="show_brightness" msgid="6700267491672470007">"দ্ৰুত ছেটিঙত উজ্জ্বলতা দেখুৱাওক"</string>
     <string name="experimental" msgid="3549865454812314826">"পৰীক্ষামূলক"</string>
@@ -802,8 +802,8 @@
       <item quantity="one"> %d মিনিট</item>
       <item quantity="other"> %d মিনিট</item>
     </plurals>
-    <string name="battery_panel_title" msgid="5931157246673665963">"বেটাৰিৰ ব্যৱহাৰ"</string>
-    <string name="battery_detail_charging_summary" msgid="8821202155297559706">"চ্চাৰ্জ কৰি থকাৰ সময়ত বেটাৰি সঞ্চয়কাৰী উপলব্ধ নহয়।"</string>
+    <string name="battery_panel_title" msgid="5931157246673665963">"বেটাৰীৰ ব্যৱহাৰ"</string>
+    <string name="battery_detail_charging_summary" msgid="8821202155297559706">"চাৰ্জ কৰি থকাৰ সময়ত বেটাৰী সঞ্চয়কাৰী উপলব্ধ নহয়।"</string>
     <string name="battery_detail_switch_title" msgid="6940976502957380405">"বেটাৰী সঞ্চয়কাৰী"</string>
     <string name="battery_detail_switch_summary" msgid="3668748557848025990">"কাৰ্যদক্ষতা আৰু নেপথ্য ডেটা হ্ৰাস কৰে"</string>
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বুটাম"</string>
@@ -853,7 +853,7 @@
     <string name="volume_and_do_not_disturb" msgid="502044092739382832">"অসুবিধা নিদিব"</string>
     <string name="volume_dnd_silent" msgid="4154597281458298093">"ভলিউম বুটামসমূহৰ শ্বৰ্টকাট"</string>
     <string name="volume_up_silent" msgid="1035180298885717790">"ভলিউম বঢ়োৱা বুটাম ব্যৱহাৰ কৰি অসুবিধা নিদিব নিষ্ক্ৰিয় কৰক"</string>
-    <string name="battery" msgid="769686279459897127">"বেটাৰি"</string>
+    <string name="battery" msgid="769686279459897127">"বেটাৰী"</string>
     <string name="clock" msgid="8978017607326790204">"ঘড়ী"</string>
     <string name="headset" msgid="4485892374984466437">"হেডছেট"</string>
     <string name="accessibility_long_click_tile" msgid="210472753156768705">"ছেটিং খোলক"</string>
@@ -962,7 +962,7 @@
     <string name="tuner_menu" msgid="363690665924769420">"মেনু"</string>
     <string name="tuner_app" msgid="6949280415826686972">"<xliff:g id="APP">%1$s</xliff:g> এপ্"</string>
     <string name="notification_channel_alerts" msgid="3385787053375150046">"সতৰ্কবার্তাসমূহ"</string>
-    <string name="notification_channel_battery" msgid="9219995638046695106">"বেটাৰি"</string>
+    <string name="notification_channel_battery" msgid="9219995638046695106">"বেটাৰী"</string>
     <string name="notification_channel_screenshot" msgid="7665814998932211997">"স্ক্ৰীণশ্বটসমূহ"</string>
     <string name="notification_channel_general" msgid="4384774889645929705">"সাধাৰণ বার্তাসমূহ"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"ষ্ট\'ৰেজ"</string>
@@ -986,7 +986,7 @@
     <string name="qs_dnd_keep" msgid="3829697305432866434">"Keep"</string>
     <string name="qs_dnd_replace" msgid="7712119051407052689">"সলনি কৰক"</string>
     <string name="running_foreground_services_title" msgid="5137313173431186685">"নেপথ্যত চলি থকা এপসমূহ"</string>
-    <string name="running_foreground_services_msg" msgid="3009459259222695385">"বেটাৰি আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে বিশদভাৱে জানিবলৈ টিপক"</string>
+    <string name="running_foreground_services_msg" msgid="3009459259222695385">"বেটাৰী আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে সবিশেষ জানিবলৈ টিপক"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ম’বাইল ডেটা অফ কৰিবনে?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"আপুনি <xliff:g id="CARRIER">%s</xliff:g>ৰ জৰিয়তে ডেটা সংযোগ বা ইণ্টাৰনেট সংযোগ নাপাব। কেৱল ৱাই-ফাইৰ যোগেৰে ইণ্টাৰনেট উপলব্ধ হ\'ব।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"আপোনাৰ বাহক"</string>
@@ -997,11 +997,11 @@
     <string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g>ক যিকোনো এপৰ অংশ দেখুওৱাবলৈ অনুমতি দিয়ক"</string>
     <string name="slice_permission_allow" msgid="6340449521277951123">"অনুমতি দিয়ক"</string>
     <string name="slice_permission_deny" msgid="6870256451658176895">"অস্বীকাৰ কৰক"</string>
-    <string name="auto_saver_title" msgid="6873691178754086596">"বেটাৰি সঞ্চয়কাৰীৰ সময়সূচী সক্ৰিয় কৰিবলৈ টিপক"</string>
-    <string name="auto_saver_text" msgid="3214960308353838764">"বেটাৰি শেষ হোৱাৰ সম্ভাৱনা থাকিলে অন কৰক"</string>
+    <string name="auto_saver_title" msgid="6873691178754086596">"বেটাৰী সঞ্চয়কাৰীৰ সময়সূচী সক্ৰিয় কৰিবলৈ টিপক"</string>
+    <string name="auto_saver_text" msgid="3214960308353838764">"বেটাৰী শেষ হোৱাৰ সম্ভাৱনা থাকিলে অন কৰক"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"নালাগে, ধন্যবাদ"</string>
-    <string name="auto_saver_enabled_title" msgid="4294726198280286333">"বেটাৰি সঞ্চয়কাৰীৰ সময়সূচী অন কৰা অৱস্থাত আছে"</string>
-    <string name="auto_saver_enabled_text" msgid="7889491183116752719">"বেটাৰি চ্চাৰ্জৰ স্তৰ <xliff:g id="PERCENTAGE">%d</xliff:g>%%তকৈ কম হোৱাৰ লগে লগে বেটাৰি সঞ্চয়কাৰী স্বয়ংক্ৰিয়ভাৱে অন হ’ব।"</string>
+    <string name="auto_saver_enabled_title" msgid="4294726198280286333">"বেটাৰী সঞ্চয়কাৰীৰ সময়সূচী অন কৰা অৱস্থাত আছে"</string>
+    <string name="auto_saver_enabled_text" msgid="7889491183116752719">"বেটাৰী চাৰ্জৰ স্তৰ <xliff:g id="PERCENTAGE">%d</xliff:g>%%তকৈ কম হোৱাৰ লগে লগে বেটাৰী সঞ্চয়কাৰী স্বয়ংক্ৰিয়ভাৱে অন হ’ব।"</string>
     <string name="open_saver_setting_action" msgid="2111461909782935190">"ছেটিং"</string>
     <string name="auto_saver_okay_action" msgid="7815925750741935386">"বুজি পালোঁ"</string>
     <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI হীপ ডাম্প কৰক"</string>
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এতিয়া ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটৱৰ্ক সলনি কৰিবলৈ ইথাৰনেটৰ পৰা সংযোগ বিচ্ছিন্ন কৰক"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ডিভাইচ ব্যৱহাৰৰ অভিজ্ঞতা উন্নত কৰিবলৈ ৱাই-ফাই অফ থকা অৱস্থাতো এপ্ আৰু সেৱাসমূহে ৱাই-ফাই নেটৱৰ্কবোৰ স্কেন কৰিব পাৰে। আপুনি ৱাই-ফাই স্কেনিঙৰ ছেটিঙত এইটো সলনি কৰিব পাৰে। "<annotation id="link">"সলনি কৰক"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>এ ক্ষিপ্ৰ ছেটিঙত এই টাইলটো যোগ দিব বিচাৰিছে"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"টাইল যোগ দিয়ক"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"টাইল যোগ নিদিব"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 9b4b42b..72b9836 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hələlik avtomatik qoşulmayacaq"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Şəbəkəni dəyişmək üçün etherneti ayırın"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Cihaz təcrübəsini yaxşılaşdırmaq üçün Wi-Fi deaktiv olduqda belə, tətbiqlər və xidmətlər Wi-Fi şəbəkəsini axtara biləcək. Bunu Wi-Fi axtarışı ayarlarında dəyişə bilərsiniz. "<annotation id="link">"Dəyişin"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aşağıdakı mozaiki Sürətli Ayarlara əlavə etmək istəyir"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Mozaik əlavə edin"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Mozaik əlavə etməyin"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 4317599..f9468ee 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1185,6 +1185,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi trenutno ne može da se automatski poveže"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste promenili mrežu, prekinite eternet vezu"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi boljeg doživljaja uređaja, aplikacije i usluge i dalje mogu da traže WiFi mreže u bilo kom trenutku, čak i kada je WiFi isključen. To možete da promenite u podešavanjima WiFi skeniranja. "<annotation id="link">"Promenite"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi da doda sledeću pločicu u Brza podešavanja"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj pločicu"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne dodaj pločicu"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 535144f..bee7943 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Аўтаматычнае падключэнне да Wi-Fi адсутнічае"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Каб падключыцца да сетак, выключыце Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Каб палепшыць працу прылады, вы можаце дазволіць праграмам і сэрвісам шукаць сеткі Wi-Fi, нават калі Wi‑Fi выключаны. Змяніць гэты рэжым можна ў наладах пошуку сетак Wi-Fi. "<annotation id="link">"Змяніць"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> запытвае дазвол на дадаванне ў хуткія налады наступнай пліткі"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Дадаць плітку"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не дадаваць плітку"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 2f54457..e126034 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Засега Wi-Fi няма да се свързва автоматично"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За да превключите мрежите, прекъснете връзката с Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"С цел подобряване на практическата работа с устройството приложенията и услугите пак могат да сканират за Wi‑Fi мрежи по всяко време дори когато функцията за Wi‑Fi e изключена. Можете да промените съответното поведение от настройките за сканиране за Wi‑Fi. "<annotation id="link">"Промяна"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> иска да добави следния панел към бързите настройки"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Добавяне на панел"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Отмяна на добавянето"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 2dc23f0..bde0d8c 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-এর <xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%3$s</xliff:g> অ্যাপে চলছে"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>টির মধ্যে <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>টি"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"চালান"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> অ্যাপ খুলুন"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-এর <xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%3$s</xliff:g> অ্যাপে চালান"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এখন ওয়াই-ফাই নিজে থেকে কানেক্ট হবে না"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটওয়ার্ক বদলাতে ইথারনেট ডিসকানেক্ট করুন"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ডিভাইস সংক্রান্ত অভিজ্ঞতা আরও ভাল করতে, অ্যাপ ও পরিষেবা যেকোনও সময় আপনার ওয়াই-ফাই নেটওয়ার্ক স্ক্যান করতে পারবে, এমনকি ডিভাইসের ওয়াই-ফাই বন্ধ করা থাকলেও। ওয়াই-ফাই স্ক্যানিং সেটিংস থেকে আপনি এটি পরিবর্তন করতে পারবেন। "<annotation id="link">"পরিবর্তন করুন"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> নিম্নলিখিত টাইল দ্রুত সেটিংস মেনুতে যোগ করতে চায়"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"টাইল যোগ করুন"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"টাইল যোগ করবেন না"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 0fc1405..c29cc31 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1185,6 +1185,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu bilo kada skenirati WiFi mreže, čak i kada je WiFi isključen. Ovo možete promijeniti u Postavkama skeniranja WiFi mreže. "<annotation id="link">"Promijeni"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću karticu u Brze postavke"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nemoj dodati karticu"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 9d8f599..751a49d 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Per ara la Wi‑Fi no es connectarà automàticament"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per canviar de xarxa, desconnecta la connexió Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Per millorar l\'experiència del dispositiu, les aplicacions i els serveis poden cercar xarxes Wi‑Fi en qualsevol moment, fins i tot quan la Wi‑Fi estigui desactivada. Pots canviar aquesta opció a la configuració de cerca de xarxes Wi‑Fi. "<annotation id="link">"Canvia-la"</annotation>"."</string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vol afegir la icona següent a la configuració ràpida"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Afegeix la icona"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No afegeixis la icona"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 1393431..1aa9c44 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se prozatím nebude připojovat automaticky"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pokud chcete přepnout sítě, odpojte ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Za účelem lepšího fungování zařízení mohou aplikace a služby vyhledávat sítě Wi-Fi, i když je připojení Wi-Fi vypnuté. Toto chování můžete změnit v nastavení vyhledávání Wi-Fi. "<annotation id="link">"Změnit"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikace <xliff:g id="APPNAME">%1$s</xliff:g> chce do Rychlého nastavení přidat následující dlaždici"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Přidat dlaždici"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepřidávat dlaždici"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1502dea..492546f 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Ingen automatisk forbindelse til Wi-Fi i øjeblikket"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Afbryd ethernetforbindelsen for at skifte netværk"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"For at forbedre brugeroplevelsen på enheden kan apps og tjenester stadig til enhver tid scanne efter Wi‑Fi-netværk, også selvom Wi‑Fi er deaktiveret. Du kan ændre dette i indstillingerne for Wi-Fi-scanning. "<annotation id="link">"Skift indstilling"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vil gerne føje dette handlingsfelt til Kvikmenu"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tilføj handlingsfelt"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tilføj ikke felt"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index d4ebeda..085e082 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> von <xliff:g id="ARTIST_NAME">%2$s</xliff:g> wird gerade über <xliff:g id="APP_LABEL">%3$s</xliff:g> wiedergegeben"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> von <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Wiedergeben"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> öffnen"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> von <xliff:g id="ARTIST_NAME">%2$s</xliff:g> über <xliff:g id="APP_LABEL">%3$s</xliff:g> wiedergeben"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Zurzeit wird keine automatische WLAN-Verbindung hergestellt"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Trenne das Ethernetkabel, um das Netzwerk zu wechseln"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Zur Verbesserung der Gerätenutzung können Apps und Dienste weiter nach WLANs suchen, auch wenn die WLAN-Funktion deaktiviert ist. Dies lässt sich in den Einstellungen für die WLAN-Suche ändern. "<annotation id="link">"Ändern"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> möchte die folgende Kachel den Schnelleinstellungen hinzufügen"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Kachel hinzufügen"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Kachel nicht hinzu"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 576a806..b6ef245 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Δεν θα γίνεται προς το παρόν αυτόματη σύνδεση Wi-Fi."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Για εναλλαγή δικτύων, αποσυνδέστε το ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Για βελτίωση της εμπειρίας στη συσκευή, οι εφαρμογές και οι υπηρεσίες μπορούν ακόμα να εκτελούν σάρωση για δίκτυα Wi‑Fi ανά πάσα στιγμή, ακόμα και όταν το Wi‑Fi είναι απενεργοποιημένο. Μπορείτε να αλλάξετε αυτήν τη ρύθμιση στις ρυθμίσεις της Σάρωσης Wi‑Fi. "<annotation id="link">"Αλλαγή"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Η εφαρμογή <xliff:g id="APPNAME">%1$s</xliff:g> θέλει να προσθέσει το παρακάτω πλακίδιο στις Γρήγορες ρυθμίσεις"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Προσθήκη πλακιδίου"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Χωρίς προσθ. πλακιδ."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 3898159..d50402f 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 5a31735..d30be77 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 3898159..d50402f 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 3898159..d50402f 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. "<annotation id="link">"Change"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 1ec39d4..880be2b 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎Wi‑Fi won’t auto-connect for now‎‏‎‎‏‎"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎See all‎‏‎‎‏‎"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎To switch networks, disconnect ethernet‎‏‎‎‏‎"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎To improve device experience, apps and services can still scan for Wi‑Fi networks at any time, even when Wi‑Fi is off. You can change this in Wi‑Fi scanning settings. ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎Change‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ wants to add the following tile to Quick Settings‎‏‎‎‏‎"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎Add tile‎‏‎‎‏‎"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‎Do not add tile‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e8f8b36cf..f68196bf 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora, el Wi-Fi no se conectará automáticamente"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconéctate de Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para mejorar la experiencia con el dispositivo, las apps y los servicios pueden seguir buscando redes Wi-Fi en cualquier momento, incluso cuando la conexión Wi-Fi esté desactivada. Puedes cambiar este parámetro en la configuración de búsqueda de Wi-Fi. "<annotation id="link">"Cambiar"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> quiere agregar el siguiente azulejo a la Configuración rápida"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Agregar azulejo"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No agregar azulejo"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 433536a..7f4c0bc 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora no se conectará automáticamente a redes Wi-Fi"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconecta el cable Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para mejorar la experiencia con el dispositivo, las aplicaciones y los servicios podrán buscar redes Wi-Fi en cualquier momento, aunque la conexión Wi-Fi esté desactivada. Puedes cambiarlo en los ajustes de búsqueda de redes Wi-Fi. "<annotation id="link">"Cambiar"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> quiere añadir el siguiente recuadro a ajustes rápidos"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Añadir recuadro"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No añadir recuadro"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index e23f50d..9bd6e83 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi-ühendust ei looda praegu automaatselt"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Võrkude vahetamiseks katkestage Etherneti-ühendus"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Seadme kasutuskogemuse parandamiseks võivad rakendused ja teenused siiski alati otsida WiFi-võrke isegi siis, kui WiFi on väljas. Seda saab muuta WiFi-skannimise seadetes. "<annotation id="link">"Muuda"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> soovib kiirseadetesse lisada järgmise paani"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lisa paan"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ära lisa paani"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index de23069..53415d5 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Oraingoz ez da automatikoki konektatuko wifira"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Sarea aldatzeko, deskonektatu Ethernet-a"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Gailuaren funtzionamendua hobetzeko, aplikazioek eta zerbitzuek wifi-sareak bilatzen jarraituko dute, baita wifi-konexioa desaktibatuta dagoenean ere. Aukera hori aldatzeko, joan wifi-sareen bilaketaren ezarpenetara. "<annotation id="link">"Aldatu"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioak lauza hau gehitu nahi du Ezarpen bizkorrak menuan:"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Gehitu lauza"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ez gehitu lauza"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index dd778af..acb57f8 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> از <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ازطریق <xliff:g id="APP_LABEL">%3$s</xliff:g> پخش می‌شود"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> از <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"پخش"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"باز کردن <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> از <xliff:g id="ARTIST_NAME">%2$s</xliff:g> را ازطریق <xliff:g id="APP_LABEL">%3$s</xliff:g> پخش کنید"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"‏فعلاً Wi-Fi به‌طور خودکار متصل نمی‌شود"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"برای تغییر شبکه، اترنت را قطع کنید"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"‏برای بهبود تجربه استفاده از دستگاه، برنامه‌ها و سرویس‌ها همچنان می‌توانند در هر زمانی شبکه‌های Wi-Fi را اسکن کنند؛ حتی وقتی که Wi-Fi خاموش باشد. می‌توانید این مورد را در تنظیمات اسکن کردن Wi‑Fi تغییر دهید. "<annotation id="link">"تغییر"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> می‌خواهد کاشی زیر را به «تنظیمات فوری» اضافه کند"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"افزودن کاشی"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"کاشی اضافه نشود"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 2ea8dd7..0c1db38 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> soittaa nyt tätä: <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>)"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Toista"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Avaa <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) sovelluksessa <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ei toistaiseksi yhdistä automaattisesti"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Irrota Ethernet-johto, jos haluat vaihtaa verkkoa"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Laitteen käyttökokemuksen parantamiseksi sovellukset ja palvelut voivat hakea Wi-Fi-verkkoja myös silloin, kun Wi-Fi on pois päältä. Voit muuttaa asetusta Wi-Fi-haun asetuksissa. "<annotation id="link">"Muuta"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> haluaa lisätä seuraavan laatan pika-asetuksiin"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lisää laatta"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Älä lisää laattaa"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index db02e05c..6ece89f 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi impossible pour le moment"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, débranchez le câble Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pour améliorer l\'expérience de l\'appareil, les applications et les services peuvent quand même rechercher des réseaux Wi-Fi en tout temps, même lorsque le Wi-Fi est désactivé. Vous pouvez modifier vos préférences dans les paramètres de recherche de réseaux Wi-Fi. "<annotation id="link">"Modifier"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"L\'application <xliff:g id="APPNAME">%1$s</xliff:g> veut ajouter la tuile suivante au menu Paramètres rapides"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ajouter la tuile"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne pas ajouter tuile"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index cd4cfbd..91c05c6 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> est en cours de lecture depuis <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> sur <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Lire"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Ouvre <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> depuis <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi désactivée pour le moment"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, déconnectez l\'Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pour améliorer l\'expérience sur l\'appareil, les applis et les services peuvent continuer de rechercher les réseaux Wi-Fi, même si le Wi-Fi est désactivé. Vous pouvez modifier cela dans les paramètres de recherche Wi-Fi. "<annotation id="link">"Modifier"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> veut ajouter le bloc suivant aux Réglages rapides"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ajouter un bloc"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne pas ajouter bloc"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index b10090c..d4fd046 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"De momento, a wifi non se conectará automaticamente"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de rede, desconecta a Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para mellorar a experiencia que ofrece o dispositivo, as aplicacións e os servizos poden seguir buscando redes wifi en calquera momento, aínda que esta conexión estea desactivada. Podes cambiar esta opción na configuración da función Busca de redes wifi. "<annotation id="link">"Cambiar"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> solicita a túa aprobación para engadir o seguinte atallo a Configuración rápida"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Engadir atallo"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non engadir atallo"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index d440222..8733604 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> પર <xliff:g id="ARTIST_NAME">%2$s</xliff:g>નું <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચાલી રહ્યું છે"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>માંથી <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"ચલાવો"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ખોલો"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> પર <xliff:g id="ARTIST_NAME">%2$s</xliff:g>નું <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચલાવો"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"હમણાં પૂરતું વાઇ-ફાઇ ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"બીજા નેટવર્ક પર જવા માટે, ઇથરનેટ ડિસ્કનેક્ટ કરો"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ડિવાઇસના અનુભવને બહેતર બનાવવા માટે, વાઇ-ફાઇ બંધ હોય ત્યારે પણ ઍપ અને સેવાઓ કોઈપણ સમયે વાઇ-ફાઇ નેટવર્ક સ્કૅન કરી શકે છે. તમે વાઇ-ફાઇ સ્કૅનિંગના સેટિંગમાં જઈને આને બદલી શકો છો. "<annotation id="link">"બદલો"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"ઝડપી સેટિંગમાં <xliff:g id="APPNAME">%1$s</xliff:g> નીચે જણાવેલા ટાઇલ ઉમેરવા માગે છે"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ટાઇલ ઉમેરો"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ટાઇલ ઉમેરશો નહીં"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 0b945f0..ff3bd7f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"फ़िलहाल, वाई-फ़ाई अपने-आप कनेक्ट नहीं होगा"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदलने के लिए, पहले ईथरनेट को डिसकनेक्ट करें"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"डिवाइस इस्तेमाल करने के अनुभव काे बेहतर बनाने के लिए, ऐप्लिकेशन और सेवाओं की मदद से, किसी भी समय वाई-फ़ाई नेटवर्क स्कैन किए जा सकते हैं. ऐसा वाई-फ़ाई बंद होने पर भी किया जा सकता है. वाई-फ़ाई स्कैनिंग की सेटिंग में जाकर, इसे बदला जा सकता है. "<annotation id="link">"बदलें"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> इस टाइल को \'फटाफट सेटिंग\' में जोड़ने के लिए अनुमति चाहता है"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल जोड़ें"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल न जोड़ें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 731d00a..1acb156 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1185,6 +1185,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se zasad neće automatski povezivati"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste se prebacili na drugu mrežu, odspojite Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Da bi se poboljšao doživljaj uređaja, aplikacije i usluge i dalje mogu tražiti Wi-Fi mreže u bilo kojem trenutku, čak i kada je Wi-Fi isključen. To možete promijeniti u postavkama traženja Wi-Fija. "<annotation id="link">"Promijeni"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću pločicu u Brze postavke"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj pločicu"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nemoj dodati pločicu"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 2ebd1458..9986abe 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A Wi-Fi-re történő csatlakozás jelenleg nem automatikus"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Hálózatváltáshoz válassza le az ethernetet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Az eszközhasználati élmény javítása érdekében az alkalmazások és a szolgáltatások továbbra is bármikor kereshetnek Wi-Fi-hálózatokat, még akkor is, ha a Wi-Fi ki van kapcsolva. A funkciót a „Wi-Fi scanning settings” (Wi-Fi-keresési beállítások) részben módosíthatja. "<annotation id="link">"Módosítás"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> a következő mozaikot szeretné hozzáadni a Gyorsbeállításokhoz"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Mozaik hozzáadása"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne legyen hozzáadva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 735eedd..25931bf 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-ն ավտոմատ չի միանա"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Մի ցանցից մյուսին անցնելու համար անջատեք Ethernet-ը"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Սարքի աշխատանքը բարելավելու համար հավելվածներն ու ծառայությունները կորոնեն Wi‑Fi ցանցեր, նույնիսկ երբ Wi‑Fi-ն անջատված է։ Այս պարամետրը կարող եք փոխել Wi‑Fi ցանցերի որոնման կարգավորումներում։ "<annotation id="link">"Փոխել"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածն ուզում է ավելացնել հետևյալ սալիկը Արագ կարգավորումներում"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ավելացնել սալիկ"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Չավելացնել սալիկ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index cc514c0..b197f62 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan otomatis terhubung untuk saat ini"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk beralih jaringan, lepaskan kabel ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Agar pengalaman perangkat menjadi lebih baik, aplikasi dan layanan tetap dapat memindai jaringan Wi-Fi kapan saja, bahkan saat Wi-Fi nonaktif. Anda dapat mengubahnya di setelan pemindaian Wi-Fi. "<annotation id="link">"Ubah"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ingin menambahkan kartu berikut ke Setelan Cepat"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan kartu"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah kartu"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 0491d8c..8ae27dc 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tengist ekki sjálfkrafa eins og er"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aftengdu ethernet til að skipta um net"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Til að bæta tækjaupplifun geta forrit og þjónustur áfram leitað að WiFi-netum hvenær sem er, jafnvel þótt slökkt sé á WiFi. Hægt er að breyta þessu í stillingum WiFi-leitar. "<annotation id="link">"Breyta"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vill bæta eftirfarandi reit við flýtistillingar"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Bæta reit við"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ekki bæta reit við"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 8aaf77d..96fc861 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connessione automatica rete Wi-Fi non attiva al momento"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per cambiare rete, scollega il cavo Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Per migliorare l\'esperienza con il dispositivo, le app e i servizi possono continuare a cercare reti Wi-Fi in qualsiasi momento, anche quando la connessione Wi-Fi non è attiva. Puoi modificare questa preferenza nelle impostazioni relative alla ricerca di reti Wi-Fi. "<annotation id="link">"Cambia"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vuole aggiungere il seguente riquadro alle Impostazioni rapide"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Aggiungi riquadro"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non aggiungerlo"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 9ec4103..2ecf51d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"‏ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"‏כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות זאת בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"האפליקציה <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את האריח הבא"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"הוספת אריח"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"אין להוסיף אריח"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 520b048..74cc6be 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi に自動接続しません"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ネットワークを変更するにはイーサネット接続を解除してください"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"デバイスの機能向上のため、アプリやサービスは、Wi-Fi が OFF の場合でも、いつでも Wi-Fi ネットワークをスキャンできます。この設定は Wi-Fi スキャンの設定で変更できます。"<annotation id="link">"変更"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> が以下のタイルをクイック設定に追加しようとしています"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"タイルを追加"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"タイルを追加しない"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index de028b0..6596f6a 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ინტერნეტს დროებით ავტომატურად არ დაუკავშირდება"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ქსელების გადასართავად, გაწყვიტეთ Ethernet-თან კავშირი"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"მოწყობილობისგან მიღებული გამოცდილების გასაუმჯობესებლად, აპებსა და სერვისებს მაინც შეუძლია სკანირება Wi‑Fi ქსელების აღმოსაჩენად, ნებისმიერ დროს, მაშინაც კი, როცა Wi‑Fi გამორთულია. ამის შეცვლა Wi-Fi სკანირების პარამეტრებში შეგიძლიათ. "<annotation id="link">"შეცვლა"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>-ს სურს, დაამატოს შემდეგი მოზაიკა სწრაფ პარამეტრებში"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"მოზაიკის დამატება"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"არ დაემატოს მოზაიკა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b1dad23..b347709 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Әзірше Wi-Fi автоматты түрде қосылмайды."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Желілерді ауыстыру үшін ethernet кабелін ажыратыңыз."</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Құрылғы жұмысын жақсарту үшін қолданбалар мен қызметтер Wi-Fi байланысы өшірулі кезде де Wi-Fi желілерін іздейді. Оны Wi-Fi іздеу параметрлерінен өзгерте аласыз. "<annotation id="link">"Өзгерту"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> Жылдам параметрлерге келесі бөлшекті қосқысы келеді."</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Бөлшек қосу"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Бөлшек қоспау"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 828c864..db0018d 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិន​ភ្ជាប់ដោយស្វ័យ​ប្រវត្តិក្នុងពេលនេះទេ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បី​ប្ដូរ​បណ្ដាញ សូមផ្ដាច់​អ៊ីសឺរណិត"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យ​បទពិសោធន៍ប្រើប្រាស់​ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្ម​នៅតែអាចស្កេនរក​បណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជា​នៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាន​នៅក្នុង​ការកំណត់​ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូល​ប្រអប់​ខាងក្រោម​ទៅក្នុង​ការកំណត់រហ័ស"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូល​ប្រអប់"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"កុំ​បញ្ចូល​ប្រអប់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 67d6488..60bb5ec 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ಅವರ <xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%3$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> ರಲ್ಲಿ <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"ಪ್ಲೇ ಮಾಡಿ"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ಅವರ <xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%3$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ಸದ್ಯದ ಮಟ್ಟಿಗೆ ವೈ-ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಬದಲಿಸಲು, ಇಥರ್ನೆಟ್ ಅನ್ನು ಡಿಸ್‌ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ವೈ-ಫೈ ಆಫ್ ಇದ್ದಾಗಲೂ ಸಹ, ಸಾಧನದ ಅನುಭವವನ್ನು ಸುಧಾರಿಸಲು, ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಸೇವೆಗಳು ಯಾವಾಗ ಬೇಕಾದರೂ ಸಹ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್‌ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್ ಮಾಡಬಹುದು. ನೀವು ಇದನ್ನು ವೈ-ಫೈ ಸ್ಕ್ಯಾನಿಂಗ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಬದಲಾಯಿಸಬಹುದು. "<annotation id="link">"ಬದಲಿಸಿ"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ಈ ಕೆಳಗಿನ ಟೈಲ್ ಅನ್ನು ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳಿಗೆ ಸೇರಿಸಲು ಬಯಸುತ್ತದೆ"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಬೇಡಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 0f59303..01d3158 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"지금은 Wi-Fi가 자동으로 연결되지 않습니다."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"네트워크를 전환하려면 이더넷을 연결 해제하세요."</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"기기 환경을 개선하기 위해 Wi‑Fi가 꺼져 있을 때도 앱과 서비스에서 Wi‑Fi 네트워크를 검색할 수 있습니다. 이 설정은 Wi‑Fi 검색 설정에서 변경할 수 있습니다. "<annotation id="link">"변경"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>에서 빠른 설정에 다음 타일을 추가하려고 합니다."</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"타일 추가"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"타일 추가 안함"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 6e4c97f..e5383c7 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi азырынча автоматтык түрдө туташпайт"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Башка тармактарга которулуу үчүн Ethernet кабелин ажыратыңыз"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Түзмөктүн колдонулушун жакшыртуу үчүн колдонмолор менен кызматтар Wi‑Fi өчүп турса да зымсыз тармактарды издей беришет. Аны Wi-Fi тармактарын издөө жөндөөлөрүнөн өзгөртө аласыз. "<annotation id="link">"Өзгөртүү"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> төмөнкү ыкчам баскычты Ыкчам жөндөөлөргө кошкону жатат"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ыкчам баскыч кошуу"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ыкчам баскыч кошулбасын"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index e61e847..c75dbca 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດສຳລັບຕອນນີ້"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ເພື່ອສະຫຼັບເຄືອຂ່າຍ, ໃຫ້ຕັດການເຊື່ອມຕໍ່ອີເທີເນັດກ່ອນ"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ເພື່ອປັບປຸງປະສົບການອຸປະກອນ, ແອັບ ແລະ ບໍລິການຍັງຄົງສາມາດສະແກນຫາເຄືອຂ່າຍ Wi‑Fi ຕອນໃດກໍໄດ້, ເຖິງແມ່ນວ່າຈະປິດ Wi‑Fi ໄວ້ກໍຕາມ. ທ່ານສາມາດປ່ຽນສິ່ງນີ້ໄດ້ໃນການຕັ້ງຄ່າການສະແກນ Wi‑Fi. "<annotation id="link">"ປ່ຽນ"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ຕ້ອງການເພີ່ມແຜ່ນຕໍ່ໄປນີ້ໃສ່ການຕັ້ງຄ່າດ່ວນ"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ເພີ່ມແຜ່ນ"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ຢ່າເພີ່ມແຜ່ນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index fc77acb..2d5a4c4 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"„Wi-Fi“ šiuo metu nebus prijungtas automatiškai"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Norėdami perjungti tinklus, atjunkite eternetą"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Kad pagerintų įrenginio funkcijas, programos ir paslaugos vis tiek gali bet kada nuskaityti ieškodamos „Wi‑Fi“ tinklų, net jei „Wi‑Fi“ išjungtas. Tai galite pakeisti „Wi-Fi“ nuskaitymo nustatymuose. "<annotation id="link">"Pakeisti"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ nori prie sparčiųjų nustatymų pridėti toliau pateiktą išklotinės elementą"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pridėti išklotinės elementą"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepridėti išklotinės elemento"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index bfcb01d..c98bd79 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1185,6 +1185,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi savienojums īslaicīgi netiks izveidots automātiski."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Lai pārslēgtu tīklus, atvienojiet tīkla Ethernet vadu."</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Lai uzlabotu ierīces lietošanas iespējas, lietotnes un pakalpojumi joprojām varēs meklēt Wi‑Fi tīklus jebkurā laikā, pat ja Wi‑Fi būs izslēgts. Varat to mainīt Wi‑Fi meklēšanas iestatījumos. "<annotation id="link">"Mainīt"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> pieprasa atļauju pievienot tālāk norādīto elementu ātrajiem iestatījumiem"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pievienot elementu"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepievienot elementu"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a55fe97..1f22b69 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi нема да се поврзува автоматски засега"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За промена на мрежата, прекинете ја врската со етернетот"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"За да се подобри доживувањето на уредот, апликациите и услугите може сѐ уште да скенираат за Wi‑Fi мрежи во секое време, дури и кога Wi‑Fi е исклучено. Може да го промените ова во поставките за „Скенирање за Wi-Fi“. "<annotation id="link">"Промени"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> сака да ја додаде следнава плочка на „Брзите поставки“"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додајте плочка"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додавајте плочка"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 8242d91..d448f04 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"വൈഫൈ ഇപ്പോൾ സ്വയമേവ കണക്റ്റ് ചെയ്യില്ല"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"മറ്റ് നെറ്റ്‌വർക്കുകളിലേക്ക് മാറാൻ, ഇതർനെറ്റ് വിച്ഛേദിക്കുക"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ഉപകരണ അനുഭവം മെച്ചപ്പെടുത്താൻ, വൈഫൈ ഓഫാക്കിയിരിക്കുമ്പോൾ പോലും ആപ്പുകൾക്കും സേവനങ്ങൾക്കും വൈഫൈ നെറ്റ്‌വർക്കുകൾ കണ്ടെത്താൻ ഏത് സമയത്തും സ്‌കാൻ ചെയ്യാനാകും. നിങ്ങൾക്ക് ഇത് വൈഫൈ സ്‌കാനിംഗ് ക്രമീകരണത്തിൽ മാറ്റാം. "<annotation id="link">"മാറ്റുക"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"ദ്രുത ക്രമീകരണത്തിലേക്ക് ഇനിപ്പറയുന്ന ടൈൽ ചേർക്കാൻ <xliff:g id="APPNAME">%1$s</xliff:g> ആവശ്യപ്പെടുന്നു"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ടൈൽ ചേർക്കുക"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ടൈൽ ചേർക്കരുത്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 6772aff..e8fbef3 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-г одоогоор автоматаар холбохгүй"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Сүлжээг сэлгэхийн тулд этернэтийг салгана уу"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Төхөөрөмжийн туршлагыг сайжруулахын тулд аппууд болон үйлчилгээнүүд нь Wi-Fi сүлжээг хүссэн үедээ буюу Wi-Fi-г унтраалттай байсан ч скан хийх боломжтой хэвээр байна. Та үүнийг Wi-Fi скан хийх тохиргоонд өөрчлөх боломжтой. "<annotation id="link">"Өөрчлөх"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> нь дараах хавтанг Шуурхай тохиргоонд нэмэх хүсэлтэй байна"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Хавтан нэмэх"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Хавтанг бүү нэм"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 2cba8a0..1ab644d 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"सध्या वाय-फाय ऑटो-कनेक्ट होणार नाही"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क स्विच करण्यासाठी, इथरनेट केबल डिस्कनेक्ट करा"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"डिव्हाइसच्या अनुभवामध्ये सुधारणा करण्यासाठी, वाय-फाय बंद असले तरीही ॲप्स आणि सेवा या कधीही वाय-फाय नेटवर्क स्कॅन करू शकतात. तुम्ही हे वाय-फाय स्कॅनिंग सेटिंग्जमध्ये बदलू शकता. "<annotation id="link">"बदला"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ला क्विक सेटिंग्जमध्ये पुढील टाइल जोडायची आहे"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल जोडा"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल जोडू नका"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 461456b..0a4bd89 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan disambungkan secara automatik buat masa ini"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk menukar rangkaian, putuskan sambungan ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Untuk meningkatkan pengalaman peranti, apl dan perkhidmatan masih dapat melakukan imbasan untuk mengesan rangkaian Wi-Fi pada bila-bila masa, meskipun apabila Wi-Fi dimatikan. Anda boleh menukar tetapan ini dalam tetapan pengimbasan Wi-Fi. "<annotation id="link">"Tukar"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> mahu menambah jubin yang berikut kepada Tetapan Pantas"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan jubin"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah jubin"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 35e53a6..643bd01 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi က လောလောဆယ် အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ကွန်ရက်ပြောင်းရန် အီသာနက်ကို ချိတ်ဆက်မှုဖြုတ်ပါ"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"စက်ပစ္စည်းကို ပိုမိုကောင်းမွန်စွာ အသုံးပြုနိုင်ရန် Wi-Fi ပိတ်ထားသည့်တိုင် အက်ပ်နှင့် ဝန်ဆောင်မှုများက Wi-Fi ကွန်ရက်များကို အချိန်မရွေး စကင်ဖတ်နိုင်သည်။ ၎င်းကို Wi-Fi ရှာဖွေခြင်း ဆက်တင်များတွင် ပြောင်းနိုင်သည်။ "<annotation id="link">"ပြောင်းရန်"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> က ‘အမြန် ဆက်တင်များ’ တွင် အောက်ပါအကွက်ငယ်ကို ထည့်လိုသည်"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"အကွက်ငယ် ထည့်ရန်"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"အကွက်ငယ် မထည့်ပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index f99aaee..70529a6 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> av <xliff:g id="ARTIST_NAME">%2$s</xliff:g> spilles av fra <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> av <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Spill av"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Åpne <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> av <xliff:g id="ARTIST_NAME">%2$s</xliff:g> fra <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi kobles ikke til automatisk inntil videre"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"For å bytte nettverk, koble fra Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"For å forbedre brukeropplevelsen på enheten kan apper og tjenester søke etter Wi-Fi-nettverk når som helst – også når Wi-Fi er slått av. Du kan endre dette i innstillingene for Wi-Fi-skanning. "<annotation id="link">"Bytt"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vil legge til denne brikken i Hurtiginnstillinger"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Legg til brikke"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ikke legg til brikke"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index f061507..d75bb1b 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"केही समयका लागि Wi-Fi स्वतः कनेक्ट हुँदैन"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदल्न इथरनेट डिस्कनेक्ट गर्नुहोस्"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"डिभाइस प्रयोगको अनुभवमा गुणस्तर सुधार गर्न, एप तथा सेवाहरूले अझै पनि जुनसुकै बेला (Wi‑Fi अफ भएका बेलामा पनि) Wi‑Fi नेटवर्क खोज्न सक्छन्। तपाईं यसलाई Wi‑Fi स्क्यानिङका सेटिङमा गई परिवर्तन गर्न सक्नुहुन्छ। "<annotation id="link">"बदल्नुहोस्"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> द्रुत सेटिङमा निम्न टाइल हाल्न चाहन्छ"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल हाल्नुहोस्"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल नहाल्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 756141e..cf72f0c 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi maakt momenteel niet automatisch verbinding"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Verbreek de ethernetverbinding om van netwerk te wisselen"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Apps en services kunnen nog steeds op elk moment scannen op wifi-netwerken, zelfs als wifi uitstaat, om de apparaatfunctionaliteit te verbeteren. Je kunt dit aanpassen in de instellingen voor wifi-scannen. "<annotation id="link">"Wijzigen"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil de volgende tegel toevoegen aan Snelle instellingen"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tegel toevoegen"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tegel niet toevoegen"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index e336c38..6821f8c 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ରୁ <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ଙ୍କ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚାଲୁଛି"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>ରୁ <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"ଚଲାନ୍ତୁ"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> ଖୋଲନ୍ତୁ"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ରୁ <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ଙ୍କ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚଲାନ୍ତୁ"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ବର୍ତ୍ତମାନ ପାଇଁ ୱାଇ-ଫାଇ ସ୍ୱତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ନେଟୱାର୍କ ସ୍ୱିଚ୍ କରିବାକୁ, ଇଥରନେଟ୍ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ଡିଭାଇସ ଅନୁଭୂତିକୁ ଉନ୍ନତ କରିବା ପାଇଁ, ୱାଇ-ଫାଇ ବନ୍ଦ ଥିଲେ ମଧ୍ୟ ଆପ ଓ ସେବାଗୁଡ଼ିକ ଏବେ ବି ଯେ କୌଣସି ସମୟରେ ୱାଇ-ଫାଇ ନେଟୱାର୍କ ପାଇଁ ସ୍କାନ କରିପାରିବ। ଆପଣ ଏହାକୁ ୱାଇ-ଫାଇ ସ୍କାନିଂ ସେଟିଂସରେ ପରିବର୍ତ୍ତନ କରିପାରିବେ। "<annotation id="link">"ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> କ୍ୱିକ୍ ସେଟିଂସରେ ନିମ୍ନୋକ୍ତ ଟାଇଲ୍ ଯୋଗ କରିବାକୁ ଚାହେଁ"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ଟାଇଲ୍ ଯୋଗ କର ନାହିଁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index cc20ab7..ee748a8 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ਫ਼ਿਲਹਾਲ ਵਾਈ-ਫਾਈ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਈਥਰਨੈੱਟ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ਡੀਵਾਈਸ ਦੇ ਅਨੁਭਵ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ, ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਕਰ ਸਕਦੀਆਂ ਹਨ, ਭਾਵੇਂ ਵਾਈ-ਫਾਈ ਬੰਦ ਹੀ ਕਿਉਂ ਨਾ ਹੋਵੇ। ਤੁਸੀਂ ਇਸ ਨੂੰ ਵਾਈ‑ਫਾਈ ਸਕੈਨਿੰਗ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਬਦਲ ਸਕਦੇ ਹੋ। "<annotation id="link">"ਬਦਲੋ"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ਅੱਗੇ ਦਿੱਤੀ ਟਾਇਲ ਨੂੰ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ਟਾਇਲ ਸ਼ਾਮਲ ਨਾ ਕਰੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index b4fb410..ac2d489 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nie będzie na razie włączać się automatycznie"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aby przełączać sieci, odłącz Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Aby zapewnić Ci większy komfort korzystania z urządzenia, aplikacje i usługi mogą nadal wyszukiwać sieci Wi-Fi w pobliżu nawet wtedy, gdy Wi-Fi jest wyłączone. Możesz to zmienić w ustawieniach skanowania Wi-Fi. "<annotation id="link">"Zmień"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikacja <xliff:g id="APPNAME">%1$s</xliff:g> chce dodać do Szybkich ustawień ten kafelek"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj kafelek"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nie dodawaj kafelka"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index f03525d..6a67522 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A conexão automática ao Wi-Fi ficará indisponível"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para melhorar a experiência no dispositivo, os apps e serviços ainda podem procurar redes Wi-Fi a qualquer momento, mesmo quando o Wi-Fi estiver desativado. Você pode mudar essa opção nas configurações de busca por Wi-Fi. "<annotation id="link">"Mudar"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"O app <xliff:g id="APPNAME">%1$s</xliff:g> quer adicionar o bloco a seguir às Configurações rápidas"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar bloco"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicionar bloco"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index f49627f..6225ae4 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por agora, o Wi-Fi não irá estabelecer lig. automaticamente"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desligue a Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para melhorar a experiência do dispositivo, as apps e os serviços podem continuar a procurar redes Wi-Fi em qualquer altura, mesmo quando o Wi-Fi está desativado. Pode alterar esta opção nas definições de procura de Wi-Fi. "<annotation id="link">"Alterar"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"A app <xliff:g id="APPNAME">%1$s</xliff:g> pretende adicionar o seguinte mosaico às Definições rápidas"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar mosaico"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicion. mosaico"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index f03525d..6a67522 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A conexão automática ao Wi-Fi ficará indisponível"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para melhorar a experiência no dispositivo, os apps e serviços ainda podem procurar redes Wi-Fi a qualquer momento, mesmo quando o Wi-Fi estiver desativado. Você pode mudar essa opção nas configurações de busca por Wi-Fi. "<annotation id="link">"Mudar"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"O app <xliff:g id="APPNAME">%1$s</xliff:g> quer adicionar o bloco a seguir às Configurações rápidas"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar bloco"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicionar bloco"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 978c222..a09779d 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1185,6 +1185,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Deocamdată, Wi-Fi nu se poate conecta automat"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pentru a îmbunătăți experiența cu dispozitivul, aplicațiile și serviciile pot să caute în continuare rețele Wi‑Fi chiar și atunci când conexiunea Wi-Fi este dezactivată. Puteți să schimbați acest aspect din setările pentru căutarea de rețele Wi-Fi. "<annotation id="link">"Schimbați"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vrea să adauge următorul card la Setări rapide"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adăugați un card"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nu adăugați un card"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 35366df..1b6e85b 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Подключение по Wi-Fi не установится автоматически."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Чтобы переключиться между сетями, отключите кабель Ethernet."</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Чтобы улучшать работу устройства, приложения и сервисы могут искать беспроводные сети в любое время, даже если вы отключили Wi‑Fi. Чтобы запретить это, отключите поиск сетей Wi‑Fi. "<annotation id="link">"Открыть настройки"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Приложение \"<xliff:g id="APPNAME">%1$s</xliff:g>\" хочет добавить в меню \"Быстрые настройки\" указанный параметр."</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Добавить параметр"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не добавлять"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index f6bb208..94d8705 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi දැනට ස්වයං-සබැඳි නොවනු ඇත"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ජාල මාරු කිරීමට, ඊතර්නෙට් විසන්ධි කරන්න"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"උපාංග අත්දැකීම වැඩි දියුණු කිරිමට, Wi‑Fi ක්‍රියාවිරහිත විට පවා, ඕනෑම අවස්ථාවක Wi‑Fi ජාල සඳහා ස්කෑන් කිරීමට යෙදුම් සහ සේවාවලට හැකිය. ඔබට මෙය Wi‑Fi ස්කෑන් කිරීමේ සැකසීම් තුළ වෙනස් කළ හැකිය. "<annotation id="link">"වෙනස් කරන්න"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> හට ක්ෂණික සැකසීම් වෙත පහත ටයිල් එක් කිරීමට අවශ්‍යයි"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ටයිල් එක් කරන්න"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ටයිල් එක් නොකරන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index c349ef1..a0ba5cd 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi sa teraz automaticky nepripojí"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ak chcete prepnúť siete, odpojte ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Aplikácie a služby môžu kedykoľvek vyhľadávať siete Wi‑Fi (a to aj vtedy, keď je pripojenie Wi‑Fi vypnuté), čím zlepšujú prostredie v zariadení. Môžete to zmeniť v nastaveniach vyhľadávania sietí Wi‑Fi. "<annotation id="link">"Zmeniť"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikácia <xliff:g id="APPNAME">%1$s</xliff:g> chce pridať do rýchlych nastavení túto kartu"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pridať kartu"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepridať kartu"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index d41459a..649e123 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Vmesnik Wi-Fi trenutno ne bo samodejno vzpostavil povezave."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Če želite preklopiti omrežje, prekinite ethernetno povezavo."</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Za izboljšano izkušnjo pri uporabi naprave lahko aplikacije in storitve kadar koli iščejo omrežja Wi‑Fi, tudi ko je Wi‑Fi izklopljen. To lahko spremenite v nastavitvah iskanja omrežij Wi-Fi. "<annotation id="link">"Spremeni"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> želi dodati to ploščico v hitre nastavitve."</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj ploščico"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne dodaj ploščice"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index e4ec008..f5c30dd 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nuk do të lidhet automatikisht për momentin"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Për të ndërruar rrjetet, shkëput Ethernet-in"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Për të përmirësuar përvojën e pajisjes, aplikacionet dhe shërbimet mund të vazhdojnë të skanojnë për rrjete Wi-Fi në çdo kohë, edhe kur Wi-Fi është joaktiv. Mund ta ndryshosh këtë te cilësimet e skanimit të Wi-Fi. "<annotation id="link">"Ndrysho"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> dëshiron të shtojë pllakëzën e mëposhtme te \"Cilësimet e shpejta\""</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Shto një pllakëz"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Mos e shto pllakëzën"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 3dbda9b..bcd7bc8 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1185,6 +1185,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi тренутно не може да се аутоматски повеже"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Да бисте променили мрежу, прекините етернет везу"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Ради бољег доживљаја уређаја, апликације и услуге и даље могу да траже WiFi мреже у било ком тренутку, чак и када је WiFi искључен. То можете да промените у подешавањима WiFi скенирања. "<annotation id="link">"Промените"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> жели да дода следећу плочицу у Брза подешавања"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додај плочицу"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додај плочицу"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index eccf9ee..45666a2 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Du ansluts inte till wifi automatiskt för närvarande"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"I syfte att förbättra upplevelsen med enheten kan appar och tjänster fortfarande söka efter wifi-nätverk när som helst, även om wifi har inaktiverats. "<annotation id="link">"Ändra"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vill lägga till följande ruta i snabbinställningarna"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lägg till ruta"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Lägg inte till ruta"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 31f74b2..e976c92 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi haitaunganishwa kiotomatiki kwa sasa"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ili kubadili mitandao, tenganisha ethaneti"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Ili kuboresha hali ya matumizi ya kifaa, programu na huduma bado zinaweza kutafuta mitandao ya Wi‑Fi wakati wowote, hata wakati umezima Wi‑Fi. Unaweza kubadilisha mipangilio hii katika mipangilio ya kutafuta Wi-Fi. "<annotation id="link">"Badilisha"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ingependa kuongeza kigae kifuatacho kwenye Mipangilio ya Haraka"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ongeza kigae"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Usiongeze kigae"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index f2d2254..bfe399d 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"தற்போதைக்கு வைஃபை தானாக இணைக்கப்படாது"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"நெட்வொர்க்குகளை மாற்ற ஈதர்நெட் இணைப்பைத் துண்டிக்கவும்"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"சாதன அனுபவத்தை மேம்படுத்த, வைஃபை ஆஃப் செய்யப்பட்டிருந்தாலும்கூட எந்த நேரத்திலும் ஆப்ஸும் சேவைகளும் வைஃபை நெட்வொர்க்குகளைத் தேடலாம். வைஃபை ஸ்கேனிங் அமைப்புகளில் இதை மாற்றிக் கொள்ளலாம். "<annotation id="link">"மாற்று"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"விரைவு அமைப்புகளில் பின்வரும் கட்டத்தைச் சேர்க்க <xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸ் விரும்புகிறது"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"கட்டத்தைச் சேர்"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"கட்டத்தை சேர்க்காதே"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index be277b1..e6d7de9 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -563,30 +563,30 @@
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ఈ పరికరంలో ప్రమాణపత్ర అధికారం ఇన్‌స్టాల్ చేయబడింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
     <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేశారు."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"మీ అడ్మిన్ నెట్‌వర్క్ లాగింగ్‌ను ఆన్ చేశారు, ఇది మీ వర్క్ ప్రొఫైల్‌లోని ట్రాఫిక్‌ను పర్యవేక్షిస్తుంది కానీ మీ వ్యక్తిగత ప్రొఫైల్‌లో కాదు."</string>
-    <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
-    <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"మీరు ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP_0">%1$s</xliff:g> మరియు <xliff:g id="VPN_APP_1">%2$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
-    <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"మీ కార్యాలయ ప్రొఫైల్ ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
-    <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"మీ వ్యక్తిగత ప్రొఫైల్ ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
+    <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"మీరు ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP_0">%1$s</xliff:g> మరియు <xliff:g id="VPN_APP_1">%2$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
+    <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"మీ కార్యాలయ ప్రొఫైల్ ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
+    <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"మీ వ్యక్తిగత ప్రొఫైల్ ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
     <string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"మీ పరికరం <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ద్వారా నిర్వహించబడుతోంది."</string>
     <string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> మీ పరికరాన్ని నిర్వహించడానికి <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>ని ఉపయోగిస్తుంది."</string>
     <string name="monitoring_description_do_body" msgid="7700878065625769970">"మీ పరికరంతో అనుబంధించబడిన సెట్టింగ్‌లు, కార్పొరేట్ యాక్సెస్, యాప్‌లు, డేటా మరియు మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="645149183455573790">"మరింత తెలుసుకోండి"</string>
-    <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN సెట్టింగ్‌లను తెరవండి"</string>
     <string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"విశ్వసనీయ ఆధారాలను తెరువు"</string>
     <string name="monitoring_description_network_logging" msgid="577305979174002252">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేశారు.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి."</string>
-    <string name="monitoring_description_vpn" msgid="1685428000684586870">"మీరు VPN కనెక్షన్ సెటప్ చేయడానికి ఒక యాప్‌నకు అనుమతి ఇచ్చారు.\n\nఈ యాప్ ఇమెయిల్‌లు,యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ డివైజ్ మరియు నెట్‌వర్క్ యాక్టివిటీని పర్యవేక్షించగలదు."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> ద్వారా మీ కార్యాలయ ప్రొఫైల్ నిర్వహించబడుతోంది.\n\nఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల సామర్థ్యం మీ నిర్వాహకులకు ఉంది.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి.\n\nమీరు VPNకి కూడా కనెక్ట్ అయ్యారు, ఇది మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_vpn" msgid="1685428000684586870">"మీరు VPN కనెక్షన్ సెటప్ చేయడానికి ఒక యాప్‌నకు అనుమతి ఇచ్చారు.\n\nఈ యాప్ ఈమెయిళ్లు,యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ డివైజ్ మరియు నెట్‌వర్క్ యాక్టివిటీని పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> ద్వారా మీ కార్యాలయ ప్రొఫైల్ నిర్వహించబడుతోంది.\n\nఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల సామర్థ్యం మీ నిర్వాహకులకు ఉంది.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి.\n\nమీరు VPNకి కూడా కనెక్ట్ అయ్యారు, ఇది మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు. మీ తల్లి/తండ్రి, మీరు ఉపయోగించే యాప్‌లు, మీ లొకేషన్, అలాగే మీ పరికర వినియోగ వ్యవధి వంటి సమాచారాన్ని చూడగలరు, మేనేజ్ చేయగలరు."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
-    <string name="monitoring_description_app" msgid="376868879287922929">"మీరు ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
-    <string name="monitoring_description_app_personal" msgid="1970094872688265987">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌‍సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
-    <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
-    <string name="monitoring_description_app_work" msgid="3713084153786663662">"మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహణలో ఉంది. ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION">%2$s</xliff:g>కి ప్రొఫైల్ కనెక్ట్ చేయబడింది.\n\nమరింత సమాచారం కోసం, మీ నిర్వాహకులను సంప్రదించండి."</string>
-    <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహణలో ఉంది. ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>కి ప్రొఫైల్ కనెక్ట్ చేయబడింది.\n\nమీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>కి కూడా మీరు కనెక్ట్ చేయబడ్డారు."</string>
+    <string name="monitoring_description_app" msgid="376868879287922929">"మీరు ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
+    <string name="monitoring_description_app_personal" msgid="1970094872688265987">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌‍సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_app_work" msgid="3713084153786663662">"మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహణలో ఉంది. ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION">%2$s</xliff:g>కి ప్రొఫైల్ కనెక్ట్ చేయబడింది.\n\nమరింత సమాచారం కోసం, మీ నిర్వాహకులను సంప్రదించండి."</string>
+    <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహణలో ఉంది. ఈమెయిళ్లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>కి ప్రొఫైల్ కనెక్ట్ చేయబడింది.\n\nమీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>కి కూడా మీరు కనెక్ట్ చేయబడ్డారు."</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ద్వారా అన్‌లాక్ చేయబడింది"</string>
     <string name="keyguard_indication_trust_disabled" msgid="6820793704816727918">"మీరు మాన్యువల్‌గా అన్‌లాక్ చేస్తే మినహా పరికరం లాక్ చేయబడి ఉంటుంది"</string>
     <string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
@@ -611,7 +611,7 @@
     <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"మీరు అన్‌పిన్ చేసే వరకు ఇది వీక్షణలో ఉంచబడుతుంది. అన్‌పిన్ చేయడానికి, పైకి స్వైప్ చేసి &amp; పట్టుకోండి."</string>
     <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"దీని వలన మీరు అన్‌పిన్ చేసే వరకు ఇది వీక్షణలో ఉంచబడుతుంది. అన్‌పిన్ చేయడానికి స్థూలదృష్టిని తాకి &amp; అలాగే పట్టుకోండి."</string>
     <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"దీని వలన మీరు అన్‌పిన్ చేసే వరకు ఇది వీక్షణలో ఉంచబడుతుంది. అన్‌పిన్ చేయడానికి హోమ్‌ని తాకి &amp; అలాగే పట్టుకోండి."</string>
-    <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"వ్యక్తిగత డేటా (కాంటాక్ట్‌లు, ఇంకా ఇమెయిల్ కంటెంట్ లాంటివి) యాక్సెస్ చేయబడవచ్చు."</string>
+    <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"వ్యక్తిగత డేటా (కాంటాక్ట్‌లు, ఇంకా ఈమెయిల్‌ కంటెంట్ లాంటివి) యాక్సెస్ చేయబడవచ్చు."</string>
     <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"పిన్ చేయబడిన యాప్ ఇతర యాప్‌లను తెరవవచ్చు."</string>
     <string name="screen_pinning_toast" msgid="8177286912533744328">"ఈ యాప్‌ను అన్‌పిన్ చేయడానికి, \'వెనుకకు\', \'ఓవర్‌వ్యూ\' బటన్‌లను తాకి &amp; అలాగే పట్టుకోండి"</string>
     <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"ఈ యాప్‌ను అన్‌పిన్ చేయడానికి, వెనుకకు, హోమ్ బటన్‌లను తాకి &amp; అలాగే పట్టుకోండి"</string>
@@ -844,7 +844,7 @@
     <string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"సహాయకం"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"బ్రౌజర్"</string>
     <string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"కాంటాక్ట్‌లు"</string>
-    <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"ఇమెయిల్"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"ఈమెయిల్‌"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"మ్యూజిక్"</string>
     <string name="keyboard_shortcut_group_applications_youtube" msgid="5078136084632450333">"YouTube"</string>
@@ -1090,8 +1090,7 @@
     <string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్‌లు"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> పాడిన <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%3$s</xliff:g> నుండి ప్లే అవుతోంది"</string>
-    <!-- no translation found for controls_media_seekbar_description (4389621713616214611) -->
-    <skip />
+    <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>లో <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"ప్లే చేయండి"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g>ను తెరవండి"</string>
     <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> నుండి <xliff:g id="ARTIST_NAME">%2$s</xliff:g> పాడిన <xliff:g id="SONG_NAME">%1$s</xliff:g>‌ను ప్లే చేయండి"</string>
@@ -1180,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ప్రస్తుతానికి Wi-Fi ఆటోమేటిక్‌గా కనెక్ట్ అవ్వదు"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"నెట్‌వర్క్‌లను మార్చడానికి, ఈథర్‌నెట్‌ను డిస్‌కనెక్ట్ చేయండి"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"పరికర అనుభవాన్ని మెరుగుపరచడానికి, Wi‑Fi ఆఫ్‌లో ఉన్నప్పుడు కూడా, ఏ సమయంలో అయినా ఇప్పటికీ Wi‑Fi నెట్‌వర్క్‌ల కోసం యాప్‌లు, సర్వీస్‌లు స్కాన్ చేయగలవు. మీరు దీనిని Wi‑Fi స్కానింగ్ సెట్టింగ్‌లలో మార్చవచ్చు. "<annotation id="link">"మార్చండి"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"కింది టైల్‌ను క్విక్ సెట్టింగ్‌లకు జోడించడానికి <xliff:g id="APPNAME">%1$s</xliff:g> అనుమతి కోరుతోంది"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"టైల్‌ను జోడించండి"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"టైల్‌ను జోడించవద్దు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index f7811fb..55329bd 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi จะไม่เชื่อมต่ออัตโนมัติในตอนนี้"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ตัดการเชื่อมต่ออีเทอร์เน็ตเพื่อสลับเครือข่าย"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"เพื่อปรับปรุงประสบการณ์การใช้อุปกรณ์ แอปและบริการต่างๆ จะยังคงสแกนหาเครือข่าย Wi‑Fi ได้ทุกเมื่อแม้ว่า Wi‑Fi จะปิดอยู่ คุณเปลี่ยนตัวเลือกนี้ได้ในการตั้งค่าการสแกนหา Wi-Fi "<annotation id="link">"เปลี่ยน"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ต้องการเพิ่มชิ้นส่วนต่อไปนี้ในการตั้งค่าด่วน"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"เพิ่มชิ้นส่วน"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ไม่ต้องเพิ่มชิ้นส่วน"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 822a3fd..264632b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Hindi awtomatikong kokonekta ang Wi-Fi sa ngayon"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para lumipat ng network, idiskonekta ang ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Para pahusayin ang karanasan sa device, puwede pa ring mag-scan ng mga Wi-Fi network ang mga app at serbisyo anumang oras, kahit habang naka-off ang Wi‑Fi. Mababago mo ito sa mga setting ng pag-scan ng Wi-Fi. "<annotation id="link">"Baguhin"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Gustong idagdag ng <xliff:g id="APPNAME">%1$s</xliff:g> ang sumusunod na tile sa Mga Mabilisang Setting"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Idagdag ang tile"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Huwag idagdag"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 3849727..c691440 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Şu anda kablosuz ağa otomatik olarak bağlanılamıyor"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ağ değiştirmek için ethernet bağlantısını kesin"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Uygulamalar ve hizmetler, cihaz deneyimini iyileştirmek için Kablosuz özelliği kapalı bile olsa kablosuz ağlar herhangi bir zamanda tarayabilir. Bunu kablosuz ağ taraması ayarlarından değiştirebilirsiniz. "<annotation id="link">"Değiştir"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aşağıdaki kartı Hızlı Ayarlar\'a eklemek istiyor"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Kart ekle"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Kart ekleme"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 51cc6dd..57b9d8a 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1191,6 +1191,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Пристрій не підключатиметься до Wi-Fi автоматично"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Щоб вибрати іншу мережу, від’єднайте кабель Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Щоб користуватися пристроєм було зручніше, додатки й сервіси можуть шукати бездротові мережі, навіть якщо Wi-Fi вимкнено. Це налаштування можна змінити в параметрах пошуку мереж Wi-Fi. "<annotation id="link">"Змінити"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Додаток <xliff:g id="APPNAME">%1$s</xliff:g> хоче додати такий параметр у меню швидких налаштувань:"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додати параметр"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додавати параметр"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 0690b5f..788b162 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"‏ابھی Wi-Fi خود کار طور پر منسلک نہیں ہوگا"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"نیٹ ورکس پر سوئچ کرنے کیلئے، ایتھرنیٹ غیر منسلک کریں"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"‏آلے کے تجربے کو بہتر بنانے کے لیے، Wi‑Fi کے آف ہونے پر بھی ایپس اور سروسز کسی بھی وقت Wi‑Fi نیٹ ورکس اسکین کر سکتی ہیں۔ آپ اسے Wi‑Fi اسکیننگ کی ترتیبات میں تبدیل کر سکتے ہیں۔ "<annotation id="link">"تبدیل کریں"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> درج ذیل ٹائل کو فوری ترتیبات میں شامل کرنا چاہتی ہے"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ٹائل شامل کریں"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ٹائل شامل نہ کریں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index acda73d..1be13be 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hozir avtomatik ulanmaydi"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Boshqa tarmoqqa almashish uchun Ethernet tarmogʻini uzing"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Qurilma ishlashini yaxshilash uchun ilova va xizmatlar hatto Wi-Fi yoqilmaganda ham istalgan vaqt Wi-Fi tarmoqlarni qidirishi mumkin. Buni taqiqlash uchun Wi-Fi tarmoqlarni qidirish funksiyasini faolsizlantiring. "<annotation id="link">"Sozlamalarni ochish"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ilovasi Tezkor sozlamalarga quyidagi tugmani kiritmoqchi"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tugma kiritish"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tugma kiritilmasin"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 2679b00..2478ca4 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Tạm thời, Wi-Fi sẽ không tự động kết nối"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Để chuyển mạng, hãy rút cáp Ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Để cải thiện trải nghiệm khi dùng thiết bị, các ứng dụng và dịch vụ vẫn có thể quét tìm mạng Wi‑Fi bất cứ lúc nào, ngay cả khi Wi‑Fi tắt. Bạn có thể thay đổi chế độ này trong phần cài đặt tính năng Quét tìm Wi‑Fi. "<annotation id="link">"Thay đổi"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> muốn thêm ô bên dưới vào trình đơn Cài đặt nhanh"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Thêm ô"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Không thêm ô"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 8ca914d..e6421a2 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WLAN 暂时无法自动连接"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切换网络,请断开以太网连接"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"为了提升设备的使用体验,即使 WLAN 已关闭,应用和服务仍可以随时扫描 WLAN 网络。您可以在 WLAN 扫描设置中更改此设置。"<annotation id="link">"更改"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"“<xliff:g id="APPNAME">%1$s</xliff:g>”希望将以下图块添加到“快捷设置”"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"添加图块"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不添加图块"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index e1c3b35..f94a46a 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前系統不會自動連線至 Wi-Fi"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網絡,請中斷以太網連線"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"為改善裝置的使用體驗,應用程式和服務仍可隨時掃瞄 Wi-Fi 網絡 (即使 Wi-Fi 已關閉)。您可在 Wi-Fi 掃瞄設定中變更此設定。"<annotation id="link">"變更"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"「<xliff:g id="APPNAME">%1$s</xliff:g>」想在「快速設定」選單新增以下圖塊"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"新增圖塊"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不要新增圖塊"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 66434e4..48c6cda 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前不會自動連上 Wi-Fi"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網路,請中斷乙太網路連線"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"為提升裝置的使用體驗,應用程式和服務仍可隨時掃描 Wi‑Fi 網路,即使 Wi-Fi 連線功能處於關閉狀態時亦然。你可以前往「掃描 Wi-Fi」設定進行變更。"<annotation id="link">"變更"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"「<xliff:g id="APPNAME">%1$s</xliff:g>」想在快速設定選單新增以下設定方塊"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"新增設定方塊"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不要新增設定方塊"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 6505503..de88b05 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1179,6 +1179,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"I-Wi-Fi ngeke ixhume ngokuzenzakalelayo okwamanje"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ukuze ushintshe amanethiwekhi, nqamula i-ethernet"</string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Ukuze kuthuthukiswe ukuzizwela kwedivayisi, ama-app namasevisi kusengakwazi ukuskena amanethiwekhi we-Wi-Fi noma kunini, ngisho noma i-Wi-Fi ivaliwe, Ungashintsha lokhu kumasethingi Wokuskena i-Wi-Fi. "<annotation id="link">"Shintsha"</annotation></string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"I-<xliff:g id="APPNAME">%1$s</xliff:g> ifuna ukwengeza ithayela elilandelayo Kumasethingi Asheshayo"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Engeza ithayela"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ungafaki ithayela"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c2901a5..2eaf098 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -723,6 +723,30 @@
     <!-- Timeout to idle mode duration in milliseconds. -->
     <integer name="config_idleModeTimeout">10000</integer>
 
+    <!-- This value is used when calculating whether the device is in ambient light mode. It is
+        light mode when the light sensor sample value exceeds above this value. -->
+    <integer name="config_ambientLightModeThreshold">5</integer>
+
+    <!-- This value is used when calculating whether the device is in ambient dark mode. It is
+        dark mode when the light sensor sample value drops below this value. -->
+    <integer name="config_ambientDarkModeThreshold">2</integer>
+
+    <!-- This value is used when calculating whether the device is in ambient light mode. Each
+        sample contains light sensor events from this span of time duration. -->
+    <integer name="config_ambientLightModeSamplingSpanMillis">10000</integer>
+
+    <!-- This value is used when calculating whether the device is in ambient dark mode. Each
+    sample contains light sensor events from this span of time duration. -->
+    <integer name="config_ambientDarkModeSamplingSpanMillis">2000</integer>
+
+    <!-- This value is used when calculating whether the device is in ambient light mode. The
+        samples are collected at this frequency. -->
+    <integer name="config_ambientLightModeSamplingFrequencyMillis">1000</integer>
+
+    <!-- This value is used when calculating whether the device is in ambient dark mode. The
+    samples are collected at this frequency. -->
+    <integer name="config_ambientDarkModeSamplingFrequencyMillis">500</integer>
+
     <!-- The maximum number of attempts to reconnect to the communal source target after failing
          to connect -->
     <integer name="config_communalSourceMaxReconnectAttempts">10</integer>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1d0660e..fc455be 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2878,7 +2878,7 @@
     <!-- Summary for media output group with the active device count [CHAR LIMIT=NONE] -->
     <string name="media_output_dialog_multiple_devices"><xliff:g id="count" example="2">%1$d</xliff:g> devices selected</string>
     <!-- Summary for disconnected status [CHAR LIMIT=50] -->
-    <string name="media_output_dialog_disconnected"><xliff:g id="device_name" example="My device">%1$s</xliff:g> (disconnected)</string>
+    <string name="media_output_dialog_disconnected">(disconnected)</string>
     <!-- Summary for connecting error message [CHAR LIMIT=NONE] -->
     <string name="media_output_dialog_connect_failed">Couldn\'t connect. Try again.</string>
     <!-- Title for pairing item [CHAR LIMIT=60] -->
@@ -3028,6 +3028,8 @@
     <string name="see_all_networks">See all</string>
     <!-- Summary for warning to disconnect ethernet first then switch to other networks. [CHAR LIMIT=60] -->
     <string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
+    <!-- Message to describe "Wi-Fi scan always available feature" when Wi-Fi is off and Wi-Fi scanning is on. [CHAR LIMIT=NONE] -->
+    <string name="wifi_scan_notify_message">To improve device experience, apps and services can still scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. You can change this in Wi\u2011Fi scanning settings. <annotation id="link">Change</annotation></string>
 
     <!-- Text for TileService request dialog. This is shown to the user that an app is requesting
          user approval to add the shown tile to Quick Settings [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
new file mode 100644
index 0000000..e072d41
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
@@ -0,0 +1,73 @@
+package com.android.systemui.unfold.util
+
+import android.content.Context
+import android.os.RemoteException
+import android.util.Log
+import android.view.IRotationWatcher
+import android.view.IWindowManager
+import android.view.Surface
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+
+/**
+ * [UnfoldTransitionProgressProvider] that emits transition progress only when the display has
+ * default rotation or 180 degrees opposite rotation (ROTATION_0 or ROTATION_180).
+ * It could be helpful to run the animation only when the display's rotation is perpendicular
+ * to the fold.
+ */
+class NaturalRotationUnfoldProgressProvider(
+    private val context: Context,
+    private val windowManagerInterface: IWindowManager,
+    unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider
+) : UnfoldTransitionProgressProvider {
+
+    private val scopedUnfoldTransitionProgressProvider =
+            ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+    private val rotationWatcher = RotationWatcher()
+
+    private var isNaturalRotation: Boolean = false
+
+    fun init() {
+        try {
+            windowManagerInterface.watchRotation(rotationWatcher, context.display.displayId)
+        } catch (e: RemoteException) {
+            throw e.rethrowFromSystemServer()
+        }
+
+        onRotationChanged(context.display.rotation)
+    }
+
+    private fun onRotationChanged(rotation: Int) {
+        val isNewRotationNatural = rotation == Surface.ROTATION_0 ||
+                rotation == Surface.ROTATION_180
+
+        if (isNaturalRotation != isNewRotationNatural) {
+            isNaturalRotation = isNewRotationNatural
+            scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(isNewRotationNatural)
+        }
+    }
+
+    override fun destroy() {
+        try {
+            windowManagerInterface.removeRotationWatcher(rotationWatcher)
+        } catch (e: RemoteException) {
+            e.rethrowFromSystemServer()
+        }
+
+        scopedUnfoldTransitionProgressProvider.destroy()
+    }
+
+    override fun addCallback(listener: TransitionProgressListener) {
+        scopedUnfoldTransitionProgressProvider.addCallback(listener)
+    }
+
+    override fun removeCallback(listener: TransitionProgressListener) {
+        scopedUnfoldTransitionProgressProvider.removeCallback(listener)
+    }
+
+    private inner class RotationWatcher : IRotationWatcher.Stub() {
+        override fun onRotationChanged(rotation: Int) {
+            this@NaturalRotationUnfoldProgressProvider.onRotationChanged(rotation)
+        }
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.java b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.java
new file mode 100644
index 0000000..543232d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.java
@@ -0,0 +1,142 @@
+/*
+ * 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.systemui.unfold.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages progress listeners that can have smaller lifespan than the unfold animation.
+ * Allows to limit getting transition updates to only when
+ * {@link ScopedUnfoldTransitionProgressProvider#setReadyToHandleTransition} is called
+ * with readyToHandleTransition = true
+ *
+ * If the transition has already started by the moment when the clients are ready to play
+ * the transition then it will report transition started callback and current animation progress.
+ */
+public final class ScopedUnfoldTransitionProgressProvider implements
+        UnfoldTransitionProgressProvider, TransitionProgressListener {
+
+    private static final float PROGRESS_UNSET = -1f;
+
+    @Nullable
+    private UnfoldTransitionProgressProvider mSource;
+
+    private final List<TransitionProgressListener> mListeners = new ArrayList<>();
+
+    private boolean mIsReadyToHandleTransition;
+    private boolean mIsTransitionRunning;
+    private float mLastTransitionProgress = PROGRESS_UNSET;
+
+    public ScopedUnfoldTransitionProgressProvider() {
+        this(null);
+    }
+
+    public ScopedUnfoldTransitionProgressProvider(
+            @Nullable UnfoldTransitionProgressProvider source) {
+        setSourceProvider(source);
+    }
+
+    /**
+     * Sets the source for the unfold transition progress updates,
+     * it replaces current provider if it is already set
+     * @param provider transition provider that emits transition progress updates
+     */
+    public void setSourceProvider(@Nullable UnfoldTransitionProgressProvider provider) {
+        if (mSource != null) {
+            mSource.removeCallback(this);
+        }
+
+        if (provider != null) {
+            mSource = provider;
+            mSource.addCallback(this);
+        } else {
+            mSource = null;
+        }
+    }
+
+    /**
+     * Allows to notify this provide whether the listeners can play the transition or not.
+     * Call this method with readyToHandleTransition = true when all listeners
+     * are ready to consume the transition progress events.
+     * Call it with readyToHandleTransition = false when listeners can't process the events.
+     */
+    public void setReadyToHandleTransition(boolean isReadyToHandleTransition) {
+        if (mIsTransitionRunning) {
+            if (isReadyToHandleTransition) {
+                mListeners.forEach(TransitionProgressListener::onTransitionStarted);
+
+                if (mLastTransitionProgress != PROGRESS_UNSET) {
+                    mListeners.forEach(listener ->
+                            listener.onTransitionProgress(mLastTransitionProgress));
+                }
+            } else {
+                mIsTransitionRunning = false;
+                mListeners.forEach(TransitionProgressListener::onTransitionFinished);
+            }
+        }
+
+        mIsReadyToHandleTransition = isReadyToHandleTransition;
+    }
+
+    @Override
+    public void addCallback(@NonNull TransitionProgressListener listener) {
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void removeCallback(@NonNull TransitionProgressListener listener) {
+        mListeners.remove(listener);
+    }
+
+    @Override
+    public void destroy() {
+        mSource.removeCallback(this);
+    }
+
+    @Override
+    public void onTransitionStarted() {
+        this.mIsTransitionRunning = true;
+        if (mIsReadyToHandleTransition) {
+            mListeners.forEach(TransitionProgressListener::onTransitionStarted);
+        }
+    }
+
+    @Override
+    public void onTransitionProgress(float progress) {
+        if (mIsReadyToHandleTransition) {
+            mListeners.forEach(listener -> listener.onTransitionProgress(progress));
+        }
+
+        mLastTransitionProgress = progress;
+    }
+
+    @Override
+    public void onTransitionFinished() {
+        if (mIsReadyToHandleTransition) {
+            mListeners.forEach(TransitionProgressListener::onTransitionFinished);
+        }
+
+        mIsTransitionRunning = false;
+        mLastTransitionProgress = PROGRESS_UNSET;
+    }
+}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
index 2ed6328..3a8ee29 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -23,11 +23,19 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Map;
 
 import javax.inject.Inject;
@@ -42,7 +50,7 @@
  * To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
  */
 @SysUISingleton
-public class FeatureFlagManager implements FlagReader, FlagWriter {
+public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
     private static final String TAG = "SysUIFlags";
 
     private static final String SYSPROP_PREFIX = "persist.systemui.flag_";
@@ -54,41 +62,62 @@
     private static final String FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS";
     private final SystemPropertiesHelper mSystemPropertiesHelper;
 
+    private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
+
     @Inject
-    public FeatureFlagManager(SystemPropertiesHelper systemPropertiesHelper, Context context) {
+    public FeatureFlagManager(SystemPropertiesHelper systemPropertiesHelper, Context context,
+            DumpManager dumpManager) {
         mSystemPropertiesHelper = systemPropertiesHelper;
 
         IntentFilter filter = new IntentFilter(ACTION_SET_FLAG);
         context.registerReceiver(mReceiver, filter, FLAGS_PERMISSION, null);
+        dumpManager.registerDumpable(TAG, this);
     }
 
     /** Return a {@link BooleanFlag}'s value. */
     public boolean isEnabled(int id, boolean defaultValue) {
+        if (!mBooleanFlagCache.containsKey(id)) {
+            Boolean result = isEnabledInternal(id);
+            mBooleanFlagCache.put(id, result == null ? defaultValue : result);
+        }
+
+        return mBooleanFlagCache.get(id);
+    }
+
+    /** Returns the stored value or null if not set. */
+    private Boolean isEnabledInternal(int id) {
         String data = mSystemPropertiesHelper.get(keyToSysPropKey(id));
         if (data.isEmpty()) {
-            return defaultValue;
+            return null;
         }
         JSONObject json;
         try {
             json = new JSONObject(data);
             if (!assertType(json, TYPE_BOOLEAN)) {
-                return defaultValue;
+                return null;
             }
+
             return json.getBoolean(FIELD_VALUE);
         } catch (JSONException e) {
-            eraseFlag(id);
-            return defaultValue;
+            eraseInternal(id);  // Don't restart SystemUI in this case.
         }
+        return null;
     }
 
     /** Set whether a given {@link BooleanFlag} is enabled or not. */
     public void setEnabled(int id, boolean value) {
+        Boolean currentValue = isEnabledInternal(id);
+        if (currentValue != null && currentValue == value) {
+            return;
+        }
+
         JSONObject json = new JSONObject();
         try {
             json.put(FIELD_TYPE, TYPE_BOOLEAN);
             json.put(FIELD_VALUE, value);
             mSystemPropertiesHelper.set(keyToSysPropKey(id), json.toString());
-            Log.i(TAG, "Set id " + id + " to  " + value);
+            Log.i(TAG, "Set id " + id + " to " + value);
+            restartSystemUI();
         } catch (JSONException e) {
             // no-op
         }
@@ -96,6 +125,12 @@
 
     /** Erase a flag's overridden value if there is one. */
     public void eraseFlag(int id) {
+        eraseInternal(id);
+        restartSystemUI();
+    }
+
+    /** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */
+    private void eraseInternal(int id) {
         // We can't actually "erase" things from sysprops, but we can set them to empty!
         mSystemPropertiesHelper.set(keyToSysPropKey(id), "");
         Log.i(TAG, "Erase id " + id);
@@ -105,6 +140,12 @@
 
     public void removeListener(Listener run) {}
 
+    private void restartSystemUI() {
+        Log.i(TAG, "Restarting SystemUI");
+        // SysUI starts back when up exited. Is there a better way to do this?
+        System.exit(0);
+    }
+
     private static String keyToSysPropKey(int key) {
         return SYSPROP_PREFIX + key;
     }
@@ -154,4 +195,16 @@
             }
         }
     };
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        ArrayList<String> flagStrings = new ArrayList<>(mBooleanFlagCache.size());
+        for (Map.Entry<Integer, Boolean> entry : mBooleanFlagCache.entrySet()) {
+            flagStrings.add("  sysui_flag_" + entry.getKey() + ": " + entry.getValue());
+        }
+        flagStrings.sort(String.CASE_INSENSITIVE_ORDER);
+        for (String flagString : flagStrings) {
+            pw.println(flagString);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 6fd0c82..3d4e896 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -89,6 +89,7 @@
 
     private int mClockSwitchYAmount;
     @VisibleForTesting boolean mChildrenAreLaidOut = false;
+    private OnPreDrawListener mPreDrawListener;
 
     public KeyguardClockSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -284,15 +285,14 @@
         if (mChildrenAreLaidOut) {
             animateClockChange(clockSize == LARGE);
             mDisplayedClockSize = clockSize;
-        } else {
-            getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    switchToClock(clockSize);
-                    getViewTreeObserver().removeOnPreDrawListener(this);
-                    return true;
-                }
-            });
+        } else if (mPreDrawListener == null) {
+            mPreDrawListener = () -> {
+                switchToClock(clockSize);
+                getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
+                mPreDrawListener = null;
+                return true;
+            };
+            getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
         }
         return true;
     }
@@ -303,6 +303,13 @@
         mChildrenAreLaidOut = true;
     }
 
+    void onViewDetached() {
+        if (mPreDrawListener != null) {
+            getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
+            mPreDrawListener = null;
+        }
+    }
+
     public Paint getPaint() {
         return mClockView.getPaint();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 01976b4..1931c0a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -238,6 +238,7 @@
         }
         mColorExtractor.removeOnColorsChangedListener(mColorsListener);
         mView.setClockPlugin(null, mStatusBarStateController.getState());
+        mView.onViewDetached();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 11eeac2..099dd5d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -27,8 +27,10 @@
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.android.internal.policy.SystemBarUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 
@@ -55,6 +57,8 @@
     private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
     private boolean mBouncerVisible;
     private boolean mAltBouncerShowing;
+    private ViewGroup mContainer;
+    private int mTopMargin;
 
     public KeyguardMessageArea(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -65,6 +69,24 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mContainer = getRootView().findViewById(R.id.keyguard_message_area_container);
+    }
+
+    void onConfigChanged() {
+        final int newTopMargin = SystemBarUtils.getStatusBarHeight(getContext());
+        if (mTopMargin == newTopMargin) {
+            return;
+        }
+        mTopMargin = newTopMargin;
+        ViewGroup.MarginLayoutParams lp =
+                (ViewGroup.MarginLayoutParams) mContainer.getLayoutParams();
+        lp.topMargin = mTopMargin;
+        mContainer.setLayoutParams(lp);
+    }
+
+    @Override
     public void setNextMessageColor(ColorStateList colorState) {
         mNextMessageColorState = colorState;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 51ded3f..05318bb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -48,6 +49,11 @@
 
     private ConfigurationListener mConfigurationListener = new ConfigurationListener() {
         @Override
+        public void onConfigChanged(Configuration newConfig) {
+            mView.onConfigChanged();
+        }
+
+        @Override
         public void onThemeChanged() {
             mView.onThemeChanged();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index ef4353b..68132f4 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -26,6 +26,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
@@ -40,6 +41,17 @@
  * A view positioned under the notification shade.
  */
 public class LockIconView extends FrameLayout implements Dumpable {
+    @IntDef({ICON_NONE, ICON_LOCK, ICON_FINGERPRINT, ICON_UNLOCK})
+    public @interface IconType {}
+
+    public static final int ICON_NONE = -1;
+    public static final int ICON_LOCK = 0;
+    public static final int ICON_FINGERPRINT = 1;
+    public static final int ICON_UNLOCK = 2;
+
+    private @IconType int mIconType;
+    private boolean mAod;
+
     @NonNull private final RectF mSensorRect;
     @NonNull private PointF mLockIconCenter = new PointF(0f, 0f);
     private int mRadius;
@@ -49,6 +61,7 @@
 
     private int mLockIconColor;
     private boolean mUseBackground = false;
+    private float mDozeAmount = 0f;
 
     public LockIconView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -62,11 +75,17 @@
         mBgView = findViewById(R.id.lock_icon_bg);
     }
 
+    void setDozeAmount(float dozeAmount) {
+        mDozeAmount = dozeAmount;
+        updateColorAndBackgroundVisibility();
+    }
+
     void updateColorAndBackgroundVisibility() {
         if (mUseBackground && mLockIcon.getDrawable() != null) {
             mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
                     android.R.attr.textColorPrimary);
             mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
+            mBgView.setAlpha(1f - mDozeAmount);
             mBgView.setVisibility(View.VISIBLE);
         } else {
             mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
@@ -129,10 +148,75 @@
         return mLockIconCenter.y - mRadius;
     }
 
+    /**
+     * Updates the icon its default state where no visual is shown.
+     */
+    public void clearIcon() {
+        updateIcon(ICON_NONE, false);
+    }
+
+    /**
+     * Transition the current icon to a new state
+     * @param icon type (ie: lock icon, unlock icon, fingerprint icon)
+     * @param aod whether to use the aod icon variant (some icons don't have aod variants and will
+     *            therefore show no icon)
+     */
+    public void updateIcon(@IconType int icon, boolean aod) {
+        mIconType = icon;
+        mAod = aod;
+
+        mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
+    }
+
+    private static int[] getLockIconState(@IconType int icon, boolean aod) {
+        if (icon == ICON_NONE) {
+            return new int[0];
+        }
+
+        int[] lockIconState = new int[2];
+        switch (icon) {
+            case ICON_LOCK:
+                lockIconState[0] = android.R.attr.state_first;
+                break;
+            case ICON_FINGERPRINT:
+                lockIconState[0] = android.R.attr.state_middle;
+                break;
+            case ICON_UNLOCK:
+                lockIconState[0] = android.R.attr.state_last;
+                break;
+        }
+
+        if (aod) {
+            lockIconState[1] = android.R.attr.state_single;
+        } else {
+            lockIconState[1] = -android.R.attr.state_single;
+        }
+
+        return lockIconState;
+    }
+
+    private String typeToString(@IconType int type) {
+        switch (type) {
+            case ICON_NONE:
+                return "none";
+            case ICON_LOCK:
+                return "lock";
+            case ICON_FINGERPRINT:
+                return "fingerprint";
+            case ICON_UNLOCK:
+                return "unlock";
+        }
+
+        return "invalid";
+    }
+
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("Center in px (x, y)= (" + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
         pw.println("Radius in pixels: " + mRadius);
         pw.println("topLeft= (" + getX() + ", " + getY() + ")");
+        pw.println("topLeft= (" + getX() + ", " + getY() + ")");
+        pw.println("mIconType=" + typeToString(mIconType));
+        pw.println("mAod=" + mAod);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 321c1a3..3c80a18 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -18,6 +18,9 @@
 
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 
+import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
+import static com.android.keyguard.LockIconView.ICON_LOCK;
+import static com.android.keyguard.LockIconView.ICON_UNLOCK;
 import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset;
@@ -26,8 +29,7 @@
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.AnimatedStateListDrawable;
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -103,11 +105,8 @@
     private boolean mUdfpsEnrolled;
 
     @Nullable private LottieAnimationView mAodFp;
+    @NonNull private final AnimatedStateListDrawable mIcon;
 
-    @NonNull private final AnimatedVectorDrawable mFpToUnlockIcon;
-    @NonNull private final AnimatedVectorDrawable mLockToUnlockIcon;
-    @NonNull private final Drawable mLockIcon;
-    @NonNull private final Drawable mUnlockIcon;
     @NonNull private CharSequence mUnlockedLabel;
     @NonNull private CharSequence mLockedLabel;
     @Nullable private final Vibrator mVibrator;
@@ -133,13 +132,13 @@
     private boolean mShowLockIcon;
 
     // for udfps when strong auth is required or unlocked on AOD
+    private boolean mShowAodLockIcon;
     private boolean mShowAODFpIcon;
     private final int mMaxBurnInOffsetX;
     private final int mMaxBurnInOffsetY;
     private float mInterpolatedDarkAmount;
 
     private boolean mDownDetected;
-    private boolean mDetectedLongPress;
     private final Rect mSensorTouchLocation = new Rect();
 
     @Inject
@@ -177,12 +176,9 @@
         mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
         mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
 
-        mUnlockIcon = resources.getDrawable(R.drawable.ic_unlock, mView.getContext().getTheme());
-        mLockIcon = resources.getDrawable(R.anim.lock_to_unlock, mView.getContext().getTheme());
-        mFpToUnlockIcon = (AnimatedVectorDrawable) resources.getDrawable(
-                R.anim.fp_to_unlock, mView.getContext().getTheme());
-        mLockToUnlockIcon = (AnimatedVectorDrawable) resources.getDrawable(R.anim.lock_to_unlock,
-                mView.getContext().getTheme());
+        mIcon = (AnimatedStateListDrawable)
+                resources.getDrawable(R.drawable.super_lock_icon, mView.getContext().getTheme());
+        mView.setImageDrawable(mIcon);
         mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
         mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
         dumpManager.registerDumpable("LockIconViewController", this);
@@ -256,42 +252,47 @@
             return;
         }
 
+        boolean wasShowingUnlock = mShowUnlockIcon;
         boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon;
-        boolean wasShowingLockIcon = mShowLockIcon;
-        boolean wasShowingUnlockIcon = mShowUnlockIcon;
         mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
                 && (!mUdfpsEnrolled || !mRunningFPS);
         mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
-        mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS;
+        mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
+        mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
 
         final CharSequence prevContentDescription = mView.getContentDescription();
         if (mShowLockIcon) {
-            mView.setImageDrawable(mLockIcon);
-            mView.setVisibility(View.VISIBLE);
+            mView.updateIcon(ICON_LOCK, false);
             mView.setContentDescription(mLockedLabel);
-        } else if (mShowUnlockIcon) {
-            if (!wasShowingUnlockIcon) {
-                if (wasShowingFpIcon) {
-                    mView.setImageDrawable(mFpToUnlockIcon);
-                    mFpToUnlockIcon.forceAnimationOnUI();
-                    mFpToUnlockIcon.start();
-                } else if (wasShowingLockIcon) {
-                    mView.setImageDrawable(mLockToUnlockIcon);
-                    mLockToUnlockIcon.forceAnimationOnUI();
-                    mLockToUnlockIcon.start();
-                } else {
-                    mView.setImageDrawable(mUnlockIcon);
-                }
-            }
             mView.setVisibility(View.VISIBLE);
+        } else if (mShowUnlockIcon) {
+            if (wasShowingFpIcon) {
+                // fp icon was shown by UdfpsView, and now we still want to animate the transition
+                // in this drawable
+                mView.updateIcon(ICON_FINGERPRINT, false);
+            }
+            mView.updateIcon(ICON_UNLOCK, false);
             mView.setContentDescription(mUnlockedLabel);
+            mView.setVisibility(View.VISIBLE);
         } else if (mShowAODFpIcon) {
-            mView.setImageDrawable(null);
+            // AOD fp icon is special cased as a lottie view (it updates for each burn-in offset),
+            // this state shows a transparent view
             mView.setContentDescription(null);
             mAodFp.setVisibility(View.VISIBLE);
             mAodFp.setContentDescription(mCanDismissLockScreen ? mUnlockedLabel : mLockedLabel);
+
+            mView.updateIcon(ICON_FINGERPRINT, true); // this shows no icon
+            mView.setVisibility(View.VISIBLE);
+        } else if (mShowAodLockIcon) {
+            if (wasShowingUnlock) {
+                // transition to the unlock icon first
+                mView.updateIcon(ICON_LOCK, false);
+            }
+            mView.updateIcon(ICON_LOCK, true);
+            mView.setContentDescription(mLockedLabel);
             mView.setVisibility(View.VISIBLE);
         } else {
+            mView.clearIcon();
             mView.setVisibility(View.INVISIBLE);
             mView.setContentDescription(null);
         }
@@ -381,6 +382,11 @@
         pw.println("mUdfpsSupported: " + mUdfpsSupported);
         pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
         pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
+        pw.println(" mIcon: ");
+        for (int state : mIcon.getState()) {
+            pw.print(" " + state);
+        }
+        pw.println();
         pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
         pw.println(" mShowLockIcon: " + mShowLockIcon);
         pw.println(" mShowAODFpIcon: " + mShowAODFpIcon);
@@ -418,6 +424,11 @@
             mAodFp.setProgress(progress);
             mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
         }
+
+        if (mShowAodLockIcon) {
+            mView.setTranslationX(offsetX);
+            mView.setTranslationY(offsetY);
+        }
     }
 
     private void updateIsUdfpsEnrolled() {
@@ -442,6 +453,7 @@
                 @Override
                 public void onDozeAmountChanged(float linear, float eased) {
                     mInterpolatedDarkAmount = eased;
+                    mView.setDozeAmount(eased);
                     updateBurnInOffsets();
                 }
 
@@ -562,7 +574,6 @@
     private final GestureDetector mGestureDetector =
             new GestureDetector(new SimpleOnGestureListener() {
                 public boolean onDown(MotionEvent e) {
-                    mDetectedLongPress = false;
                     if (!isClickable()) {
                         mDownDetected = false;
                         return false;
@@ -587,7 +598,6 @@
                     if (!wasClickableOnDownEvent()) {
                         return;
                     }
-                    mDetectedLongPress = true;
 
                     if (onAffordanceClick() && mVibrator != null) {
                         // only vibrate if the click went through and wasn't intercepted by falsing
@@ -685,8 +695,11 @@
     private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
         @Override
         public void onAllAuthenticatorsRegistered() {
-            updateIsUdfpsEnrolled();
-            updateConfiguration();
+            // must be called from the main thread since it may update the views
+            mExecutor.execute(() -> {
+                updateIsUdfpsEnrolled();
+                updateConfiguration();
+            });
         }
     };
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
index 99e122e..7517dee 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
@@ -91,7 +91,7 @@
         mResources = res;
         mLayoutInflater = inflater;
         mColorExtractor = colorExtractor;
-        mClockPosition = new SmallClockPosition(res);
+        mClockPosition = new SmallClockPosition(inflater.getContext());
     }
 
     private void createViews() {
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index fac923c01..1add1a3 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -91,7 +91,7 @@
         mResources = res;
         mLayoutInflater = inflater;
         mColorExtractor = colorExtractor;
-        mClockPosition = new SmallClockPosition(res);
+        mClockPosition = new SmallClockPosition(inflater.getContext());
     }
 
     private void createViews() {
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 3775628..013cdac 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -41,7 +41,6 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.settings.CurrentUserObservable;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.util.InjectionInflationController;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -125,16 +124,16 @@
     private final int mHeight;
 
     @Inject
-    public ClockManager(Context context, InjectionInflationController injectionInflater,
+    public ClockManager(Context context, LayoutInflater layoutInflater,
             PluginManager pluginManager, SysuiColorExtractor colorExtractor,
             @Nullable DockManager dockManager, BroadcastDispatcher broadcastDispatcher) {
-        this(context, injectionInflater, pluginManager, colorExtractor,
+        this(context, layoutInflater, pluginManager, colorExtractor,
                 context.getContentResolver(), new CurrentUserObservable(broadcastDispatcher),
                 new SettingsWrapper(context.getContentResolver()), dockManager);
     }
 
     @VisibleForTesting
-    ClockManager(Context context, InjectionInflationController injectionInflater,
+    ClockManager(Context context, LayoutInflater layoutInflater,
             PluginManager pluginManager, SysuiColorExtractor colorExtractor,
             ContentResolver contentResolver, CurrentUserObservable currentUserObservable,
             SettingsWrapper settingsWrapper, DockManager dockManager) {
@@ -147,7 +146,6 @@
         mPreviewClocks = new AvailableClocks();
 
         Resources res = context.getResources();
-        LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context));
 
         addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor));
 
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java b/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java
index b304074..4e51b98 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java
@@ -16,10 +16,11 @@
 
 package com.android.keyguard.clock;
 
-import android.content.res.Resources;
+import android.content.Context;
 import android.util.MathUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
 
 /**
@@ -40,11 +41,11 @@
      */
     private float mDarkAmount;
 
-    SmallClockPosition(Resources res) {
-        this(res.getDimensionPixelSize(R.dimen.status_bar_height),
-                res.getDimensionPixelSize(R.dimen.keyguard_lock_padding),
-                res.getDimensionPixelSize(R.dimen.keyguard_lock_height),
-                res.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y)
+    SmallClockPosition(Context context) {
+        this(SystemBarUtils.getStatusBarHeight(context),
+                context.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_padding),
+                context.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_height),
+                context.getResources().getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y)
         );
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 9023d3a..2f5a18e 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -89,8 +89,12 @@
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -102,6 +106,7 @@
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -360,6 +365,11 @@
     @Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
     @Inject Lazy<InternetDialogFactory> mInternetDialogFactory;
     @Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
+    @Inject Lazy<NotificationSectionsManager> mNotificationSectionsManagerLazy;
+    @Inject Lazy<UnlockedScreenOffAnimationController> mUnlockedScreenOffAnimationControllerLazy;
+    @Inject Lazy<AmbientState> mAmbientStateLazy;
+    @Inject Lazy<GroupMembershipManager> mGroupMembershipManagerLazy;
+    @Inject Lazy<GroupExpansionManager> mGroupExpansionManagerLazy;
 
     @Inject
     public Dependency() {
@@ -574,6 +584,12 @@
         mProviders.put(UiEventLogger.class, mUiEventLogger::get);
         mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
         mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get);
+        mProviders.put(NotificationSectionsManager.class, mNotificationSectionsManagerLazy::get);
+        mProviders.put(UnlockedScreenOffAnimationController.class,
+                mUnlockedScreenOffAnimationControllerLazy::get);
+        mProviders.put(AmbientState.class, mAmbientStateLazy::get);
+        mProviders.put(GroupMembershipManager.class, mGroupMembershipManagerLazy::get);
+        mProviders.put(GroupExpansionManager.class, mGroupExpansionManagerLazy::get);
 
         Dependency.setInstance(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index cffc048..d1739aa 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -127,7 +127,12 @@
             setFixedSizeAllowed(true);
             updateSurfaceSize();
 
-            mRenderer.setOnBitmapChanged(this::updateMiniBitmap);
+            mRenderer.setOnBitmapChanged(b -> {
+                mLocalColorsToAdd.addAll(mColorAreas);
+                if (mLocalColorsToAdd.size() > 0) {
+                    updateMiniBitmapAndNotify(b);
+                }
+            });
             getDisplayContext().getSystemService(DisplayManager.class)
                     .registerDisplayListener(this, mWorker.getThreadHandler());
             Trace.endSection();
@@ -171,7 +176,7 @@
                     computeAndNotifyLocalColors(new ArrayList<>(mColorAreas), mMiniBitmap));
         }
 
-        private void updateMiniBitmap(Bitmap b) {
+        private void updateMiniBitmapAndNotify(Bitmap b) {
             if (b == null) return;
             int size = Math.min(b.getWidth(), b.getHeight());
             float scale = 1.0f;
@@ -233,6 +238,7 @@
                 Bitmap bitmap = mMiniBitmap;
                 if (bitmap == null) {
                     mLocalColorsToAdd.addAll(regions);
+                    if (mRenderer != null) mRenderer.use(this::updateMiniBitmapAndNotify);
                 } else {
                     computeAndNotifyLocalColors(regions, bitmap);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 9c10d74..c4147e7 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -161,8 +161,6 @@
     private boolean mPendingRotationChange;
     private boolean mIsRoundedCornerMultipleRadius;
     private boolean mIsPrivacyDotEnabled;
-    private int mStatusBarHeightPortrait;
-    private int mStatusBarHeightLandscape;
     private Drawable mRoundedCornerDrawable;
     private Drawable mRoundedCornerDrawableTop;
     private Drawable mRoundedCornerDrawableBottom;
@@ -315,7 +313,6 @@
 
     private void setupDecorations() {
         if (hasRoundedCorners() || shouldDrawCutout() || mIsPrivacyDotEnabled) {
-            updateStatusBarHeight();
             final DisplayCutout cutout = getCutout();
             for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
                 if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
@@ -326,7 +323,8 @@
                 }
             }
 
-            if (mIsPrivacyDotEnabled) {
+            if (mTopLeftDot != null && mTopRightDot != null && mBottomLeftDot != null
+                    && mBottomRightDot != null) {
                 // Overlays have been created, send the dots to the controller
                 //TODO: need a better way to do this
                 mDotViewController.initialize(
@@ -430,7 +428,7 @@
         if (mOverlays[pos] != null) {
             return;
         }
-        mOverlays[pos] = overlayForPosition(pos);
+        mOverlays[pos] = overlayForPosition(pos, cutout);
 
         mCutoutViews[pos] = new DisplayCutoutView(mContext, pos, this);
         ((ViewGroup) mOverlays[pos]).addView(mCutoutViews[pos]);
@@ -462,10 +460,46 @@
     /**
      * Allow overrides for top/bottom positions
      */
-    private View overlayForPosition(@BoundsPosition int pos) {
+    private View overlayForPosition(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
         final int layoutId = (pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_TOP)
                 ? R.layout.rounded_corners_top : R.layout.rounded_corners_bottom;
-        return LayoutInflater.from(mContext).inflate(layoutId, null);
+        final ViewGroup vg = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutId, null);
+        initPrivacyDotView(vg, pos, cutout);
+        return vg;
+    }
+
+    private void initPrivacyDotView(@NonNull ViewGroup viewGroup, @BoundsPosition int pos,
+            @Nullable DisplayCutout cutout) {
+        final View left = viewGroup.findViewById(R.id.privacy_dot_left_container);
+        final View right = viewGroup.findViewById(R.id.privacy_dot_right_container);
+        if (!shouldShowPrivacyDot(pos, cutout)) {
+            viewGroup.removeView(left);
+            viewGroup.removeView(right);
+            return;
+        }
+
+        switch (pos) {
+            case BOUNDS_POSITION_LEFT: {
+                mTopLeftDot = left;
+                mBottomLeftDot = right;
+                break;
+            }
+            case BOUNDS_POSITION_TOP: {
+                mTopLeftDot = left;
+                mTopRightDot = right;
+                break;
+            }
+            case BOUNDS_POSITION_RIGHT: {
+                mTopRightDot = left;
+                mBottomRightDot = right;
+                break;
+            }
+            case BOUNDS_POSITION_BOTTOM: {
+                mBottomLeftDot = left;
+                mBottomRightDot = right;
+                break;
+            }
+        }
     }
 
     private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
@@ -483,8 +517,6 @@
         if (mCutoutViews != null && mCutoutViews[pos] != null) {
             mCutoutViews[pos].setRotation(mRotation);
         }
-
-        updatePrivacyDotView(pos, cutout);
     }
 
     @VisibleForTesting
@@ -671,14 +703,6 @@
         }
     }
 
-    private void updateStatusBarHeight() {
-        mStatusBarHeightLandscape = mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height_landscape);
-        mStatusBarHeightPortrait = mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height_portrait);
-        mDotViewController.setStatusBarHeights(mStatusBarHeightPortrait, mStatusBarHeightLandscape);
-    }
-
     private void updateRoundedCornerRadii() {
         // We should eventually move to just using the intrinsic size of the drawables since
         // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
@@ -813,26 +837,6 @@
         }
     }
 
-    private void updatePrivacyDotView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
-        final ViewGroup viewGroup = (ViewGroup) mOverlays[pos];
-
-        final View left = viewGroup.findViewById(R.id.privacy_dot_left_container);
-        final View right = viewGroup.findViewById(R.id.privacy_dot_right_container);
-        if (shouldShowPrivacyDot(pos, cutout)) {
-            // TODO (b/201481944) Privacy Dots pos and var are wrong with long side cutout enable
-            if (pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_TOP) {
-                mTopLeftDot = left;
-                mTopRightDot = right;
-            } else {
-                mBottomLeftDot = left;
-                mBottomRightDot = right;
-            }
-        } else {
-            viewGroup.removeView(left);
-            viewGroup.removeView(right);
-        }
-    }
-
     private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) {
         final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
         switch (rotatedPos) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 2f88291..294d1f4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -174,7 +174,8 @@
                 mReceiver,
                 mReceiver.createIntentFilter(),
                 PERMISSION_SELF,
-                null);
+                null,
+                Context.RECEIVER_EXPORTED);
         registerActions();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
index d8e80fe..0d7551f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
@@ -30,7 +30,7 @@
 /**
  * A span that turns the text wrapped by annotation tag into the clickable link text.
  */
-class AnnotationLinkSpan extends ClickableSpan {
+public class AnnotationLinkSpan extends ClickableSpan {
     private final Optional<View.OnClickListener> mClickListener;
 
     private AnnotationLinkSpan(View.OnClickListener listener) {
@@ -50,7 +50,7 @@
      * @param linkInfos used to attach the click action into the corresponding span
      * @return the text attached with the span
      */
-    static CharSequence linkify(CharSequence text, LinkInfo... linkInfos) {
+    public static CharSequence linkify(CharSequence text, LinkInfo... linkInfos) {
         final SpannableString msg = new SpannableString(text);
         final Annotation[] spans =
                 msg.getSpans(/* queryStart= */ 0, msg.length(), Annotation.class);
@@ -78,12 +78,12 @@
     /**
      * Data class to store the annotation and the click action.
      */
-    static class LinkInfo {
-        static final String DEFAULT_ANNOTATION = "link";
+    public static class LinkInfo {
+        public static final String DEFAULT_ANNOTATION = "link";
         private final Optional<String> mAnnotation;
         private final Optional<View.OnClickListener> mListener;
 
-        LinkInfo(@NonNull String annotation, View.OnClickListener listener) {
+        public LinkInfo(@NonNull String annotation, View.OnClickListener listener) {
             mAnnotation = Optional.of(annotation);
             mListener = Optional.ofNullable(listener);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 069a1b2..f7462b6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -889,6 +889,7 @@
         }
 
         if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
+            mKeyguardViewManager.showBouncer(true);
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 37a6cfa..0a93298 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -330,7 +330,7 @@
                 || mTestHarness
                 || mDataProvider.isJustUnlockedWithFace()
                 || mDataProvider.isDocked()
-                || mAccessibilityManager.isEnabled();
+                || mAccessibilityManager.isTouchExplorationEnabled();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index 5f75d86..31dd82d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -20,10 +20,13 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
+import com.android.systemui.idle.AmbientLightModeMonitor;
+import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
 import com.android.systemui.idle.dagger.IdleViewComponent;
 
 import javax.inject.Named;
 
+import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 
@@ -44,4 +47,13 @@
         FrameLayout view = new FrameLayout(context);
         return view;
     }
+
+    /**
+     * Provides LightSensorEventsDebounceAlgorithm as an instance to DebounceAlgorithm interface.
+     * @param algorithm the instance of algorithm that is bound to the interface.
+     * @return the interface that is bound to.
+     */
+    @Binds
+    AmbientLightModeMonitor.DebounceAlgorithm ambientLightDebounceAlgorithm(
+            LightSensorEventsDebounceAlgorithm algorithm);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index aac03f8..a9a4da8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -25,7 +25,6 @@
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.people.PeopleProvider;
 import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.InjectionInflationController;
 import com.android.wm.shell.ShellCommandHandler;
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
@@ -147,11 +146,6 @@
     InitController getInitController();
 
     /**
-     * ViewInstanceCreator generates all Views that need injection.
-     */
-    InjectionInflationController.ViewInstanceCreator.Factory createViewInstanceCreatorFactory();
-
-    /**
      * Member injection into the supplied argument.
      */
     void inject(SystemUIAppComponentFactory factory);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 0923caaf..50d2dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -187,9 +187,13 @@
         return new Recents(context, recentsImplementation, commandQueue);
     }
 
-    @Binds
-    abstract DeviceProvisionedController bindDeviceProvisionedController(
-            DeviceProvisionedControllerImpl deviceProvisionedController);
+    @SysUISingleton
+    @Provides
+    static DeviceProvisionedController bindDeviceProvisionedController(
+            DeviceProvisionedControllerImpl deviceProvisionedController) {
+        deviceProvisionedController.init();
+        return deviceProvisionedController;
+    }
 
     @Binds
     abstract KeyguardViewController bindKeyguardViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index e57e991..e845e2a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -76,7 +76,6 @@
 import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
 import com.android.systemui.tuner.dagger.TunerModule;
 import com.android.systemui.user.UserModule;
-import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
 import com.android.systemui.util.dagger.UtilModule;
 import com.android.systemui.util.sensors.SensorModule;
@@ -218,11 +217,9 @@
 
     @Provides
     @SysUISingleton
-    static StatusBarWindowView providesStatusBarWindowView(Context context,
-            InjectionInflationController injectionInflationController) {
+    static StatusBarWindowView providesStatusBarWindowView(LayoutInflater layoutInflater) {
         StatusBarWindowView view =
-                (StatusBarWindowView) injectionInflationController.injectable(
-                        LayoutInflater.from(context)).inflate(R.layout.super_status_bar,
+                (StatusBarWindowView) layoutInflater.inflate(R.layout.super_status_bar,
                         /* root= */ null);
         if (view == null) {
             throw new IllegalStateException(
diff --git a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt
index ed11db5..f58d628 100644
--- a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt
@@ -81,7 +81,7 @@
         val demoFilter = IntentFilter()
         demoFilter.addAction(ACTION_DEMO)
         context.registerReceiverAsUser(broadcastReceiver, UserHandle.ALL, demoFilter,
-                android.Manifest.permission.DUMP, null)
+                android.Manifest.permission.DUMP, null, Context.RECEIVER_EXPORTED)
     }
 
     override fun addCallback(listener: DemoMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java
index 85baed4..78f0b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java
@@ -16,23 +16,48 @@
 
 package com.android.systemui.flags;
 
+import android.util.SparseBooleanArray;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 import javax.inject.Inject;
 
 /**
  * Default implementation of the a Flag manager that returns default values for release builds
+ *
+ * There's a version of this file in src-debug which allows overriding, and has documentation about
+ * how to set flags.
  */
 @SysUISingleton
-public class FeatureFlagManager implements FlagReader, FlagWriter {
+public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
+    SparseBooleanArray mAccessedFlags = new SparseBooleanArray();
     @Inject
-    public FeatureFlagManager() {}
+    public FeatureFlagManager(DumpManager dumpManager) {
+        dumpManager.registerDumpable("SysUIFlags", this);
+    }
     public boolean isEnabled(String key, boolean defaultValue) {
         return defaultValue;
     }
     public boolean isEnabled(int key, boolean defaultValue) {
+        mAccessedFlags.append(key, defaultValue);
         return defaultValue;
     }
     public void setEnabled(String key, boolean value) {}
     public void setEnabled(int key, boolean value) {}
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        int size = mAccessedFlags.size();
+        for (int i = 0; i < size; i++) {
+            pw.println("  sysui_flag_" + mAccessedFlags.keyAt(i)
+                    + ": " + mAccessedFlags.valueAt(i));
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index a51ec54..729730c 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -66,6 +66,13 @@
         mOnBitmapUpdated = c;
     }
 
+    /**
+     * @hide
+     */
+    public void use(Consumer<Bitmap> c) {
+        mTexture.use(c);
+    }
+
     @Override
     public boolean isWcgContent() {
         return mTexture.isWcgContent();
diff --git a/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt b/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt
index 8564497..fa00dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt
@@ -24,17 +24,17 @@
 import android.util.Log
 import com.android.systemui.util.sensors.AsyncSensorManager
 import javax.inject.Inject
-import kotlin.properties.Delegates
 
 /**
  * Monitors ambient light signals, applies a debouncing algorithm, and produces the current
- * [AmbientLightMode].
+ * ambient light mode.
  *
- * For debouncer behavior, refer to go/titan-light-sensor-debouncer.
- *
+ * @property algorithm the debounce algorithm which transforms light sensor events into an
+ * ambient light mode.
  * @property sensorManager the sensor manager used to register sensor event updates.
  */
 class AmbientLightModeMonitor @Inject constructor(
+    private val algorithm: DebounceAlgorithm,
     private val sensorManager: AsyncSensorManager
 ) {
     companion object {
@@ -47,41 +47,27 @@
     }
 
     // Light sensor used to detect ambient lighting conditions.
-    private val lightSensor: Sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
-
-    // Registered callback, which gets triggered when the ambient light mode changes.
-    private var callback: Callback? = null
+    private val lightSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
 
     // Represents all ambient light modes.
     @Retention(AnnotationRetention.SOURCE)
     @IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED)
     annotation class AmbientLightMode
 
-    // The current ambient light mode.
-    @AmbientLightMode private var mode: Int by Delegates.observable(AMBIENT_LIGHT_MODE_UNDECIDED
-    ) { _, old, new ->
-        if (old != new) {
-            callback?.onChange(new)
-        }
-    }
-
     /**
      * Start monitoring the current ambient light mode.
      *
-     * @param callback callback that gets triggered when the ambient light mode changes. It also
-     * gets triggered immediately to update the current value when this function is called.
+     * @param callback callback that gets triggered when the ambient light mode changes.
      */
     fun start(callback: Callback) {
         if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")
 
-        if (this.callback != null) {
-            if (DEBUG) Log.w(TAG, "already started")
+        if (lightSensor == null) {
+            if (DEBUG) Log.w(TAG, "light sensor not available")
             return
         }
 
-        this.callback = callback
-        callback.onChange(mode)
-
+        algorithm.start(callback)
         sensorManager.registerListener(mSensorEventListener, lightSensor,
                 SensorManager.SENSOR_DELAY_NORMAL)
     }
@@ -92,12 +78,7 @@
     fun stop() {
         if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")
 
-        if (callback == null) {
-            if (DEBUG) Log.w(TAG, "haven't started")
-            return
-        }
-
-        callback = null
+        algorithm.stop()
         sensorManager.unregisterListener(mSensorEventListener)
     }
 
@@ -108,9 +89,7 @@
                 return
             }
 
-            // TODO(b/201657509): add debouncing logic.
-            val shouldBeLowLight = event.values[0] < 10
-            mode = if (shouldBeLowLight) AMBIENT_LIGHT_MODE_DARK else AMBIENT_LIGHT_MODE_LIGHT
+            algorithm.onUpdateLightSensorEvent(event.values[0])
         }
 
         override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
@@ -124,4 +103,14 @@
     interface Callback {
         fun onChange(@AmbientLightMode mode: Int)
     }
+
+    /**
+     * Interface of the algorithm that transforms light sensor events to an ambient light mode.
+     */
+    interface DebounceAlgorithm {
+        // Setting Callback to nullable so mockito can verify without throwing NullPointerException.
+        fun start(callback: Callback?)
+        fun stop()
+        fun onUpdateLightSensorEvent(value: Float)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt b/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt
new file mode 100644
index 0000000..7b06b96
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt
@@ -0,0 +1,284 @@
+/*
+ * 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.systemui.idle
+
+import android.content.res.Resources
+import android.util.Log
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
+import javax.inject.Inject
+
+/**
+ * An algorithm that receives light sensor events, debounces the signals, and transforms into an
+ * ambient light mode: light, dark, or undecided.
+ *
+ * More about the algorithm at go/titan-light-sensor-debouncer.
+ */
+class LightSensorEventsDebounceAlgorithm @Inject constructor(
+    @Main private val executor: DelayableExecutor,
+    @Main resources: Resources
+) : AmbientLightModeMonitor.DebounceAlgorithm {
+    companion object {
+        private const val TAG = "LightDebounceAlgorithm"
+        private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+    }
+
+    // The ambient mode is considered light mode when the light sensor value increases exceeding
+    // this value.
+    private val lightModeThreshold =
+            resources.getInteger(R.integer.config_ambientLightModeThreshold)
+
+    // The ambient mode is considered dark mode when the light sensor value drops below this
+    // value.
+    private val darkModeThreshold = resources.getInteger(R.integer.config_ambientDarkModeThreshold)
+
+    // Each sample for calculating light mode contains light sensor events collected for this
+    // duration of time in milliseconds.
+    private val lightSamplingSpanMillis =
+            resources.getInteger(R.integer.config_ambientLightModeSamplingSpanMillis)
+
+    // Each sample for calculating dark mode contains light sensor events collected for this
+    // duration of time in milliseconds.
+    private val darkSamplingSpanMillis =
+            resources.getInteger(R.integer.config_ambientDarkModeSamplingSpanMillis)
+
+    // The calculations for light mode is performed at this frequency in milliseconds.
+    private val lightSamplingFrequencyMillis =
+            resources.getInteger(R.integer.config_ambientLightModeSamplingFrequencyMillis)
+
+    // The calculations for dark mode is performed at this frequency in milliseconds.
+    private val darkSamplingFrequencyMillis =
+            resources.getInteger(R.integer.config_ambientDarkModeSamplingFrequencyMillis)
+
+    // Registered callback, which gets triggered when the ambient light mode changes.
+    private var callback: AmbientLightModeMonitor.Callback? = null
+
+    // Queue of bundles used for calculating [isLightMode], ordered from oldest to latest.
+    val bundlesQueueLightMode = ArrayList<ArrayList<Float>>()
+
+    // Queue of bundles used for calculating [isDarkMode], ordered from oldest to latest
+    val bundlesQueueDarkMode = ArrayList<ArrayList<Float>>()
+
+    // The current ambient light mode.
+    @AmbientLightModeMonitor.AmbientLightMode
+    var mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
+        set(value) {
+            if (field == value) return
+            field = value
+
+            if (DEBUG) Log.d(TAG, "ambient light mode changed to $value")
+
+            callback?.onChange(value)
+        }
+
+    // The latest claim of whether it should be light mode.
+    var isLightMode = false
+        set(value) {
+            if (field == value) return
+            field = value
+
+            if (DEBUG) Log.d(TAG, "isLightMode: $value")
+
+            mode = when {
+                isDarkMode -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
+                value -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
+                else -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
+            }
+        }
+
+    // The latest claim of whether it should be dark mode.
+    var isDarkMode = false
+        set(value) {
+            if (field == value) return
+            field = value
+
+            if (DEBUG) Log.d(TAG, "isDarkMode: $value")
+
+            mode = when {
+                value -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
+                isLightMode -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
+                else -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
+            }
+        }
+
+    // The latest average of the light mode bundle.
+    var bundleAverageLightMode = 0.0
+        set(value) {
+            field = value
+
+            if (DEBUG) Log.d(TAG, "light mode average: $value")
+
+            isLightMode = value > lightModeThreshold
+        }
+
+    // The latest average of the dark mode bundle.
+    var bundleAverageDarkMode = 0.0
+        set(value) {
+            field = value
+
+            if (DEBUG) Log.d(TAG, "dark mode average: $value")
+
+            isDarkMode = value < darkModeThreshold
+        }
+
+    // The latest bundle for calculating light mode claim.
+    var bundleLightMode = ArrayList<Float>()
+        set(value) {
+            field = value
+
+            val average = value.average()
+
+            if (!average.isNaN()) {
+                bundleAverageLightMode = average
+            }
+        }
+
+    // The latest bundle for calculating dark mode claim.
+    var bundleDarkMode = ArrayList<Float>()
+        set(value) {
+            field = value
+
+            val average = value.average()
+
+            if (!average.isNaN()) {
+                bundleAverageDarkMode = average
+            }
+        }
+
+    // The latest light level from light sensor event updates.
+    var lightSensorLevel = 0.0f
+        set(value) {
+            field = value
+
+            bundlesQueueLightMode.forEach { bundle -> bundle.add(value) }
+            bundlesQueueDarkMode.forEach { bundle -> bundle.add(value) }
+        }
+
+    // Creates a new bundle that collects light sensor events for calculating the light mode claim,
+    // and adds it to the end of the queue. It schedules a call to dequeue this bundle after
+    // [LIGHT_SAMPLING_SPAN_MILLIS]. Once started, it also repeatedly calls itself at
+    // [LIGHT_SAMPLING_FREQUENCY_MILLIS].
+    val enqueueLightModeBundle: Runnable = object : Runnable {
+        override fun run() {
+            if (DEBUG) Log.d(TAG, "enqueueing a light mode bundle")
+
+            bundlesQueueLightMode.add(ArrayList())
+
+            executor.executeDelayed(dequeueLightModeBundle, lightSamplingSpanMillis.toLong())
+            executor.executeDelayed(this, lightSamplingFrequencyMillis.toLong())
+        }
+    }
+
+    // Creates a new bundle that collects light sensor events for calculating the dark mode claim,
+    // and adds it to the end of the queue. It schedules a call to dequeue this bundle after
+    // [DARK_SAMPLING_SPAN_MILLIS]. Once started, it also repeatedly calls itself at
+    // [DARK_SAMPLING_FREQUENCY_MILLIS].
+    val enqueueDarkModeBundle: Runnable = object : Runnable {
+        override fun run() {
+            if (DEBUG) Log.d(TAG, "enqueueing a dark mode bundle")
+
+            bundlesQueueDarkMode.add(ArrayList())
+
+            executor.executeDelayed(dequeueDarkModeBundle, darkSamplingSpanMillis.toLong())
+            executor.executeDelayed(this, darkSamplingFrequencyMillis.toLong())
+        }
+    }
+
+    // Collects the oldest bundle from the light mode bundles queue, and as a result triggering a
+    // calculation of the light mode claim.
+    val dequeueLightModeBundle: Runnable = object : Runnable {
+        override fun run() {
+            if (bundlesQueueLightMode.isEmpty()) return
+
+            bundleLightMode = bundlesQueueLightMode.removeAt(0)
+
+            if (DEBUG) Log.d(TAG, "dequeued a light mode bundle of size ${bundleLightMode.size}")
+        }
+    }
+
+    // Collects the oldest bundle from the dark mode bundles queue, and as a result triggering a
+    // calculation of the dark mode claim.
+    val dequeueDarkModeBundle: Runnable = object : Runnable {
+        override fun run() {
+            if (bundlesQueueDarkMode.isEmpty()) return
+
+            bundleDarkMode = bundlesQueueDarkMode.removeAt(0)
+
+            if (DEBUG) Log.d(TAG, "dequeued a dark mode bundle of size ${bundleDarkMode.size}")
+        }
+    }
+
+    /**
+     * Start the algorithm.
+     *
+     * @param callback callback that gets triggered when the ambient light mode changes.
+     */
+    override fun start(callback: AmbientLightModeMonitor.Callback?) {
+        if (DEBUG) Log.d(TAG, "start algorithm")
+
+        if (callback == null) {
+            if (DEBUG) Log.w(TAG, "callback is null")
+            return
+        }
+
+        if (this.callback != null) {
+            if (DEBUG) Log.w(TAG, "already started")
+            return
+        }
+
+        this.callback = callback
+
+        executor.execute(enqueueLightModeBundle)
+        executor.execute(enqueueDarkModeBundle)
+    }
+
+    /**
+     * Stop the algorithm.
+     */
+    override fun stop() {
+        if (DEBUG) Log.d(TAG, "stop algorithm")
+
+        if (callback == null) {
+            if (DEBUG) Log.w(TAG, "haven't started")
+            return
+        }
+
+        callback = null
+
+        // Resets bundle queues.
+        bundlesQueueLightMode.clear()
+        bundlesQueueDarkMode.clear()
+
+        // Resets ambient light mode.
+        mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
+    }
+
+    /**
+     * Update the light sensor event value.
+     *
+     * @param value light sensor update value.
+     */
+    override fun onUpdateLightSensorEvent(value: Float) {
+        if (callback == null) {
+            if (DEBUG) Log.w(TAG, "ignore light sensor event because algorithm not started")
+            return
+        }
+
+        lightSensorLevel = value
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 3c58079..01a0f27 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -102,7 +102,7 @@
             "persist.wm.enable_remote_keyguard_animation";
 
     private static final int sEnableRemoteKeyguardAnimation =
-            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 0);
+            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 1);
 
     /**
      * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index e51b602..2cc564b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -62,7 +62,7 @@
  * The dismiss amount is the inverse of the notification panel expansion, which decreases as the
  * lock screen is swiped away.
  */
-const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.1f
+const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.25f
 
 /**
  * Dismiss amount at which to complete the keyguard exit animation and hide the keyguard.
@@ -70,7 +70,7 @@
  * The dismiss amount is the inverse of the notification panel expansion, which decreases as the
  * lock screen is swiped away.
  */
-const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.3f
+const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.4f
 
 /**
  * Initiates, controls, and ends the keyguard unlock animation.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 125b87b..113ba59 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -16,14 +16,10 @@
 
 package com.android.systemui.media.dialog;
 
-import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
-
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
-import android.text.SpannableString;
 import android.text.TextUtils;
-import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -99,23 +95,6 @@
         return mController.getMediaDevices().size();
     }
 
-    @Override
-    CharSequence getItemTitle(MediaDevice device) {
-        if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
-                && !device.isConnected()) {
-            final CharSequence deviceName = device.getName();
-            // Append status to title only for the disconnected Bluetooth device.
-            final SpannableString spannableTitle = new SpannableString(
-                    mContext.getString(R.string.media_output_dialog_disconnected, deviceName));
-            spannableTitle.setSpan(new ForegroundColorSpan(
-                    Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorSecondary)),
-                    deviceName.length(),
-                    spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
-            return spannableTitle;
-        }
-        return super.getItemTitle(device);
-    }
-
     class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder {
 
         MediaDeviceViewHolder(View view) {
@@ -166,6 +145,14 @@
                             false /* showProgressBar */, false /* showSubtitle */);
                     initSeekbar(device);
                     mCurrentActivePosition = position;
+                } else if (
+                        device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
+                                && !device.isConnected()) {
+                    setTwoLineLayout(device, false /* bFocused */,
+                            false /* showSeekBar */, false /* showProgressBar */,
+                            true /* showSubtitle */);
+                    mSubTitleText.setText(R.string.media_output_dialog_disconnected);
+                    mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
                 } else {
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
                     mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
@@ -175,7 +162,6 @@
 
         @Override
         void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
-            super.onBind(customizedItem, topMargin, bottomMargin);
             if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
                 mCheckBox.setVisibility(View.GONE);
                 mAddIcon.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 1ffc2c4..868193b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -153,9 +153,7 @@
             });
         }
 
-        void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
-            // TODO (b/201718621): clean up method after adjustment
-        }
+        abstract void onBind(int customizedItem, boolean topMargin, boolean bottomMargin);
 
         void setSingleLineLayout(CharSequence title, boolean bFocused) {
             mTwoLineLayout.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
index 11d76db..a201c07 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -126,7 +126,6 @@
 
         @Override
         void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
-            super.onBind(customizedItem, topMargin, bottomMargin);
             if (customizedItem == CUSTOMIZED_ITEM_GROUP) {
                 setTwoLineLayout(mContext.getText(R.string.media_output_dialog_group),
                         true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */,
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 8c66ba3..4e1e1cd 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -749,7 +749,7 @@
             filter.addAction(ACTION_AUTO_SAVER_NO_THANKS);
             filter.addAction(ACTION_DISMISS_AUTO_SAVER_SUGGESTION);
             mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
-                    android.Manifest.permission.DEVICE_POWER, mHandler);
+                    android.Manifest.permission.DEVICE_POWER, mHandler, Context.RECEIVER_EXPORTED);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 3fc4f50..8588ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -32,6 +32,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.util.Utils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -155,8 +156,7 @@
             QuickStatusBarHeaderController quickStatusBarHeaderController) {
         mQSPanelContainer.setPaddingRelative(
                 getPaddingStart(),
-                mContext.getResources()
-                        .getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height),
+                Utils.getQsHeaderSystemIconsAreaHeight(mContext),
                 getPaddingEnd(),
                 getPaddingBottom()
         );
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 58a942a..d43404b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -40,6 +40,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
@@ -164,8 +165,7 @@
     public void updateResources() {
         updateDetailText();
         MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
-        lp.topMargin = mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.quick_qs_offset_height);
+        lp.topMargin = SystemBarUtils.getQuickQsOffsetHeight(mContext);
         setLayoutParams(lp);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 17c2fd0..89bbcf5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -37,9 +37,9 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.FalsingManager;
@@ -54,7 +54,6 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.util.LifecycleFragment;
 import com.android.systemui.util.Utils;
 
@@ -95,7 +94,6 @@
     private ImageView mQsDragHandler;
 
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
-    private final InjectionInflationController mInjectionInflater;
     private final CommandQueue mCommandQueue;
     private final QSDetailDisplayer mQsDetailDisplayer;
     private final MediaHost mQsMediaHost;
@@ -145,7 +143,7 @@
 
     @Inject
     public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
-            InjectionInflationController injectionInflater, QSTileHost qsTileHost,
+            QSTileHost qsTileHost,
             StatusBarStateController statusBarStateController, CommandQueue commandQueue,
             QSDetailDisplayer qsDetailDisplayer, @Named(QS_PANEL) MediaHost qsMediaHost,
             @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
@@ -153,7 +151,6 @@
             QSFragmentComponent.Factory qsComponentFactory,
             FalsingManager falsingManager, DumpManager dumpManager) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
-        mInjectionInflater = injectionInflater;
         mCommandQueue = commandQueue;
         mQsDetailDisplayer = qsDetailDisplayer;
         mQsMediaHost = qsMediaHost;
@@ -170,9 +167,8 @@
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
             Bundle savedInstanceState) {
-        inflater = mInjectionInflater.injectable(
-                inflater.cloneInContext(new ContextThemeWrapper(getContext(),
-                        R.style.Theme_SystemUI_QuickSettings)));
+        inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
+                R.style.Theme_SystemUI_QuickSettings));
         return inflater.inflate(R.layout.qs_panel, container, false);
     }
 
@@ -574,7 +570,7 @@
         } else if (progress > 0 && view.getVisibility() != View.VISIBLE) {
             view.setVisibility((View.VISIBLE));
         }
-        float alpha = Interpolators.getNotificationScrimAlpha(progress, true /* uiContent */);
+        float alpha = ShadeInterpolation.getContentAlpha(progress);
         view.setAlpha(alpha);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a81d3c6..071e053 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -34,6 +34,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.policy.SystemBarUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.battery.BatteryMeterView;
@@ -240,8 +241,7 @@
         mRoundedCornerPadding = resources.getDimensionPixelSize(
                 R.dimen.rounded_corner_content_padding);
 
-        int qsOffsetHeight = resources.getDimensionPixelSize(
-                com.android.internal.R.dimen.quick_qs_offset_height);
+        int qsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mContext);
 
         mDatePrivacyView.getLayoutParams().height =
                 Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index c692252..9ebdb1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -36,6 +36,7 @@
 import com.android.systemui.plugins.qs.QSContainerController;
 import com.android.systemui.qs.QSDetailClipper;
 import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.util.Utils;
 
 /**
  * Allows full-screen customization of QS, through show() and hide().
@@ -84,8 +85,7 @@
 
     void updateResources() {
         LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
-        lp.height = mContext.getResources()
-                .getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height);
+        lp.height = Utils.getQsHeaderSystemIconsAreaHeight(mContext);
         mTransparentView.setLayoutParams(lp);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 11430d9..15b78e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -36,6 +36,7 @@
 import android.telephony.TelephonyManager;
 import android.text.Html;
 import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -63,12 +64,15 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.wifitrackerlib.WifiEntry;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
@@ -82,6 +86,7 @@
     static final long PROGRESS_DELAY_MS = 2000L;
 
     private final Handler mHandler;
+    private final Executor mBackgroundExecutor;
     private final LinearLayoutManager mLayoutManager;
 
     @VisibleForTesting
@@ -110,6 +115,8 @@
     private LinearLayout mTurnWifiOnLayout;
     private LinearLayout mEthernetLayout;
     private TextView mWifiToggleTitleText;
+    private LinearLayout mWifiScanNotifyLayout;
+    private TextView mWifiScanNotifyText;
     private LinearLayout mSeeAllLayout;
     private RecyclerView mWifiRecyclerView;
     private ImageView mConnectedWifiIcon;
@@ -154,13 +161,14 @@
     public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
             InternetDialogController internetDialogController, boolean canConfigMobileData,
             boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
-            @Main Handler handler) {
+            @Main Handler handler, @Background Executor executor) {
         super(context, R.style.Theme_SystemUI_Dialog_Internet);
         if (DEBUG) {
             Log.d(TAG, "Init InternetDialog");
         }
         mContext = context;
         mHandler = handler;
+        mBackgroundExecutor = executor;
         mInternetDialogFactory = internetDialogFactory;
         mInternetDialogController = internetDialogController;
         mSubscriptionManager = mInternetDialogController.getSubscriptionManager();
@@ -220,6 +228,8 @@
         mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
         mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
         mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
+        mWifiScanNotifyLayout = mDialogView.requireViewById(R.id.wifi_scan_notify_layout);
+        mWifiScanNotifyText = mDialogView.requireViewById(R.id.wifi_scan_notify_text);
         mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
         mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
         mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
@@ -293,7 +303,13 @@
         dismiss();
     }
 
-    void updateDialog() {
+    /**
+     * Update the internet dialog when receiving the callback.
+     *
+     * @param shouldUpdateMobileNetwork {@code true} for update the mobile network layout,
+     * otherwise {@code false}.
+     */
+    void updateDialog(boolean shouldUpdateMobileNetwork) {
         if (DEBUG) {
             Log.d(TAG, "updateDialog");
         }
@@ -303,8 +319,10 @@
             mInternetDialogSubTitle.setText(getSubtitleText());
         }
         updateEthernet();
-        setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular()
-                || mInternetDialogController.isCarrierNetworkActive());
+        if (shouldUpdateMobileNetwork) {
+            setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular()
+                    || mInternetDialogController.isCarrierNetworkActive());
+        }
 
         if (!mCanConfigWifi) {
             return;
@@ -313,8 +331,10 @@
         showProgressBar();
         final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked();
         final boolean isWifiEnabled = mWifiManager.isWifiEnabled();
+        final boolean isWifiScanEnabled = mWifiManager.isScanAlwaysAvailable();
         updateWifiToggle(isWifiEnabled, isDeviceLocked);
         updateConnectedWifi(isWifiEnabled, isDeviceLocked);
+        updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
 
         final int visibility = (isDeviceLocked || !isWifiEnabled || mWifiEntriesCount <= 0)
                 ? View.GONE : View.VISIBLE;
@@ -371,7 +391,13 @@
             } else {
                 mMobileSummaryText.setVisibility(View.GONE);
             }
-            mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
+
+            mBackgroundExecutor.execute(() -> {
+                Drawable drawable = getSignalStrengthDrawable();
+                mHandler.post(() -> {
+                    mSignalIcon.setImageDrawable(drawable);
+                });
+            });
             mMobileTitleText.setTextAppearance(isCarrierNetworkConnected
                     ? R.style.TextAppearance_InternetDialog_Active
                     : R.style.TextAppearance_InternetDialog);
@@ -411,6 +437,24 @@
                 mContext.getColor(R.color.connected_network_primary_color));
     }
 
+    @MainThread
+    private void updateWifiScanNotify(boolean isWifiEnabled, boolean isWifiScanEnabled,
+            boolean isDeviceLocked) {
+        if (isWifiEnabled || !isWifiScanEnabled || isDeviceLocked) {
+            mWifiScanNotifyLayout.setVisibility(View.GONE);
+            return;
+        }
+        if (TextUtils.isEmpty(mWifiScanNotifyText.getText())) {
+            final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo(
+                    AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
+                    v -> mInternetDialogController.launchWifiScanningSetting());
+            mWifiScanNotifyText.setText(AnnotationLinkSpan.linkify(
+                    getContext().getText(R.string.wifi_scan_notify_message), linkInfo));
+            mWifiScanNotifyText.setMovementMethod(LinkMovementMethod.getInstance());
+        }
+        mWifiScanNotifyLayout.setVisibility(View.VISIBLE);
+    }
+
     void onClickConnectedWifi() {
         if (mConnectedWifiEntry == null) {
             return;
@@ -508,52 +552,57 @@
 
     @Override
     public void onRefreshCarrierInfo() {
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
     public void onSimStateChanged() {
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
     @WorkerThread
     public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
     @WorkerThread
     public void onLost(Network network) {
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
     public void onSubscriptionsChanged(int defaultDataSubId) {
         mDefaultDataSubId = defaultDataSubId;
         mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+    }
+
+    @Override
+    public void onUserMobileDataStateChanged(boolean enabled) {
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
     public void onServiceStateChanged(ServiceState serviceState) {
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
     @WorkerThread
     public void onDataConnectionStateChanged(int state, int networkType) {
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
     public void onSignalStrengthsChanged(SignalStrength signalStrength) {
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
     public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
-        mHandler.post(() -> updateDialog());
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
     }
 
     @Override
@@ -565,7 +614,7 @@
         mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
         mHandler.post(() -> {
             mAdapter.notifyDataSetChanged();
-            updateDialog();
+            updateDialog(false /* shouldUpdateMobileNetwork */);
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index b9cd08e..1ade5ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -103,6 +103,8 @@
     private static final String TAG = "InternetDialogController";
     private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
             "android.settings.NETWORK_PROVIDER_SETTINGS";
+    private static final String ACTION_WIFI_SCANNING_SETTINGS =
+            "android.settings.WIFI_SCANNING_SETTINGS";
     private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
     public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
     public static final int NO_CELL_DATA_TYPE_ICON = 0;
@@ -147,6 +149,7 @@
     private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback;
     private WindowManager mWindowManager;
     private ToastFactory mToastFactory;
+    private SignalDrawable mSignalDrawable;
 
     @VisibleForTesting
     static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@@ -223,6 +226,7 @@
         mConnectivityManagerNetworkCallback = new DataConnectivityListener();
         mWindowManager = windowManager;
         mToastFactory = toastFactory;
+        mSignalDrawable = new SignalDrawable(mContext);
     }
 
     void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -429,10 +433,7 @@
 
     Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
             int iconType, boolean cutOut) {
-        Log.d(TAG, "getSignalStrengthIcon");
-        final SignalDrawable signalDrawable = new SignalDrawable(context);
-        signalDrawable.setLevel(
-                SignalDrawable.getState(level, numLevels, cutOut));
+        mSignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
 
         // Make the network type drawable
         final Drawable networkDrawable =
@@ -441,7 +442,7 @@
                         : context.getResources().getDrawable(iconType, context.getTheme());
 
         // Overlay the two drawables
-        final Drawable[] layers = {networkDrawable, signalDrawable};
+        final Drawable[] layers = {networkDrawable, mSignalDrawable};
         final int iconSize =
                 context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
 
@@ -603,6 +604,13 @@
         }
     }
 
+    void launchWifiScanningSetting() {
+        mCallback.dismissDialog();
+        final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+    }
+
     void connectCarrierNetwork() {
         final MergedCarrierEntry mergedCarrierEntry =
                 mAccessPointController.getMergedCarrierEntry();
@@ -883,7 +891,8 @@
             TelephonyCallback.DataConnectionStateListener,
             TelephonyCallback.DisplayInfoListener,
             TelephonyCallback.ServiceStateListener,
-            TelephonyCallback.SignalStrengthsListener {
+            TelephonyCallback.SignalStrengthsListener,
+            TelephonyCallback.UserMobileDataStateListener {
 
         @Override
         public void onServiceStateChanged(@NonNull ServiceState serviceState) {
@@ -905,6 +914,11 @@
             mTelephonyDisplayInfo = telephonyDisplayInfo;
             mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
         }
+
+        @Override
+        public void onUserMobileDataStateChanged(boolean enabled) {
+            mCallback.onUserMobileDataStateChanged(enabled);
+        }
     }
 
     private class InternetOnSubscriptionChangedListener
@@ -1009,6 +1023,8 @@
 
         void onSignalStrengthsChanged(SignalStrength signalStrength);
 
+        void onUserMobileDataStateChanged(boolean enabled);
+
         void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
 
         void dismissDialog();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
index 11c6980..ea5df17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -20,7 +20,9 @@
 import android.util.Log
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
 import javax.inject.Inject
 
 private const val TAG = "InternetDialogFactory"
@@ -32,6 +34,7 @@
 @SysUISingleton
 class InternetDialogFactory @Inject constructor(
     @Main private val handler: Handler,
+    @Background private val executor: Executor,
     private val internetDialogController: InternetDialogController,
     private val context: Context,
     private val uiEventLogger: UiEventLogger
@@ -49,7 +52,8 @@
             return
         } else {
             internetDialog = InternetDialog(context, this, internetDialogController,
-                    canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler)
+                    canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler,
+                    executor)
             internetDialog?.show()
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index a42b34c..ba6e98e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -78,6 +78,7 @@
     private ImageView mTransitionView;
     private ImageView mEnterTransitionView;
     private View mSave;
+    private View mCancel;
     private View mEdit;
     private View mShare;
     private CropView mCropView;
@@ -119,15 +120,15 @@
         mSave = requireViewById(R.id.save);
         mEdit = requireViewById(R.id.edit);
         mShare = requireViewById(R.id.share);
+        mCancel = requireViewById(R.id.cancel);
         mCropView = requireViewById(R.id.crop_view);
         mMagnifierView = requireViewById(R.id.magnifier);
         mCropView.setCropInteractionListener(mMagnifierView);
         mTransitionView = requireViewById(R.id.transition);
         mEnterTransitionView = requireViewById(R.id.enter_transition);
 
-        requireViewById(R.id.cancel).setOnClickListener(v -> finishAndRemoveTask());
-
         mSave.setOnClickListener(this::onClicked);
+        mCancel.setOnClickListener(this::onClicked);
         mEdit.setOnClickListener(this::onClicked);
         mShare.setOnClickListener(this::onClicked);
 
@@ -353,6 +354,7 @@
         v.setPressed(true);
         setButtonsEnabled(false);
         if (id == R.id.save) {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SAVED);
             startExport(PendingAction.SAVE);
         } else if (id == R.id.edit) {
             mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT);
@@ -360,6 +362,9 @@
         } else if (id == R.id.share) {
             mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
             startExport(PendingAction.SHARE);
+        } else if (id == R.id.cancel) {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EXIT);
+            finishAndRemoveTask();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 169b28c..d49ff93 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -83,7 +83,11 @@
     @UiEvent(doc = "Long screenshot editor activity loaded a previously saved screenshot")
     SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_CACHED_IMAGE_LOADED(890),
     @UiEvent(doc = "Long screenshot editor activity finished")
-    SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_FINISHED(891);
+    SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_FINISHED(891),
+    @UiEvent(doc = "User has saved a long screenshot to a file")
+    SCREENSHOT_LONG_SCREENSHOT_SAVED(910),
+    @UiEvent(doc = "User has discarded the result of a long screenshot")
+    SCREENSHOT_LONG_SCREENSHOT_EXIT(911);
 
     private final int mId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index d4f54e1..5648741e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -32,6 +32,7 @@
 import androidx.dynamicanimation.animation.SpringForce
 import com.android.systemui.Dumpable
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -184,12 +185,12 @@
         val animationRadius = MathUtils.constrain(shadeAnimation.radius,
                 blurUtils.minBlurRadius.toFloat(), blurUtils.maxBlurRadius.toFloat())
         val expansionRadius = blurUtils.blurRadiusOfRatio(
-                Interpolators.getNotificationScrimAlpha(
-                        if (shouldApplyShadeBlur()) shadeExpansion else 0f, false))
+                ShadeInterpolation.getNotificationScrimAlpha(
+                        if (shouldApplyShadeBlur()) shadeExpansion else 0f))
         var combinedBlur = (expansionRadius * INTERACTION_BLUR_FRACTION +
                 animationRadius * ANIMATION_BLUR_FRACTION)
-        val qsExpandedRatio = Interpolators.getNotificationScrimAlpha(qsPanelExpansion,
-                false /* notification */) * shadeExpansion
+        val qsExpandedRatio = ShadeInterpolation.getNotificationScrimAlpha(qsPanelExpansion) *
+                shadeExpansion
         combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio))
         combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
         var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 3bd7dd3..51a66aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -30,8 +30,9 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -114,7 +115,7 @@
 
     private void initDimens() {
         Resources res = getResources();
-        mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
+        mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
 
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
@@ -168,8 +169,8 @@
             viewState.clipTopAmount = 0;
 
             if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) {
-                viewState.alpha = Interpolators.getNotificationScrimAlpha(
-                        ambientState.getExpansionFraction(), true /* notification */);
+                float expansion = ambientState.getExpansionFraction();
+                viewState.alpha = ShadeInterpolation.getContentAlpha(expansion);
             } else {
                 viewState.alpha = 1f - ambientState.getHideAmount();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 0bd841f..23ad4ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -332,8 +332,7 @@
         deviceProvisionedController.addCallback(new DeviceProvisionedListener() {
             @Override
             public void onUserSetupChanged() {
-                setUserSetupComplete(deviceProvisionedController.isUserSetup(
-                        deviceProvisionedController.getCurrentUser()));
+                setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup());
             }
         });
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 1000788..6f8a5a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -24,7 +24,6 @@
 import android.view.Gravity
 import android.view.View
 import android.widget.FrameLayout
-
 import com.android.internal.annotations.GuardedBy
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.R
@@ -44,7 +43,6 @@
 import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
 import com.android.systemui.util.leak.RotationUtils.Rotation
 
-import java.lang.IllegalStateException
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -71,9 +69,6 @@
     private val contentInsetsProvider: StatusBarContentInsetsProvider,
     private val animationScheduler: SystemStatusAnimationScheduler
 ) {
-    private var sbHeightPortrait = 0
-    private var sbHeightLandscape = 0
-
     private lateinit var tl: View
     private lateinit var tr: View
     private lateinit var bl: View
@@ -157,16 +152,12 @@
 
         val newCorner = selectDesignatedCorner(rot, isRtl)
         val index = newCorner.cornerIndex()
+        val paddingTop = contentInsetsProvider.getStatusBarPaddingTop(rot)
 
-        val h = when (rot) {
-            0, 2 -> sbHeightPortrait
-            1, 3 -> sbHeightLandscape
-            else -> 0
-        }
         synchronized(lock) {
             nextViewState = nextViewState.copy(
                     rotation = rot,
-                    height = h,
+                    paddingTop = paddingTop,
                     designatedCorner = newCorner,
                     cornerIndex = index)
         }
@@ -204,26 +195,17 @@
         }
     }
 
-    @UiThread
-    private fun updateHeights(rot: Int) {
-        val height = when (rot) {
-            0, 2 -> sbHeightPortrait
-            1, 3 -> sbHeightLandscape
-            else -> 0
-        }
-
-        views.forEach { it.layoutParams.height = height }
-    }
-
     // Update the gravity and margins of the privacy views
     @UiThread
-    private fun updateRotations(rotation: Int) {
+    private fun updateRotations(rotation: Int, paddingTop: Int) {
         // To keep a view in the corner, its gravity is always the description of its current corner
         // Therefore, just figure out which view is in which corner. This turns out to be something
         // like (myCorner - rot) mod 4, where topLeft = 0, topRight = 1, etc. and portrait = 0, and
         // rotating the device counter-clockwise increments rotation by 1
 
         views.forEach { corner ->
+            corner.setPadding(0, paddingTop, 0, 0)
+
             val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
             (corner.layoutParams as FrameLayout.LayoutParams).apply {
                 gravity = rotatedCorner.toGravity()
@@ -266,6 +248,7 @@
 
         var rot = activeRotationForCorner(tl, rtl)
         var contentInsets = state.contentRectForRotation(rot)
+        tl.setPadding(0, state.paddingTop, 0, 0)
         (tl.layoutParams as FrameLayout.LayoutParams).apply {
             height = contentInsets.height()
             if (rtl) {
@@ -277,6 +260,7 @@
 
         rot = activeRotationForCorner(tr, rtl)
         contentInsets = state.contentRectForRotation(rot)
+        tr.setPadding(0, state.paddingTop, 0, 0)
         (tr.layoutParams as FrameLayout.LayoutParams).apply {
             height = contentInsets.height()
             if (rtl) {
@@ -288,6 +272,7 @@
 
         rot = activeRotationForCorner(br, rtl)
         contentInsets = state.contentRectForRotation(rot)
+        br.setPadding(0, state.paddingTop, 0, 0)
         (br.layoutParams as FrameLayout.LayoutParams).apply {
             height = contentInsets.height()
             if (rtl) {
@@ -299,6 +284,7 @@
 
         rot = activeRotationForCorner(bl, rtl)
         contentInsets = state.contentRectForRotation(rot)
+        bl.setPadding(0, state.paddingTop, 0, 0)
         (bl.layoutParams as FrameLayout.LayoutParams).apply {
             height = contentInsets.height()
             if (rtl) {
@@ -413,6 +399,7 @@
         val right = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_LANDSCAPE)
         val bottom = contentInsetsProvider
                 .getStatusBarContentInsetsForRotation(ROTATION_UPSIDE_DOWN)
+        val paddingTop = contentInsetsProvider.getStatusBarPaddingTop()
 
         synchronized(lock) {
             nextViewState = nextViewState.copy(
@@ -423,20 +410,12 @@
                     portraitRect = top,
                     landscapeRect = right,
                     upsideDownRect = bottom,
+                    paddingTop = paddingTop,
                     layoutRtl = rtl
             )
         }
     }
 
-    /**
-     * Set the status bar height in portrait and landscape, in pixels. If they are the same you can
-     * pass the same value twice
-     */
-    fun setStatusBarHeights(portrait: Int, landscape: Int) {
-        sbHeightPortrait = portrait
-        sbHeightLandscape = landscape
-    }
-
     private fun updateStatusBarState() {
         synchronized(lock) {
             nextViewState = nextViewState.copy(shadeExpanded = isShadeInQs())
@@ -489,7 +468,7 @@
 
         if (state.rotation != currentViewState.rotation) {
             // A rotation has started, hide the views to avoid flicker
-            updateRotations(state.rotation)
+            updateRotations(state.rotation, state.paddingTop)
         }
 
         if (state.needsLayout(currentViewState)) {
@@ -628,7 +607,7 @@
     val layoutRtl: Boolean = false,
 
     val rotation: Int = 0,
-    val height: Int = 0,
+    val paddingTop: Int = 0,
     val cornerIndex: Int = -1,
     val designatedCorner: View? = null,
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 26f6460..bacb85a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -24,7 +24,6 @@
 import android.content.ContentResolver
 import android.content.Context
 import android.content.Intent
-import android.content.pm.UserInfo
 import android.database.ContentObserver
 import android.net.Uri
 import android.os.Handler
@@ -203,7 +202,7 @@
     }
 
     private fun connectSession() {
-        if (plugin == null || session != null) {
+        if (plugin == null || session != null || smartspaceViews.isEmpty()) {
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 277b4ac..dfdc548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -523,7 +523,9 @@
         }
     }
 
-    private void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry) {
+    private void onEndLifetimeExtension(
+            @NonNull NotifLifetimeExtender extender,
+            @NonNull NotificationEntry entry) {
         Assert.isMainThread();
         if (!mAttached) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
new file mode 100644
index 0000000..8948969
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.systemui.statusbar.notification.collection.coordinator
+
+import android.util.ArraySet
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
+import com.android.systemui.statusbar.notification.row.NotificationGuts
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager
+import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener
+import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+private const val TAG = "GutsCoordinator"
+
+/**
+ * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline.
+ * Specifically, this just adds the lifetime extension necessary to keep guts from disappearing.
+ */
+@SysUISingleton
+class GutsCoordinator @Inject constructor(
+    private val notifGutsViewManager: NotifGutsViewManager,
+    private val logger: GutsCoordinatorLogger,
+    dumpManager: DumpManager
+) : Coordinator, Dumpable {
+
+    /** Keys of any Notifications for which we've been told the guts are open  */
+    private val notifsWithOpenGuts = ArraySet<String>()
+
+    /** Keys of any Notifications we've extended the lifetime for, based on guts  */
+    private val notifsExtendingLifetime = ArraySet<String>()
+
+    /** Callback for ending lifetime extension  */
+    private var onEndLifetimeExtensionCallback: OnEndLifetimeExtensionCallback? = null
+
+    init {
+        dumpManager.registerDumpable(TAG, this)
+    }
+
+    override fun attach(pipeline: NotifPipeline) {
+        notifGutsViewManager.setGutsListener(mGutsListener)
+        pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+        pw.println("  notifsWithOpenGuts: ${notifsWithOpenGuts.size}")
+        for (key in notifsWithOpenGuts) {
+            pw.println("   * $key")
+        }
+        pw.println("  notifsExtendingLifetime: ${notifsExtendingLifetime.size}")
+        for (key in notifsExtendingLifetime) {
+            pw.println("   * $key")
+        }
+        pw.println("  onEndLifetimeExtensionCallback: $onEndLifetimeExtensionCallback")
+    }
+
+    private val mLifetimeExtender: NotifLifetimeExtender = object : NotifLifetimeExtender {
+        override fun getName(): String {
+            return TAG
+        }
+
+        override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
+            onEndLifetimeExtensionCallback = callback
+        }
+
+        override fun shouldExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
+            val isShowingGuts = isCurrentlyShowingGuts(entry)
+            if (isShowingGuts) {
+                notifsExtendingLifetime.add(entry.key)
+            }
+            return isShowingGuts
+        }
+
+        override fun cancelLifetimeExtension(entry: NotificationEntry) {
+            notifsExtendingLifetime.remove(entry.key)
+        }
+    }
+
+    private val mGutsListener: NotifGutsViewListener = object : NotifGutsViewListener {
+        override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) {
+            logger.logGutsOpened(entry.key, guts)
+            if (guts.isLeavebehind) {
+                // leave-behind guts should not extend the lifetime of the notification
+                closeGutsAndEndLifetimeExtension(entry)
+            } else {
+                notifsWithOpenGuts.add(entry.key)
+            }
+        }
+
+        override fun onGutsClose(entry: NotificationEntry) {
+            logger.logGutsClosed(entry.key)
+            closeGutsAndEndLifetimeExtension(entry)
+        }
+    }
+
+    private fun isCurrentlyShowingGuts(entry: ListEntry) =
+            notifsWithOpenGuts.contains(entry.key)
+
+    private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) {
+        notifsWithOpenGuts.remove(entry.key)
+        if (notifsExtendingLifetime.remove(entry.key)) {
+            onEndLifetimeExtensionCallback?.onEndLifetimeExtension(mLifetimeExtender, entry)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
new file mode 100644
index 0000000..bac5223
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
@@ -0,0 +1,32 @@
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.row.NotificationGuts
+import javax.inject.Inject
+
+private const val TAG = "GutsCoordinator"
+
+class GutsCoordinatorLogger @Inject constructor(
+    @NotificationLog private val buffer: LogBuffer
+) {
+
+    fun logGutsOpened(key: String, guts: NotificationGuts) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = key
+            str2 = guts::class.simpleName
+            bool1 = guts.isLeavebehind
+        }, {
+            "Guts of type $str2 (leave behind: $bool1) opened for class $str1"
+        })
+    }
+
+    fun logGutsClosed(key: String) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = key
+        }, {
+            "Guts closed for class $str1"
+        })
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index 4d36251..8c8a8a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -19,7 +19,8 @@
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
 import static com.android.systemui.statusbar.notification.interruption.HeadsUpController.alertAgain;
 
-import android.annotation.Nullable;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -164,17 +165,17 @@
 
     private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
         @Override
-        public String getName() {
+        public @NonNull String getName() {
             return TAG;
         }
 
         @Override
-        public void setCallback(OnEndLifetimeExtensionCallback callback) {
+        public void setCallback(@NonNull OnEndLifetimeExtensionCallback callback) {
             mEndLifetimeExtension = callback;
         }
 
         @Override
-        public boolean shouldExtendLifetime(NotificationEntry entry, int reason) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry, int reason) {
             boolean isShowingHun = isCurrentlyShowingHun(entry);
             if (isShowingHun) {
                 mNotifExtendingLifetime = entry;
@@ -183,7 +184,7 @@
         }
 
         @Override
-        public void cancelLifetimeExtension(NotificationEntry entry) {
+        public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
             if (Objects.equals(mNotifExtendingLifetime, entry)) {
                 mNotifExtendingLifetime = null;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 47bc444..18f3b45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -57,6 +57,7 @@
             DeviceProvisionedCoordinator deviceProvisionedCoordinator,
             BubbleCoordinator bubbleCoordinator,
             HeadsUpCoordinator headsUpCoordinator,
+            GutsCoordinator gutsCoordinator,
             ConversationCoordinator conversationCoordinator,
             PreparationCoordinator preparationCoordinator,
             MediaCoordinator mediaCoordinator,
@@ -83,6 +84,7 @@
 
         if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
             mCoordinators.add(headsUpCoordinator);
+            mCoordinators.add(gutsCoordinator);
             mCoordinators.add(preparationCoordinator);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
index f8fe067..2fe3bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.notifcollection;
 
+import androidx.annotation.NonNull;
+
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -26,14 +28,14 @@
  */
 public interface NotifLifetimeExtender {
     /** Name to associate with this extender (for the purposes of debugging) */
-    String getName();
+    @NonNull String getName();
 
     /**
      * Called on the extender immediately after it has been registered. The extender should hang on
      * to this callback and execute it whenever it no longer needs to extend the lifetime of a
      * notification.
      */
-    void setCallback(OnEndLifetimeExtensionCallback callback);
+    void setCallback(@NonNull OnEndLifetimeExtensionCallback callback);
 
     /**
      * Called by the NotifCollection whenever a notification has been retracted (by the app) or
@@ -43,7 +45,7 @@
      * called on all lifetime extenders even if earlier ones return true (in other words, multiple
      * lifetime extenders can be extending a notification at the same time).
      */
-    boolean shouldExtendLifetime(NotificationEntry entry, @CancellationReason int reason);
+    boolean shouldExtendLifetime(@NonNull NotificationEntry entry, @CancellationReason int reason);
 
     /**
      * Called by the NotifCollection to inform a lifetime extender that its extension of a notif
@@ -51,7 +53,7 @@
      * lifetime extension). The extender should clean up any references it has to the notif in
      * question.
      */
-    void cancelLifetimeExtension(NotificationEntry entry);
+    void cancelLifetimeExtension(@NonNull NotificationEntry entry);
 
     /** Callback for notifying the NotifCollection that a lifetime extension has expired.*/
     interface OnEndLifetimeExtensionCallback {
@@ -59,6 +61,8 @@
          * Stop extending the lifetime of `entry` with `extender` and then immediately re-evaluates
          * whether to continue lifetime extending this notification or to remove it.
          */
-        void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry);
+        void onEndLifetimeExtension(
+                @NonNull NotifLifetimeExtender extender,
+                @NonNull NotificationEntry entry);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt
new file mode 100644
index 0000000..129f6b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.systemui.statusbar.notification.collection.render
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.NotificationGuts
+
+/**
+ * Interface for listening to guts open and close events.
+ */
+interface NotifGutsViewListener {
+    /** A notification's guts are being opened */
+    fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts)
+
+    /** A notification's guts are being closed */
+    fun onGutsClose(entry: NotificationEntry)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt
new file mode 100644
index 0000000..0260f89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.systemui.statusbar.notification.collection.render
+
+/** A type which provides open and close guts events to a single listener */
+interface NotifGutsViewManager {
+    /**
+     * @param listener the object that will listen to open and close guts events
+     */
+    fun setGutsListener(listener: NotifGutsViewListener?)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 94f5c44..dfa1f5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -58,6 +58,7 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
+import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
 import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -169,6 +170,14 @@
                 dumpManager);
     }
 
+    /** Provides an instance of {@link NotifGutsViewManager} */
+    @SysUISingleton
+    @Provides
+    static NotifGutsViewManager provideNotifGutsViewManager(
+            NotificationGutsManager notificationGutsManager) {
+        return notificationGutsManager;
+    }
+
     /** Provides an instance of {@link VisualStabilityManager} */
     @SysUISingleton
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 7eec95a..8e02d9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -65,6 +65,8 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
+import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -83,7 +85,8 @@
  * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
  * closing guts, and keeping track of the currently exposed notification guts.
  */
-public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
+public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender,
+        NotifGutsViewManager {
     private static final String TAG = "NotificationGutsManager";
 
     // Must match constant in Settings. Used to highlight preferences when linking to Settings.
@@ -123,7 +126,6 @@
     private final Optional<BubblesManager> mBubblesManagerOptional;
     private Runnable mOpenRunnable;
     private final INotificationManager mNotificationManager;
-    private final NotificationEntryManager mNotificationEntryManager;
     private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
     private final LauncherApps mLauncherApps;
     private final ShortcutManager mShortcutManager;
@@ -131,6 +133,7 @@
     private final UiEventLogger mUiEventLogger;
     private final ShadeController mShadeController;
     private final AppWidgetManager mAppWidgetManager;
+    private NotifGutsViewListener mGutsListener;
 
     /**
      * Injected constructor. See {@link NotificationsModule}.
@@ -161,7 +164,6 @@
         mAccessibilityManager = accessibilityManager;
         mHighPriorityProvider = highPriorityProvider;
         mNotificationManager = notificationManager;
-        mNotificationEntryManager = notificationEntryManager;
         mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
         mLauncherApps = launcherApps;
         mShortcutManager = shortcutManager;
@@ -258,10 +260,10 @@
     @VisibleForTesting
     protected boolean bindGuts(final ExpandableNotificationRow row,
             NotificationMenuRowPlugin.MenuItem item) {
-        StatusBarNotification sbn = row.getEntry().getSbn();
+        NotificationEntry entry = row.getEntry();
 
         row.setGutsView(item);
-        row.setTag(sbn.getPackageName());
+        row.setTag(entry.getSbn().getPackageName());
         row.getGuts().setClosedListener((NotificationGuts g) -> {
             row.onGutsClosed();
             if (!g.willBeRemoved() && !row.isRemoved()) {
@@ -272,7 +274,10 @@
                 mNotificationGutsExposed = null;
                 mGutsMenuItem = null;
             }
-            String key = sbn.getKey();
+            if (mGutsListener != null) {
+                mGutsListener.onGutsClose(entry);
+            }
+            String key = entry.getKey();
             if (key.equals(mKeyToRemoveOnGutsClosed)) {
                 mKeyToRemoveOnGutsClosed = null;
                 if (mNotificationLifetimeFinishedCallback != null) {
@@ -650,6 +655,10 @@
                         needsFalsingProtection,
                         row::onGutsOpened);
 
+                if (mGutsListener != null) {
+                    mGutsListener.onGutsOpen(row.getEntry(), guts);
+                }
+
                 row.closeRemoteInput();
                 mListContainer.onHeightChanged(row, true /* needsAnimation */);
                 mGutsMenuItem = menuItem;
@@ -695,10 +704,17 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NotificationGutsManager state:");
-        pw.print("  mKeyToRemoveOnGutsClosed: ");
+        pw.print("  mKeyToRemoveOnGutsClosed (legacy): ");
         pw.println(mKeyToRemoveOnGutsClosed);
     }
 
+    /**
+     * @param gutsListener the listener for open and close guts events
+     */
+    public void setGutsListener(NotifGutsViewListener gutsListener) {
+        mGutsListener = gutsListener;
+    }
+
     public interface OnSettingsClickListener {
         public void onSettingsClick(String key);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c7f7b66..c5a1f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -19,7 +19,6 @@
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 import static com.android.systemui.util.Utils.shouldUseSplitNotificationShade;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -72,8 +71,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.policy.SystemBarUtils;
 import com.android.keyguard.KeyguardSliceView;
 import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
@@ -120,9 +121,6 @@
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
  */
@@ -565,24 +563,17 @@
     @Nullable
     private OnClickListener mManageButtonClickListener;
 
-    @Inject
-    public NotificationStackScrollLayout(
-            @Named(VIEW_CONTEXT) Context context,
-            AttributeSet attrs,
-            NotificationSectionsManager notificationSectionsManager,
-            GroupMembershipManager groupMembershipManager,
-            GroupExpansionManager groupExpansionManager,
-            AmbientState ambientState,
-            UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+    public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
         super(context, attrs, 0, 0);
         Resources res = getResources();
-        mSectionsManager = notificationSectionsManager;
-        mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+        mSectionsManager = Dependency.get(NotificationSectionsManager.class);
+        mUnlockedScreenOffAnimationController =
+                Dependency.get(UnlockedScreenOffAnimationController.class);
         updateSplitNotificationShade();
         mSectionsManager.initialize(this, LayoutInflater.from(context));
         mSections = mSectionsManager.createSectionsForBuckets();
 
-        mAmbientState = ambientState;
+        mAmbientState = Dependency.get(AmbientState.class);
         mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
                 .getDefaultColor();
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -608,8 +599,8 @@
             mDebugPaint.setTextSize(25f);
         }
         mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
-        mGroupMembershipManager = groupMembershipManager;
-        mGroupExpansionManager = groupExpansionManager;
+        mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
+        mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
@@ -936,7 +927,7 @@
                 res.getDimensionPixelSize(R.dimen.notification_divider_height));
         mMinTopOverScrollToEscape = res.getDimensionPixelSize(
                 R.dimen.min_top_overscroll_to_qs);
-        mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
+        mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
         mMinimumPaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
         mQsTilePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal);
@@ -947,8 +938,7 @@
         mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius);
         mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
                 R.dimen.heads_up_status_bar_padding);
-        mQsScrollBoundaryPosition = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.quick_qs_offset_height);
+        mQsScrollBoundaryPosition = SystemBarUtils.getQuickQsOffsetHeight(mContext);
     }
 
     void updateSidePadding(int viewWidth) {
@@ -1706,7 +1696,7 @@
         super.onConfigurationChanged(newConfig);
         Resources res = getResources();
         updateSplitNotificationShade();
-        mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
+        mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         float densityScale = res.getDisplayMetrics().density;
         mSwipeHelper.setDensityScale(densityScale);
         float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
@@ -5265,10 +5255,6 @@
         mController.getNoticationRoundessManager().setAnimatedChildren(mChildrenToAddAnimated);
     }
 
-    public NotificationStackScrollLayoutController getController() {
-        return mController;
-    }
-
     void addSwipedOutView(View v) {
         mSwipedOutViews.add(v);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 7cbe78f..afe0bba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -26,8 +26,9 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -77,7 +78,7 @@
                 R.dimen.notification_divider_height);
         mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
         mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop);
-        int statusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
+        int statusBarHeight = SystemBarUtils.getStatusBarHeight(context);
         mHeadsUpInset = statusBarHeight + res.getDimensionPixelSize(
                 R.dimen.heads_up_status_bar_padding);
         mPinnedZTranslationExtra = res.getDimensionPixelSize(
@@ -408,8 +409,8 @@
             viewState.alpha = 1f - ambientState.getHideAmount();
         } else if (ambientState.isExpansionChanging()) {
             // Adjust alpha for shade open & close.
-            viewState.alpha = Interpolators.getNotificationScrimAlpha(
-                    ambientState.getExpansionFraction(), true /* notification */);
+            float expansion = ambientState.getExpansionFraction();
+            viewState.alpha = ShadeInterpolation.getContentAlpha(expansion);
         }
 
         if (ambientState.isShadeExpanded() && view.mustStayOnScreen()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 5f402d0..c81196d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -26,6 +26,7 @@
 import androidx.collection.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -137,9 +138,8 @@
 
     private void updateResources() {
         Resources resources = mContext.getResources();
-        mHeadsUpInset =
-                resources.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height)
-                        + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding);
+        mHeadsUpInset = SystemBarUtils.getStatusBarHeight(mContext)
+                + resources.getDimensionPixelSize(R.dimen.heads_up_status_bar_padding);
     }
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 7abe9bf..353868b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -30,6 +30,7 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 
+import com.android.internal.policy.SystemBarUtils;
 import com.android.keyguard.KeyguardHostViewController;
 import com.android.keyguard.KeyguardRootViewController;
 import com.android.keyguard.KeyguardSecurityModel;
@@ -468,8 +469,7 @@
         mKeyguardViewController.init();
 
         mContainer.addView(mRoot, mContainer.getChildCount());
-        mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset(
-                com.android.systemui.R.dimen.status_bar_height);
+        mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         setVisibility(View.INVISIBLE);
 
         final WindowInsets rootInsets = mRoot.getRootWindowInsets();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index fe154d2..03f3b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard;
 
 import android.annotation.ColorInt;
 import android.content.Context;
@@ -162,11 +163,8 @@
     }
 
     private void updateKeyguardStatusBarHeight() {
-        final int waterfallTop =
-                mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
         MarginLayoutParams lp =  (MarginLayoutParams) getLayoutParams();
-        lp.height =  getResources().getDimensionPixelSize(
-                R.dimen.status_bar_header_height_keyguard) + waterfallTop;
+        lp.height = getStatusBarHeaderHeightKeyguard(mContext);
         setLayoutParams(lp);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 5bb0e2e..917f132 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -95,6 +95,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.policy.SystemBarUtils;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardStatusViewController;
@@ -991,10 +992,8 @@
         super.loadDimens();
         mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get()
                 .setMaxLengthSeconds(0.4f).build();
-        mStatusBarMinHeight = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
-        mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize(
-                R.dimen.status_bar_header_height_keyguard);
+        mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
+        mStatusBarHeaderHeightKeyguard = Utils.getStatusBarHeaderHeightKeyguard(mView.getContext());
         mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
         mClockPositionAlgorithm.loadDimens(mResources);
         mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
@@ -1004,8 +1003,7 @@
                 R.dimen.keyguard_indication_bottom_padding);
         mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height);
         mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
-        int statusbarHeight = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
+        int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
         mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
                 R.dimen.heads_up_status_bar_padding);
         mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize(
@@ -1080,10 +1078,8 @@
     }
 
     public void updateResources() {
-        mQuickQsOffsetHeight = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.quick_qs_offset_height);
-        mSplitShadeStatusBarHeight =
-                mResources.getDimensionPixelSize(R.dimen.split_shade_header_height);
+        mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
+        mSplitShadeStatusBarHeight = Utils.getSplitShadeStatusBarHeight(mView.getContext());
         int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
         int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
         mShouldUseSplitNotificationShade =
@@ -1110,6 +1106,7 @@
             constraintSet.connect(
                     R.id.notification_stack_scroller, START,
                     R.id.qs_edge_guideline, START);
+            constraintSet.constrainHeight(R.id.split_shade_status_bar, mSplitShadeStatusBarHeight);
         } else {
             constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
             constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
@@ -1360,6 +1357,7 @@
             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
         }
 
+        mSplitShadeHeaderController.setShadeExpandedFraction(getExpandedFraction());
         mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
         mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
 
@@ -1667,6 +1665,24 @@
         mNotificationStackScrollLayoutController.resetScrollPosition();
     }
 
+    /** Collapses the panel. */
+    public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
+        boolean waiting = false;
+        if (animate && !isFullyCollapsed()) {
+            collapse(delayed, speedUpFactor);
+            waiting = true;
+        } else {
+            resetViews(false /* animate */);
+            setExpandedFraction(0); // just in case
+        }
+        if (!waiting) {
+            // it's possible that nothing animated, so we replicate the termination
+            // conditions of panelExpansionChanged here
+            // TODO(b/200063118): This can likely go away in a future refactor CL.
+            mBar.updateState(STATE_CLOSED);
+        }
+    }
+
     @Override
     public void collapse(boolean delayed, float speedUpFactor) {
         if (!canPanelBeCollapsed()) {
@@ -3117,7 +3133,7 @@
 
     @Override
     protected void onExpandingFinished() {
-        super.onExpandingFinished();
+        mScrimController.onExpandingFinished();
         mNotificationStackScrollLayoutController.onExpansionStopped();
         mHeadsUpManager.onExpandingFinished();
         mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
@@ -3192,6 +3208,7 @@
     protected void onTrackingStarted() {
         mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
         super.onTrackingStarted();
+        mScrimController.onTrackingStarted();
         if (mQsFullyExpanded) {
             mQsExpandImmediate = true;
             if (!mShouldUseSplitNotificationShade) {
@@ -3202,6 +3219,7 @@
             mAffordanceHelper.animateHideLeftRightIcon();
         }
         mNotificationStackScrollLayoutController.onPanelTrackingStarted();
+        cancelPendingPanelCollapse();
     }
 
     @Override
@@ -3391,7 +3409,7 @@
 
     @Override
     protected void onClosingFinished() {
-        super.onClosingFinished();
+        mStatusBar.onClosingFinished();
         setClosingWithAlphaFadeout(false);
         mMediaHierarchyManager.closeGuts();
     }
@@ -3822,13 +3840,27 @@
         mNotificationStackScrollLayoutController.setScrollingEnabled(b);
     }
 
+    private Runnable mHideExpandedRunnable;
+    private final Runnable mMaybeHideExpandedRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (getExpansionFraction() == 0.0f) {
+                mView.post(mHideExpandedRunnable);
+            }
+        }
+    };
+
     /**
      * Initialize objects instead of injecting to avoid circular dependencies.
+     *
+     * @param hideExpandedRunnable a runnable to run when we need to hide the expanded panel.
      */
     public void initDependencies(
             StatusBar statusBar,
+            Runnable hideExpandedRunnable,
             NotificationShelfController notificationShelfController) {
         setStatusBar(statusBar);
+        mHideExpandedRunnable = hideExpandedRunnable;
         mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
         mNotificationShelfController = notificationShelfController;
         mLockscreenShadeTransitionController.bindController(notificationShelfController);
@@ -3885,6 +3917,45 @@
             private long mLastTouchDownTime = -1L;
 
             @Override
+            public boolean onTouchForwardedFromStatusBar(MotionEvent event) {
+                // TODO(b/202981994): Move the touch debugging in this method to a central location.
+                //  (Right now, it's split between StatusBar and here.)
+
+                // If panels aren't enabled, ignore the gesture and don't pass it down to the
+                // panel view.
+                if (!mCommandQueue.panelsEnabled()) {
+                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
+                        Log.v(
+                                TAG,
+                                String.format(
+                                        "onTouchForwardedFromStatusBar: "
+                                                + "panel disabled, ignoring touch at (%d,%d)",
+                                        (int) event.getX(),
+                                        (int) event.getY()
+                                )
+                        );
+                    }
+                    return false;
+                }
+
+                // If the view that would receive the touch is disabled, just have status bar eat
+                // the gesture.
+                if (event.getAction() == MotionEvent.ACTION_DOWN && !mView.isEnabled()) {
+                    Log.v(TAG,
+                            String.format(
+                                    "onTouchForwardedFromStatusBar: "
+                                            + "panel view disabled, eating touch at (%d,%d)",
+                                    (int) event.getX(),
+                                    (int) event.getY()
+                            )
+                    );
+                    return true;
+                }
+
+                return mView.dispatchTouchEvent(event);
+            }
+
+            @Override
             public boolean onInterceptTouchEvent(MotionEvent event) {
                 if (mBlockTouches || mQs.disallowPanelTouches()) {
                     return false;
@@ -3895,7 +3966,7 @@
                 if (mStatusBar.isBouncerShowing()) {
                     return true;
                 }
-                if (mBar.panelEnabled()
+                if (mCommandQueue.panelsEnabled()
                         && !mNotificationStackScrollLayoutController.isLongPressInProgress()
                         && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
                     mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
@@ -4467,9 +4538,16 @@
                     }
                 }
             } else {
-                mKeyguardStatusBarViewController.updateViewState(
-                        /* alpha= */ 1f,
-                        keyguardShowing ? View.VISIBLE : View.INVISIBLE);
+                final boolean animatingUnlockedShadeToKeyguard = oldState == SHADE
+                        && statusBarState == KEYGUARD
+                        && mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying();
+                if (!animatingUnlockedShadeToKeyguard) {
+                    // Only make the status bar visible if we're not animating the screen off, since
+                    // we only want to be showing the clock/notifications during the animation.
+                    mKeyguardStatusBarViewController.updateViewState(
+                            /* alpha= */ 1f,
+                            keyguardShowing ? View.VISIBLE : View.INVISIBLE);
+                }
                 if (keyguardShowing && oldState != mBarState) {
                     if (mQs != null) {
                         mQs.hideImmediately();
@@ -4753,6 +4831,11 @@
         }
     }
 
+    /** Removes any pending runnables that would collapse the panel. */
+    public void cancelPendingPanelCollapse() {
+        mView.removeCallbacks(mMaybeHideExpandedRunnable);
+    }
+
     private final PanelBar.PanelStateChangeListener mPanelStateChangeListener =
             new PanelBar.PanelStateChangeListener() {
 
@@ -4767,6 +4850,14 @@
                     if (state == STATE_OPEN && mCurrentState != state) {
                         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
                     }
+                    if (state == STATE_OPENING) {
+                        mStatusBar.makeExpandedVisible(false);
+                    }
+                    if (state == STATE_CLOSED) {
+                        // Close the status bar in the next frame so we can show the end of the
+                        // animation.
+                        mView.post(mMaybeHideExpandedRunnable);
+                    }
                     mCurrentState = state;
                 }
             };
@@ -4774,4 +4865,10 @@
     public PanelBar.PanelStateChangeListener getPanelStateChangeListener() {
         return mPanelStateChangeListener;
     }
+
+
+    /** Returns the handler that the status bar should forward touches to. */
+    public PhoneStatusBarView.TouchEventHandler getStatusBarTouchEventHandler() {
+        return getTouchHandler()::onTouchForwardedFromStatusBar;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 9d06d2b..36bd31b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -56,7 +56,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.InjectionInflationController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -68,7 +67,6 @@
  */
 public class NotificationShadeWindowViewController {
     private static final String TAG = "NotifShadeWindowVC";
-    private final InjectionInflationController mInjectionInflationController;
     private final NotificationWakeUpCoordinator mCoordinator;
     private final PulseExpansionHandler mPulseExpansionHandler;
     private final DynamicPrivacyController mDynamicPrivacyController;
@@ -116,7 +114,6 @@
 
     @Inject
     public NotificationShadeWindowViewController(
-            InjectionInflationController injectionInflationController,
             NotificationWakeUpCoordinator coordinator,
             PulseExpansionHandler pulseExpansionHandler,
             DynamicPrivacyController dynamicPrivacyController,
@@ -141,7 +138,6 @@
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             LockIconViewController lockIconViewController) {
-        mInjectionInflationController = injectionInflationController;
         mCoordinator = coordinator;
         mPulseExpansionHandler = pulseExpansionHandler;
         mDynamicPrivacyController = dynamicPrivacyController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 310fe73..e90258d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -25,7 +25,6 @@
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.MotionEvent;
 import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
@@ -53,11 +52,18 @@
     public static final int STATE_OPENING = 1;
     public static final int STATE_OPEN = 2;
 
-    private PanelViewController mPanel;
     @Nullable private PanelStateChangeListener mPanelStateChangeListener;
     private int mState = STATE_CLOSED;
     private boolean mTracking;
 
+    /** Updates the panel state if necessary. */
+    public void updateState(@PanelState int state) {
+        if (DEBUG) LOG("update state: %d -> %d", mState, state);
+        if (mState != state) {
+            go(state);
+        }
+    }
+
     private void go(@PanelState int state) {
         if (DEBUG) LOG("go state: %d -> %d", mState, state);
         mState = state;
@@ -97,54 +103,11 @@
         super.onFinishInflate();
     }
 
-    /** Set the PanelViewController */
-    public void setPanel(PanelViewController pv) {
-        mPanel = pv;
-        pv.setBar(this);
-    }
-
     /** Sets the listener that will be notified of panel state changes. */
     public void setPanelStateChangeListener(PanelStateChangeListener listener) {
         mPanelStateChangeListener = listener;
     }
 
-    public boolean panelEnabled() {
-        return true;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        // Allow subclasses to implement enable/disable semantics
-        if (!panelEnabled()) {
-            if (event.getAction() == MotionEvent.ACTION_DOWN) {
-                Log.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",
-                        (int) event.getX(), (int) event.getY()));
-            }
-            return false;
-        }
-
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            final PanelViewController panel = mPanel;
-            if (panel == null) {
-                // panel is not there, so we'll eat the gesture
-                Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
-                        (int) event.getX(), (int) event.getY()));
-                return true;
-            }
-            boolean enabled = panel.isEnabled();
-            if (DEBUG) LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s %s", mState, panel,
-                    (enabled ? "" : " (disabled)"));
-            if (!enabled) {
-                // panel is disabled, so we'll eat the gesture
-                Log.v(TAG, String.format(
-                        "onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",
-                        panel, (int) event.getX(), (int) event.getY()));
-                return true;
-            }
-        }
-        return mPanel == null || mPanel.getView().dispatchTouchEvent(event);
-    }
-
     /**
      * @param frac the fraction from the expansion in [0, 1]
      * @param expanded whether the panel is currently expanded; this is independent from the
@@ -162,7 +125,6 @@
         if (expanded) {
             if (mState == STATE_CLOSED) {
                 go(STATE_OPENING);
-                onPanelPeeked();
             }
             fullyClosed = false;
             fullyOpened = frac >= 1f;
@@ -171,44 +133,16 @@
             go(STATE_OPEN);
         } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
             go(STATE_CLOSED);
-            onPanelCollapsed();
         }
 
         if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
                 fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");
     }
 
-    public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
-        boolean waiting = false;
-        PanelViewController pv = mPanel;
-        if (animate && !pv.isFullyCollapsed()) {
-            pv.collapse(delayed, speedUpFactor);
-            waiting = true;
-        } else {
-            pv.resetViews(false /* animate */);
-            pv.setExpandedFraction(0); // just in case
-        }
-        if (DEBUG) LOG("collapsePanel: animate=%s waiting=%s", animate, waiting);
-        if (!waiting && mState != STATE_CLOSED) {
-            // it's possible that nothing animated, so we replicate the termination
-            // conditions of panelExpansionChanged here
-            go(STATE_CLOSED);
-            onPanelCollapsed();
-        }
-    }
-
-    public void onPanelPeeked() {
-        if (DEBUG) LOG("onPanelPeeked");
-    }
-
     public boolean isClosed() {
         return mState == STATE_CLOSED;
     }
 
-    public void onPanelCollapsed() {
-        if (DEBUG) LOG("onPanelCollapsed");
-    }
-
     public void onTrackingStarted() {
         mTracking = true;
     }
@@ -217,14 +151,6 @@
         mTracking = false;
     }
 
-    public void onExpandingFinished() {
-        if (DEBUG) LOG("onExpandingFinished");
-    }
-
-    public void onClosingFinished() {
-
-    }
-
     /** An interface that will be notified of panel state changes. */
     public interface PanelStateChangeListener {
         /** Called when the state changes. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index c23577c..e5296af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -185,10 +185,9 @@
     protected final SysuiStatusBarStateController mStatusBarStateController;
     protected final AmbientState mAmbientState;
     protected final LockscreenGestureLogger mLockscreenGestureLogger;
+    private final TouchHandler mTouchHandler;
 
-    protected void onExpandingFinished() {
-        mBar.onExpandingFinished();
-    }
+    protected abstract void onExpandingFinished();
 
     protected void onExpandingStarted() {
     }
@@ -226,6 +225,7 @@
         mView = view;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mLockscreenGestureLogger = lockscreenGestureLogger;
+        mTouchHandler = createTouchHandler();
         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
@@ -238,7 +238,7 @@
         });
 
         mView.addOnLayoutChangeListener(createLayoutChangeListener());
-        mView.setOnTouchListener(createTouchHandler());
+        mView.setOnTouchListener(mTouchHandler);
         mView.setOnConfigurationChangedListener(createOnConfigurationChangedListener());
 
         mResources = mView.getResources();
@@ -289,6 +289,10 @@
                 : mTouchSlop;
     }
 
+    protected TouchHandler getTouchHandler() {
+        return mTouchHandler;
+    }
+
     private void addMovement(MotionEvent event) {
         // Add movement to velocity tracker using raw screen X and Y coordinates instead
         // of window coordinates because the window frame may be moving at the same time.
@@ -392,6 +396,12 @@
                     expand = false;
                 } else if (onKeyguard) {
                     expand = true;
+                } else if (mKeyguardStateController.isKeyguardFadingAway()) {
+                    // If we're in the middle of dismissing the keyguard, don't expand due to the
+                    // cancelled gesture. Gesture cancellation during an unlock is expected in some
+                    // situations, such keeping your finger down while swiping to unlock to an app
+                    // that is locked in landscape (the rotation will cancel the touch event).
+                    expand = false;
                 } else {
                     // If we get a cancel, put the shade back to the state it was in when the
                     // gesture started
@@ -448,6 +458,7 @@
     protected void onTrackingStopped(boolean expand) {
         mTracking = false;
         mBar.onTrackingStopped(expand);
+        mStatusBar.onTrackingStopped(expand);
         updatePanelExpansionAndVisibility();
     }
 
@@ -455,6 +466,7 @@
         endClosing();
         mTracking = true;
         mBar.onTrackingStarted();
+        mStatusBar.onTrackingStarted();
         notifyExpandingStarted();
         updatePanelExpansionAndVisibility();
     }
@@ -927,10 +939,7 @@
         mView.removeCallbacks(mFlingCollapseRunnable);
     }
 
-    protected void onClosingFinished() {
-        mBar.onClosingFinished();
-    }
-
+    protected abstract void onClosingFinished();
 
     protected void startUnlockHintAnimation() {
 
@@ -1153,23 +1162,28 @@
         return mView;
     }
 
-    public boolean isEnabled() {
-        return mView.isEnabled();
-    }
-
     public OnLayoutChangeListener createLayoutChangeListener() {
         return new OnLayoutChangeListener();
     }
 
-    protected TouchHandler createTouchHandler() {
-        return new TouchHandler();
-    }
+    protected abstract TouchHandler createTouchHandler();
 
     protected OnConfigurationChangedListener createOnConfigurationChangedListener() {
         return new OnConfigurationChangedListener();
     }
 
-    public class TouchHandler implements View.OnTouchListener {
+    public abstract class TouchHandler implements View.OnTouchListener {
+        /**
+         * Method called when a touch has occurred on {@link PhoneStatusBarView}.
+         *
+         * Touches that occur on the status bar view may have ramifications for the notification
+         * panel (e.g. a touch that pulls down the shade could start on the status bar), so we need
+         * to notify the panel controller when these touches occur.
+         *
+         * Returns true if the event was handled and false otherwise.
+         */
+        public abstract boolean onTouchForwardedFromStatusBar(MotionEvent event);
+
         public boolean onInterceptTouchEvent(MotionEvent event) {
             if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
                     && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
@@ -1435,4 +1449,8 @@
     private void cancelJankMonitoring(int cuj) {
         InteractionJankMonitor.getInstance().cancel(cuj);
     }
+
+    protected float getExpansionFraction() {
+        return mExpandedFraction;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 7b110a0..d19ed28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -604,8 +604,7 @@
 
     @Override
     public void onUserSetupChanged() {
-        boolean userSetup = mProvisionedController.isUserSetup(
-                mProvisionedController.getCurrentUser());
+        boolean userSetup = mProvisionedController.isCurrentUserSetup();
         if (mCurrentUserSetup == userSetup) return;
         mCurrentUserSetup = userSetup;
         updateAlarm();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 150d9c8..883313b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -24,7 +24,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.Pair;
 import android.view.DisplayCutout;
@@ -36,8 +35,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.LinearLayout;
 
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dependency;
-import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
@@ -55,14 +54,6 @@
     StatusBar mBar;
 
     private ScrimController mScrimController;
-    private Runnable mHideExpandedRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (mPanelFraction == 0.0f) {
-                mBar.makeExpandedInvisible();
-            }
-        }
-    };
     private DarkReceiver mBattery;
     private DarkReceiver mClock;
     private int mRotationOrientation = -1;
@@ -76,15 +67,12 @@
     @Nullable
     private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
     @Nullable
-    private PanelExpansionStateChangedListener mPanelExpansionStateChangedListener;
-
-    private PanelEnabledProvider mPanelEnabledProvider;
+    private TouchEventHandler mTouchEventHandler;
 
     /**
      * Draw this many pixels into the left/right side of the cutout to optimally use the space
      */
     private int mCutoutSideNudge = 0;
-    private boolean mHeadsUpVisible;
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -100,8 +88,8 @@
         mExpansionChangedListeners = listeners;
     }
 
-    void setPanelExpansionStateChangedListener(PanelExpansionStateChangedListener listener) {
-        mPanelExpansionStateChangedListener = listener;
+    void setTouchEventHandler(TouchEventHandler handler) {
+        mTouchEventHandler = handler;
     }
 
     public void setScrimController(ScrimController scrimController) {
@@ -178,15 +166,6 @@
     }
 
     @Override
-    public boolean panelEnabled() {
-        if (mPanelEnabledProvider == null) {
-            Log.e(TAG, "panelEnabledProvider is null; defaulting to super class.");
-            return super.panelEnabled();
-        }
-        return mPanelEnabledProvider.panelEnabled();
-    }
-
-    @Override
     public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
         if (super.onRequestSendAccessibilityEventInternal(child, event)) {
             // The status bar is very small so augment the view that the user is touching
@@ -202,79 +181,31 @@
     }
 
     @Override
-    public void onPanelPeeked() {
-        super.onPanelPeeked();
-        mBar.makeExpandedVisible(false);
-    }
-
-    @Override
-    public void onPanelCollapsed() {
-        super.onPanelCollapsed();
-        // Close the status bar in the next frame so we can show the end of the animation.
-        post(mHideExpandedRunnable);
-    }
-
-    public void removePendingHideExpandedRunnables() {
-        removeCallbacks(mHideExpandedRunnable);
-    }
-
-    @Override
     public boolean onTouchEvent(MotionEvent event) {
-        boolean barConsumedEvent = mBar.interceptTouchEvent(event);
-
-        if (DEBUG_GESTURES) {
-            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
-                EventLog.writeEvent(EventLogTags.SYSUI_PANELBAR_TOUCH,
-                        event.getActionMasked(), (int) event.getX(), (int) event.getY(),
-                        barConsumedEvent ? 1 : 0);
-            }
+        mBar.onTouchEvent(event);
+        if (mTouchEventHandler == null) {
+            Log.w(
+                    TAG,
+                    String.format(
+                            "onTouch: No touch handler provided; eating gesture at (%d,%d)",
+                            (int) event.getX(),
+                            (int) event.getY()
+                    )
+            );
+            return true;
         }
-
-        return barConsumedEvent || super.onTouchEvent(event);
-    }
-
-    @Override
-    public void onTrackingStarted() {
-        super.onTrackingStarted();
-        mBar.onTrackingStarted();
-        mScrimController.onTrackingStarted();
-        removePendingHideExpandedRunnables();
-    }
-
-    @Override
-    public void onClosingFinished() {
-        super.onClosingFinished();
-        mBar.onClosingFinished();
-    }
-
-    @Override
-    public void onTrackingStopped(boolean expand) {
-        super.onTrackingStopped(expand);
-        mBar.onTrackingStopped(expand);
-    }
-
-    @Override
-    public void onExpandingFinished() {
-        super.onExpandingFinished();
-        mScrimController.onExpandingFinished();
+        return mTouchEventHandler.handleTouchEvent(event);
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        return mBar.interceptTouchEvent(event) || super.onInterceptTouchEvent(event);
+        mBar.onTouchEvent(event);
+        return super.onInterceptTouchEvent(event);
     }
 
     @Override
     public void panelExpansionChanged(float frac, boolean expanded) {
         super.panelExpansionChanged(frac, expanded);
-        if ((frac == 0 || frac == 1)) {
-            if (mPanelExpansionStateChangedListener != null) {
-                mPanelExpansionStateChangedListener.onPanelExpansionStateChanged();
-            } else {
-                Log.w(TAG, "No PanelExpansionStateChangedListener provided.");
-            }
-        }
-
         if (mExpansionChangedListeners != null) {
             for (StatusBar.ExpansionChangedListener listener : mExpansionChangedListeners) {
                 listener.onExpansionChanged(frac, expanded);
@@ -282,11 +213,6 @@
         }
     }
 
-    /** Set the {@link PanelEnabledProvider} to use. */
-    public void setPanelEnabledProvider(PanelEnabledProvider panelEnabledProvider) {
-        mPanelEnabledProvider = panelEnabledProvider;
-    }
-
     public void updateResources() {
         mCutoutSideNudge = getResources().getDimensionPixelSize(
                 R.dimen.display_cutout_margin_consumption);
@@ -298,7 +224,7 @@
         final int waterfallTopInset =
                 mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
-        mStatusBarHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_height);
+        mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         layoutParams.height = mStatusBarHeight - waterfallTopInset;
 
         int statusBarPaddingTop = getResources().getDimensionPixelSize(
@@ -366,15 +292,14 @@
                 getPaddingBottom());
     }
 
-    /** An interface that will provide whether panel is enabled. */
-    interface PanelEnabledProvider {
-        /** Returns true if the panel is enabled and false otherwise. */
-        boolean panelEnabled();
-    }
-
-    /** A listener that will be notified when a panel's expansion state may have changed. */
-    public interface PanelExpansionStateChangedListener {
-        /** Called when a panel's expansion state may have changed. */
-        void onPanelExpansionStateChanged();
+    /**
+     * A handler repsonsible for all touch event handling on the status bar.
+     *
+     * The handler will be notified each time {@link this#onTouchEvent} is called, and the return
+     * value from the handler will be returned from {@link this#onTouchEvent}.
+     **/
+    public interface TouchEventHandler {
+        /** Called each time {@link this#onTouchEvent} is called. */
+        boolean handleTouchEvent(MotionEvent event);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 4c0332a..de21e73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -18,29 +18,28 @@
 import android.graphics.Point
 import android.view.View
 import android.view.ViewGroup
+import android.view.ViewTreeObserver
 import com.android.systemui.R
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.unfold.UNFOLD_STATUS_BAR
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
 import com.android.systemui.util.ViewController
+import javax.inject.Inject
+import javax.inject.Named
+import dagger.Lazy
 
 /** Controller for [PhoneStatusBarView].  */
-class PhoneStatusBarViewController(
+class PhoneStatusBarViewController private constructor(
     view: PhoneStatusBarView,
-    commandQueue: CommandQueue,
-    statusBarMoveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
-    panelExpansionStateChangedListener: PhoneStatusBarView.PanelExpansionStateChangedListener,
+    @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
+    private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
+    touchEventHandler: PhoneStatusBarView.TouchEventHandler,
 ) : ViewController<PhoneStatusBarView>(view) {
 
-    override fun onViewAttached() {}
-    override fun onViewDetached() {}
-
-    init {
-        mView.setPanelEnabledProvider {
-            commandQueue.panelsEnabled()
-        }
-        mView.setPanelExpansionStateChangedListener(panelExpansionStateChangedListener)
-
-        statusBarMoveFromCenterAnimationController?.let { animationController ->
+    override fun onViewAttached() {
+        moveFromCenterAnimationController?.let { animationController ->
             val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
             val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
 
@@ -50,15 +49,33 @@
                 systemIconArea
             )
 
-            animationController.init(viewsToAnimate, viewCenterProvider)
+            mView.viewTreeObserver.addOnPreDrawListener(object :
+                ViewTreeObserver.OnPreDrawListener {
+                override fun onPreDraw(): Boolean {
+                    animationController.onViewsReady(viewsToAnimate, viewCenterProvider)
+                    mView.viewTreeObserver.removeOnPreDrawListener(this)
+                    return true
+                }
+            })
 
             mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
                 val widthChanged = right - left != oldRight - oldLeft
                 if (widthChanged) {
-                    statusBarMoveFromCenterAnimationController.onStatusBarWidthChanged()
+                    moveFromCenterAnimationController.onStatusBarWidthChanged()
                 }
             }
         }
+
+        progressProvider?.setReadyToHandleTransition(true)
+    }
+
+    override fun onViewDetached() {
+        progressProvider?.setReadyToHandleTransition(false)
+        moveFromCenterAnimationController?.onViewDetached()
+    }
+
+    init {
+        mView.setTouchEventHandler(touchEventHandler)
     }
 
     fun setImportantForAccessibility(mode: Int) {
@@ -96,4 +113,23 @@
             outPoint.y = viewY + view.height / 2
         }
     }
+
+    class Factory @Inject constructor(
+        @Named(UNFOLD_STATUS_BAR)
+        private val progressProvider: Lazy<ScopedUnfoldTransitionProgressProvider>,
+        private val moveFromCenterController: Lazy<StatusBarMoveFromCenterAnimationController>,
+        private val unfoldConfig: UnfoldTransitionConfig,
+    ) {
+        fun create(
+            view: PhoneStatusBarView,
+            touchEventHandler: PhoneStatusBarView.TouchEventHandler
+        ): PhoneStatusBarViewController {
+            return PhoneStatusBarViewController(
+                view,
+                if (unfoldConfig.isEnabled) progressProvider.get() else null,
+                if (unfoldConfig.isEnabled) moveFromCenterController.get() else null,
+                touchEventHandler
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index a5cea06..1921357 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -47,7 +47,7 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
@@ -579,8 +579,7 @@
         if (isNaN(expansionFraction)) {
             return;
         }
-        expansionFraction = Interpolators
-                .getNotificationScrimAlpha(expansionFraction, false /* notification */);
+        expansionFraction = ShadeInterpolation.getNotificationScrimAlpha(expansionFraction);
         boolean qsBottomVisible = qsPanelBottomY > 0;
         if (mQsExpansion != expansionFraction || mQsBottomVisible != qsBottomVisible) {
             mQsExpansion = expansionFraction;
@@ -675,6 +674,12 @@
                 }
                 mInFrontAlpha = 0;
             }
+        } else if (mState == ScrimState.AUTH_SCRIMMED_SHADE) {
+            float behindFraction = getInterpolatedFraction();
+            behindFraction = (float) Math.pow(behindFraction, 0.8f);
+
+            mBehindAlpha = behindFraction * mDefaultScrimAlpha;
+            mNotificationsAlpha = mBehindAlpha;
         } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED
                 || mState == ScrimState.PULSING) {
             Pair<Integer, Float> result = calculateBackStateForState(mState);
@@ -915,8 +920,7 @@
     }
 
     private float getInterpolatedFraction() {
-        return Interpolators.getNotificationScrimAlpha(
-                mPanelExpansionFraction, false /* notification */);
+        return ShadeInterpolation.getNotificationScrimAlpha(mPanelExpansionFraction);
     }
 
     private void setScrimAlpha(ScrimView scrim, float alpha) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 850b986..9246c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -87,6 +87,17 @@
         }
     },
 
+    AUTH_SCRIMMED_SHADE {
+        @Override
+        public void prepare(ScrimState previousState) {
+            // notif & behind scrim alpha values are determined by ScrimController#applyState
+            // based on the shade expansion
+
+            mFrontTint = Color.BLACK;
+            mFrontAlpha = .66f;
+        }
+    },
+
     AUTH_SCRIMMED {
         @Override
         public void prepare(ScrimState previousState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index 768222d..a54251a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -128,7 +128,8 @@
             mNotificationShadeWindowController.setNotificationShadeFocusable(false);
 
             getStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper();
-            getStatusBarView().collapsePanel(true /* animate */, delayed, speedUpFactor);
+            getNotificationPanelViewController()
+                    .collapsePanel(true /* animate */, delayed, speedUpFactor);
         } else if (mBubblesOptional.isPresent()) {
             mBubblesOptional.get().collapseStack();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index 4b7fe4e..a7ecd06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -18,6 +18,7 @@
 
 import android.view.View
 import com.android.systemui.R
+import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.battery.BatteryMeterView
 import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.flags.FeatureFlags
@@ -53,6 +54,14 @@
             updateVisibility()
         }
 
+    var shadeExpandedFraction = -1f
+        set(value) {
+            if (visible && field != value) {
+                statusBar.alpha = ShadeInterpolation.getContentAlpha(value)
+                field = value
+            }
+        }
+
     init {
         batteryMeterViewController.init()
         val batteryIcon: BatteryMeterView = statusBar.findViewById(R.id.batteryRemainingIcon)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 5d2e6f1..0e75a45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -233,6 +233,7 @@
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
 import com.android.systemui.util.WallpaperController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.MessageRouter;
@@ -525,6 +526,7 @@
     private QSPanelController mQSPanelController;
 
     private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
+    private final PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
     KeyguardIndicationController mKeyguardIndicationController;
 
     private View mReportRejectedTouch;
@@ -543,8 +545,8 @@
     private final FeatureFlags mFeatureFlags;
     private final UnfoldTransitionConfig mUnfoldTransitionConfig;
     private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
+    private final Lazy<NaturalRotationUnfoldProgressProvider> mNaturalUnfoldProgressProvider;
     private final Lazy<UnfoldTransitionWallpaperController> mUnfoldWallpaperController;
-    private final Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimation;
     private final WallpaperController mWallpaperController;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private final MessageRouter mMessageRouter;
@@ -772,6 +774,7 @@
             ExtensionController extensionController,
             UserInfoControllerImpl userInfoControllerImpl,
             OperatorNameViewController.Factory operatorNameViewControllerFactory,
+            PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
             PhoneStatusBarPolicy phoneStatusBarPolicy,
             KeyguardIndicationController keyguardIndicationController,
             DemoModeController demoModeController,
@@ -782,7 +785,7 @@
             UnfoldTransitionConfig unfoldTransitionConfig,
             Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
             Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController,
-            Lazy<StatusBarMoveFromCenterAnimationController> statusBarUnfoldAnimationController,
+            Lazy<NaturalRotationUnfoldProgressProvider> naturalRotationUnfoldProgressProvider,
             WallpaperController wallpaperController,
             OngoingCallController ongoingCallController,
             SystemStatusAnimationScheduler animationScheduler,
@@ -812,6 +815,7 @@
         mKeyguardStateController = keyguardStateController;
         mHeadsUpManager = headsUpManagerPhone;
         mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
+        mPhoneStatusBarViewControllerFactory = phoneStatusBarViewControllerFactory;
         mKeyguardIndicationController = keyguardIndicationController;
         mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
         mDynamicPrivacyController = dynamicPrivacyController;
@@ -879,9 +883,9 @@
         mBrightnessSliderFactory = brightnessSliderFactory;
         mUnfoldTransitionConfig = unfoldTransitionConfig;
         mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
+        mNaturalUnfoldProgressProvider = naturalRotationUnfoldProgressProvider;
         mUnfoldWallpaperController = unfoldTransitionWallpaperController;
         mWallpaperController = wallpaperController;
-        mMoveFromCenterAnimation = statusBarUnfoldAnimationController;
         mOngoingCallController = ongoingCallController;
         mAnimationScheduler = animationScheduler;
         mStatusBarLocationPublisher = locationPublisher;
@@ -902,6 +906,7 @@
         mExpansionChangedListeners = new ArrayList<>();
         addExpansionChangedListener(
                 (expansion, expanded) -> mScrimController.setRawPanelExpansionFraction(expansion));
+        addExpansionChangedListener(this::onPanelExpansionChanged);
 
         mBubbleExpandListener =
                 (isExpanding, key) -> mContext.getMainExecutor().execute(() -> {
@@ -1076,6 +1081,7 @@
         if (mUnfoldTransitionConfig.isEnabled()) {
             mUnfoldLightRevealOverlayAnimation.get().init();
             mUnfoldWallpaperController.get().init();
+            mNaturalUnfoldProgressProvider.get().init();
         }
 
         mPluginManager.addPluginListener(
@@ -1167,7 +1173,6 @@
                     PhoneStatusBarView oldStatusBarView = mStatusBarView;
                     mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
                     mStatusBarView.setBar(this);
-                    mStatusBarView.setPanel(mNotificationPanelViewController);
                     mStatusBarView.setPanelStateChangeListener(
                             mNotificationPanelViewController.getPanelStateChangeListener());
                     mStatusBarView.setScrimController(mScrimController);
@@ -1176,16 +1181,11 @@
                         sendInitialExpansionAmount(listener);
                     }
 
-                    StatusBarMoveFromCenterAnimationController moveFromCenterAnimation = null;
-                    if (mUnfoldTransitionConfig.isEnabled()) {
-                        moveFromCenterAnimation = mMoveFromCenterAnimation.get();
-                    }
-                    mPhoneStatusBarViewController =
-                            new PhoneStatusBarViewController(
-                                    mStatusBarView,
-                                    mCommandQueue,
-                                    moveFromCenterAnimation,
-                                    this::onPanelExpansionStateChanged);
+                    mNotificationPanelViewController.setBar(mStatusBarView);
+
+                    mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
+                            .create(mStatusBarView, mNotificationPanelViewController
+                                    .getStatusBarTouchEventHandler());
                     mPhoneStatusBarViewController.init();
 
                     mBatteryMeterViewController = new BatteryMeterViewController(
@@ -1216,7 +1216,7 @@
                     // TODO (b/136993073) Separate notification shade and status bar
                     mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                             mNotificationIconAreaController, mHeadsUpManager,
-                            mStackScroller.getController(),
+                            mStackScrollerController,
                             mStatusBarStateController, mKeyguardBypassController,
                             mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
                             mNotificationPanelViewController, mStatusBarView);
@@ -1316,6 +1316,7 @@
 
         mNotificationPanelViewController.initDependencies(
                 this,
+                this::makeExpandedInvisible,
                 mNotificationShelfController);
 
         BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
@@ -1453,12 +1454,14 @@
         }
     }
 
-    private void onPanelExpansionStateChanged() {
-        if (getNavigationBarView() != null) {
-            getNavigationBarView().onStatusBarPanelStateChanged();
-        }
-        if (getNotificationPanelViewController() != null) {
-            getNotificationPanelViewController().updateSystemUiStateFlags();
+    private void onPanelExpansionChanged(float frac, boolean expanded) {
+        if (frac == 0 || frac == 1) {
+            if (getNavigationBarView() != null) {
+                getNavigationBarView().onStatusBarPanelStateChanged();
+            }
+            if (getNotificationPanelViewController() != null) {
+                getNotificationPanelViewController().updateSystemUiStateFlags();
+            }
         }
     }
 
@@ -2157,7 +2160,8 @@
 
     public void animateCollapseQuickSettings() {
         if (mState == StatusBarState.SHADE) {
-            mStatusBarView.collapsePanel(true, false /* delayed */, 1.0f /* speedUpFactor */);
+            mNotificationPanelViewController.collapsePanel(
+                    true, false /* delayed */, 1.0f /* speedUpFactor */);
         }
     }
 
@@ -2170,7 +2174,7 @@
         }
 
         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
-        mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
+        mNotificationPanelViewController.collapsePanel(/*animate=*/ false, false /* delayed*/,
                 1.0f /* speedUpFactor */);
 
         mNotificationPanelViewController.closeQs();
@@ -2204,7 +2208,10 @@
         }
     }
 
-    public boolean interceptTouchEvent(MotionEvent event) {
+    /** Called when a touch event occurred on {@link PhoneStatusBarView}. */
+    public void onTouchEvent(MotionEvent event) {
+        // TODO(b/202981994): Move this touch debugging to a central location. (Right now, it's
+        //   split between NotificationPanelViewController and here.)
         if (DEBUG_GESTURES) {
             if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
                 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
@@ -2236,7 +2243,6 @@
                     event.getAction() == MotionEvent.ACTION_CANCEL;
             setInteracting(StatusBarManager.WINDOW_STATUS_BAR, !upOrCancel || mExpandedVisible);
         }
-        return false;
     }
 
     boolean isSameStatusBarState(int state) {
@@ -3837,7 +3843,11 @@
         mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
 
         if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
-            mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+            if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED) {
+                mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+            } else {
+                mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+            }
         } else if (mBouncerShowing) {
             // Bouncer needs the front scrim when it's on top of an activity,
             // tapping on a notification, editing QS or being dismissed by
@@ -4328,10 +4338,9 @@
     private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
         @Override
         public void onUserSetupChanged() {
-            final boolean userSetup = mDeviceProvisionedController.isUserSetup(
-                    mDeviceProvisionedController.getCurrentUser());
-            Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user "
-                    + mDeviceProvisionedController.getCurrentUser());
+            final boolean userSetup = mDeviceProvisionedController.isCurrentUserSetup();
+            Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for "
+                    + "current user");
             if (MULTIUSER_DEBUG) {
                 Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
                         userSetup, mUserSetup));
@@ -4460,7 +4469,7 @@
                     mNavigationBarController.touchAutoDim(mDisplayId);
                     Trace.beginSection("StatusBar#updateKeyguardState");
                     if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
-                        mStatusBarView.removePendingHideExpandedRunnables();
+                        mNotificationPanelViewController.cancelPendingPanelCollapse();
                     }
                     updateDozingState();
                     checkBarModes();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index 5301b25..bb1daa2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -536,7 +536,7 @@
             }
             if (mStatusBar.getStatusBarView() != null) {
                 if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) {
-                    mStatusBar.getStatusBarView().collapsePanel(
+                    mNotificationPanelViewController.collapsePanel(
                             false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */);
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 98be77d..16d7f98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -25,6 +25,7 @@
 import android.view.View.LAYOUT_DIRECTION_RTL
 import android.view.WindowMetrics
 import androidx.annotation.VisibleForTesting
+import com.android.internal.policy.SystemBarUtils
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
@@ -174,11 +175,16 @@
                 targetRotation,
                 dc,
                 context.resources.configuration.windowConfiguration.maxBounds,
-                rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
+                SystemBarUtils.getStatusBarHeight(context),
                 minLeft,
                 minRight)
     }
 
+    fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int {
+        val res = rotation?.let { it -> getResourcesForRotation(it, context) } ?: context.resources
+        return res.getDimensionPixelSize(R.dimen.status_bar_padding_top)
+    }
+
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         insetsCache.snapshot().forEach { (key, rect) ->
             pw.println("$key -> $rect")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
index 8af03aa..8ef186c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -20,43 +20,54 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.ViewCenterProvider
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UNFOLD_STATUS_BAR
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
 import javax.inject.Inject
+import javax.inject.Named
 
 @SysUISingleton
 class StatusBarMoveFromCenterAnimationController @Inject constructor(
-    private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
-    private val windowManager: WindowManager
+    @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider,
+    private val windowManager: WindowManager,
 ) {
 
-    private lateinit var moveFromCenterAnimator: UnfoldMoveFromCenterAnimator
+    private val transitionListener = TransitionListener()
+    private var moveFromCenterAnimator: UnfoldMoveFromCenterAnimator? = null
 
-    fun init(viewsToAnimate: Array<View>, viewCenterProvider: ViewCenterProvider) {
+    fun onViewsReady(viewsToAnimate: Array<View>, viewCenterProvider: ViewCenterProvider) {
         moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
             viewCenterProvider = viewCenterProvider)
 
-        unfoldTransitionProgressProvider.addCallback(object : TransitionProgressListener {
-            override fun onTransitionStarted() {
-                moveFromCenterAnimator.updateDisplayProperties()
+        moveFromCenterAnimator?.updateDisplayProperties()
 
-                viewsToAnimate.forEach {
-                    moveFromCenterAnimator.registerViewForAnimation(it)
-                }
-            }
+        viewsToAnimate.forEach {
+            moveFromCenterAnimator?.registerViewForAnimation(it)
+        }
 
-            override fun onTransitionFinished() {
-                moveFromCenterAnimator.onTransitionFinished()
-                moveFromCenterAnimator.clearRegisteredViews()
-            }
+        progressProvider.addCallback(transitionListener)
+    }
 
-            override fun onTransitionProgress(progress: Float) {
-                moveFromCenterAnimator.onTransitionProgress(progress)
-            }
-        })
+    fun onViewDetached() {
+        progressProvider.removeCallback(transitionListener)
+        moveFromCenterAnimator?.clearRegisteredViews()
+        moveFromCenterAnimator = null
     }
 
     fun onStatusBarWidthChanged() {
-        moveFromCenterAnimator.updateViewPositions()
+        moveFromCenterAnimator?.updateDisplayProperties()
+        moveFromCenterAnimator?.updateViewPositions()
+    }
+
+    private inner class TransitionListener : TransitionProgressListener {
+        override fun onTransitionProgress(progress: Float) {
+            moveFromCenterAnimator?.onTransitionProgress(progress)
+        }
+
+        override fun onTransitionFinished() {
+            // Reset translations when transition is stopped/cancelled
+            // (e.g. the transition could be cancelled mid-way when rotating the screen)
+            moveFromCenterAnimator?.onTransitionProgress(1f)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index eb405e9..b742394 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -29,6 +29,7 @@
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
 import android.view.WindowInsets;
 
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.ScreenDecorations;
@@ -172,8 +173,7 @@
         Resources resources = mContext.getResources();
         mDisplayCutoutTouchableRegionSize = resources.getDimensionPixelSize(
                 com.android.internal.R.dimen.display_cutout_touchable_region_size);
-        mStatusBarHeight =
-                resources.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+        mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 235a8e8..9d2dbc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -40,6 +40,7 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -86,8 +87,7 @@
         mResources = resources;
 
         if (mBarHeight < 0) {
-            mBarHeight = mResources.getDimensionPixelSize(
-                    com.android.internal.R.dimen.status_bar_height);
+            mBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         }
     }
 
@@ -96,12 +96,11 @@
     }
 
     /**
-     * Rereads the status_bar_height from configuration and reapplys the current state if the height
+     * Rereads the status bar height and reapplys the current state if the height
      * is different.
      */
     public void refreshStatusBarHeight() {
-        int heightFromConfig = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
+        int heightFromConfig = SystemBarUtils.getStatusBarHeight(mContext);
 
         if (mBarHeight != heightFromConfig) {
             mBarHeight = heightFromConfig;
@@ -139,29 +138,20 @@
     private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
         int height = mBarHeight;
         if (INSETS_LAYOUT_GENERALIZATION) {
-            Rect displayBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-            int defaultAndUpsideDownHeight;
-            int theOtherHeight;
-            if (displayBounds.width() > displayBounds.height()) {
-                defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height_landscape);
-                theOtherHeight = mContext.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height_portrait);
-            } else {
-                defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height_portrait);
-                theOtherHeight = mContext.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height_landscape);
-            }
             switch (rotation) {
                 case ROTATION_UNDEFINED:
                 case Surface.ROTATION_0:
                 case Surface.ROTATION_180:
-                    height = defaultAndUpsideDownHeight;
+                    height = SystemBarUtils.getStatusBarHeightForRotation(
+                            mContext, Surface.ROTATION_0);
                     break;
                 case Surface.ROTATION_90:
+                    height = SystemBarUtils.getStatusBarHeightForRotation(
+                            mContext, Surface.ROTATION_90);
+                    break;
                 case Surface.ROTATION_270:
-                    height = theOtherHeight;
+                    height = SystemBarUtils.getStatusBarHeightForRotation(
+                            mContext, Surface.ROTATION_270);
                     break;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index f3f3325..e2332e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -145,22 +145,24 @@
                 .setDuration(duration.toLong())
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(1f)
-                .withEndAction {
-                    aodUiAnimationPlaying = false
+                .setListener(object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator?) {
+                        aodUiAnimationPlaying = false
 
-                    // Lock the keyguard if it was waiting for the screen off animation to end.
-                    keyguardViewMediatorLazy.get().maybeHandlePendingLock()
+                        // Lock the keyguard if it was waiting for the screen off animation to end.
+                        keyguardViewMediatorLazy.get().maybeHandlePendingLock()
 
-                    // Tell the StatusBar to become keyguard for real - we waited on that since it
-                    // is slow and would have caused the animation to jank.
-                    statusBar.updateIsKeyguard()
+                        // Tell the StatusBar to become keyguard for real - we waited on that since
+                        // it is slow and would have caused the animation to jank.
+                        statusBar.updateIsKeyguard()
 
-                    // Run the callback given to us by the KeyguardVisibilityHelper.
-                    after.run()
+                        // Run the callback given to us by the KeyguardVisibilityHelper.
+                        after.run()
 
-                    // Done going to sleep, reset this flag.
-                    decidedToAnimateGoingToSleep = null
-                }
+                        // Done going to sleep, reset this flag.
+                        decidedToAnimateGoingToSleep = null
+                    }
+                })
                 .start()
     }
 
@@ -226,6 +228,12 @@
             return false
         }
 
+        // If animations are disabled system-wide, don't play this one either.
+        if (Settings.Global.getString(
+                context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE) == "0") {
+            return false
+        }
+
         // We only play the unlocked screen off animation if we are... unlocked.
         if (statusBarStateControllerImpl.state != StatusBarState.SHADE) {
             return false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 5689707..c452a48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -88,6 +88,7 @@
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -111,6 +112,7 @@
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
 import com.android.systemui.util.WallpaperController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.MessageRouter;
@@ -211,6 +213,7 @@
             ExtensionController extensionController,
             UserInfoControllerImpl userInfoControllerImpl,
             OperatorNameViewController.Factory operatorNameViewControllerFactory,
+            PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
             PhoneStatusBarPolicy phoneStatusBarPolicy,
             KeyguardIndicationController keyguardIndicationController,
             DemoModeController demoModeController,
@@ -220,6 +223,7 @@
             BrightnessSlider.Factory brightnessSliderFactory,
             UnfoldTransitionConfig unfoldTransitionConfig,
             Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+            Lazy<NaturalRotationUnfoldProgressProvider> naturalRotationUnfoldProgressProvider,
             Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController,
             Lazy<StatusBarMoveFromCenterAnimationController> statusBarMoveFromCenterAnimation,
             WallpaperController wallpaperController,
@@ -311,6 +315,7 @@
                 extensionController,
                 userInfoControllerImpl,
                 operatorNameViewControllerFactory,
+                phoneStatusBarViewControllerFactory,
                 phoneStatusBarPolicy,
                 keyguardIndicationController,
                 demoModeController,
@@ -321,7 +326,7 @@
                 unfoldTransitionConfig,
                 unfoldLightRevealOverlayAnimation,
                 unfoldTransitionWallpaperController,
-                statusBarMoveFromCenterAnimation,
+                naturalRotationUnfoldProgressProvider,
                 wallpaperController,
                 ongoingCallController,
                 animationScheduler,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 2791678..9de0c46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.phone.dagger;
 
 import android.annotation.Nullable;
-import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -33,7 +32,6 @@
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 import com.android.systemui.statusbar.phone.TapAgainView;
-import com.android.systemui.util.InjectionInflationController;
 
 import javax.inject.Named;
 
@@ -49,12 +47,9 @@
     @Provides
     @StatusBarComponent.StatusBarScope
     public static NotificationShadeWindowView providesNotificationShadeWindowView(
-            InjectionInflationController injectionInflationController,
-            Context context) {
+            LayoutInflater layoutInflater) {
         NotificationShadeWindowView notificationShadeWindowView = (NotificationShadeWindowView)
-                injectionInflationController.injectable(
-                        LayoutInflater.from(context)).inflate(R.layout.super_notification_shade,
-                        /* root= */ null);
+                layoutInflater.inflate(R.layout.super_notification_shade, /* root= */ null);
         if (notificationShadeWindowView == null) {
             throw new IllegalStateException(
                     "R.layout.super_notification_shade could not be properly inflated");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
index 7b4c35a..3944c8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
@@ -14,23 +14,60 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.provider.Settings;
+
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 
+/**
+ * Controller to cache in process the state of the device provisioning.
+ * <p>
+ * This controller keeps track of the values of device provisioning and user setup complete
+ */
 public interface DeviceProvisionedController extends CallbackController<DeviceProvisionedListener> {
 
+    /**
+     * @return whether the device is provisioned
+     * @see Settings.Global#DEVICE_PROVISIONED
+     */
     boolean isDeviceProvisioned();
-    boolean isUserSetup(int currentUser);
+
+    /**
+     * @deprecated use {@link com.android.systemui.settings.UserTracker}
+     */
+    @Deprecated
     int getCurrentUser();
 
-    default boolean isCurrentUserSetup() {
-        return isUserSetup(getCurrentUser());
-    }
+    /**
+     * @param user the user to query
+     * @return whether that user has completed the user setup
+     * @see Settings.Secure#USER_SETUP_COMPLETE
+     */
+    boolean isUserSetup(int user);
 
+    /**
+     * @see DeviceProvisionedController#isUserSetup
+     */
+    boolean isCurrentUserSetup();
+
+    /**
+     * Interface to provide calls when the values tracked change
+     */
     interface DeviceProvisionedListener {
+        /**
+         * Call when the device changes from not provisioned to provisioned
+         */
         default void onDeviceProvisionedChanged() { }
+
+        /**
+         * Call on user switched
+         */
         default void onUserSwitched() {
             onUserSetupChanged();
         }
+
+        /**
+         * Call when some user changes from not provisioned to provisioned
+         */
         default void onUserSetupChanged() { }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
deleted file mode 100644
index 485b1b1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.statusbar.policy;
-
-import android.app.ActivityManager;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.Settings.Global;
-import android.provider.Settings.Secure;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.ArrayList;
-
-import javax.inject.Inject;
-
-/**
- */
-@SysUISingleton
-public class DeviceProvisionedControllerImpl extends CurrentUserTracker implements
-        DeviceProvisionedController {
-
-    protected static final String TAG = DeviceProvisionedControllerImpl.class.getSimpleName();
-    protected final ArrayList<DeviceProvisionedListener> mListeners = new ArrayList<>();
-    private final GlobalSettings mGlobalSettings;
-    private final SecureSettings mSecureSettings;
-    private final Uri mDeviceProvisionedUri;
-    private final Uri mUserSetupUri;
-    protected final ContentObserver mSettingsObserver;
-
-    /**
-     */
-    @Inject
-    public DeviceProvisionedControllerImpl(@Main Handler mainHandler,
-            BroadcastDispatcher broadcastDispatcher, GlobalSettings globalSettings,
-            SecureSettings secureSettings) {
-        super(broadcastDispatcher);
-        mGlobalSettings = globalSettings;
-        mSecureSettings = secureSettings;
-        mDeviceProvisionedUri = mGlobalSettings.getUriFor(Global.DEVICE_PROVISIONED);
-        mUserSetupUri = mSecureSettings.getUriFor(Secure.USER_SETUP_COMPLETE);
-        mSettingsObserver = new ContentObserver(mainHandler) {
-            @Override
-            public void onChange(boolean selfChange, Uri uri, int flags) {
-                Log.d(TAG, "Setting change: " + uri);
-                if (mUserSetupUri.equals(uri)) {
-                    notifySetupChanged();
-                } else {
-                    notifyProvisionedChanged();
-                }
-            }
-        };
-    }
-
-    @Override
-    public boolean isDeviceProvisioned() {
-        return mGlobalSettings.getInt(Global.DEVICE_PROVISIONED, 0) != 0;
-    }
-
-    @Override
-    public boolean isUserSetup(int currentUser) {
-        return mSecureSettings.getIntForUser(Secure.USER_SETUP_COMPLETE, 0, currentUser) != 0;
-    }
-
-    @Override
-    public int getCurrentUser() {
-        return ActivityManager.getCurrentUser();
-    }
-
-    @Override
-    public void addCallback(@NonNull DeviceProvisionedListener listener) {
-        mListeners.add(listener);
-        if (mListeners.size() == 1) {
-            startListening(getCurrentUser());
-        }
-        listener.onUserSetupChanged();
-        listener.onDeviceProvisionedChanged();
-    }
-
-    @Override
-    public void removeCallback(@NonNull DeviceProvisionedListener listener) {
-        mListeners.remove(listener);
-        if (mListeners.size() == 0) {
-            stopListening();
-        }
-    }
-
-    protected void startListening(int user) {
-        mGlobalSettings.registerContentObserverForUser(mDeviceProvisionedUri, true,
-                mSettingsObserver, 0);
-        mSecureSettings.registerContentObserverForUser(mUserSetupUri, true,
-                mSettingsObserver, user);
-        startTracking();
-    }
-
-    protected void stopListening() {
-        stopTracking();
-        mGlobalSettings.unregisterContentObserver(mSettingsObserver);
-    }
-
-    @Override
-    public void onUserSwitched(int newUserId) {
-        mGlobalSettings.unregisterContentObserver(mSettingsObserver);
-        mGlobalSettings.registerContentObserverForUser(mDeviceProvisionedUri, true,
-                mSettingsObserver, 0);
-        mSecureSettings.registerContentObserverForUser(mUserSetupUri, true,
-                mSettingsObserver, newUserId);
-        notifyUserChanged();
-    }
-
-    private void notifyUserChanged() {
-        for (int i = mListeners.size() - 1; i >= 0; --i) {
-            mListeners.get(i).onUserSwitched();
-        }
-    }
-
-    private void notifySetupChanged() {
-        for (int i = mListeners.size() - 1; i >= 0; --i) {
-            mListeners.get(i).onUserSetupChanged();
-        }
-    }
-
-    private void notifyProvisionedChanged() {
-        for (int i = mListeners.size() - 1; i >= 0; --i) {
-            mListeners.get(i).onDeviceProvisionedChanged();
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
new file mode 100644
index 0000000..acc1214
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -0,0 +1,230 @@
+/*
+ * 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.systemui.statusbar.policy
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.ArraySet
+import android.util.SparseBooleanArray
+import androidx.annotation.GuardedBy
+import androidx.annotation.WorkerThread
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SecureSettings
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicBoolean
+import javax.inject.Inject
+
+@SysUISingleton
+open class DeviceProvisionedControllerImpl @Inject constructor(
+    private val secureSettings: SecureSettings,
+    private val globalSettings: GlobalSettings,
+    private val userTracker: UserTracker,
+    private val dumpManager: DumpManager,
+    @Background private val backgroundHandler: Handler,
+    @Main private val mainExecutor: Executor
+) : DeviceProvisionedController,
+    DeviceProvisionedController.DeviceProvisionedListener,
+    Dumpable {
+
+    companion object {
+        private const val ALL_USERS = -1
+        private const val NO_USERS = -2
+        protected const val TAG = "DeviceProvisionedControllerImpl"
+    }
+
+    private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED)
+    private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
+
+    private val deviceProvisioned = AtomicBoolean(false)
+    @GuardedBy("lock")
+    private val userSetupComplete = SparseBooleanArray()
+    @GuardedBy("lock")
+    private val listeners = ArraySet<DeviceProvisionedController.DeviceProvisionedListener>()
+
+    private val lock = Any()
+
+    private val backgroundExecutor = HandlerExecutor(backgroundHandler)
+
+    private val initted = AtomicBoolean(false)
+
+    private val _currentUser: Int
+        get() = userTracker.userId
+
+    override fun getCurrentUser(): Int {
+        return _currentUser
+    }
+
+    private val observer = object : ContentObserver(backgroundHandler) {
+        override fun onChange(
+            selfChange: Boolean,
+            uris: MutableCollection<Uri>,
+            flags: Int,
+            userId: Int
+        ) {
+            val updateDeviceProvisioned = deviceProvisionedUri in uris
+            val updateUser = if (userSetupUri in uris) userId else NO_USERS
+            updateValues(updateDeviceProvisioned, updateUser)
+            if (updateDeviceProvisioned) {
+                onDeviceProvisionedChanged()
+            }
+            if (updateUser != NO_USERS) {
+                onUserSetupChanged()
+            }
+        }
+    }
+
+    private val userChangedCallback = object : UserTracker.Callback {
+        @WorkerThread
+        override fun onUserChanged(newUser: Int, userContext: Context) {
+            updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+            onUserSwitched()
+        }
+
+        override fun onProfilesChanged(profiles: List<UserInfo>) {}
+    }
+
+    init {
+        userSetupComplete.put(currentUser, false)
+    }
+
+    /**
+     * Call to initialize values and register observers
+     */
+    open fun init() {
+        if (!initted.compareAndSet(false, true)) {
+            return
+        }
+        dumpManager.registerDumpable(this)
+        updateValues()
+        userTracker.addCallback(userChangedCallback, backgroundExecutor)
+        globalSettings.registerContentObserver(deviceProvisionedUri, observer)
+        secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
+    }
+
+    @WorkerThread
+    private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
+        if (updateDeviceProvisioned) {
+            deviceProvisioned
+                    .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
+        }
+        synchronized(lock) {
+            if (updateUser == ALL_USERS) {
+                val N = userSetupComplete.size()
+                for (i in 0 until N) {
+                    val user = userSetupComplete.keyAt(i)
+                    val value = secureSettings
+                            .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+                    userSetupComplete.put(user, value)
+                }
+            } else if (updateUser != NO_USERS) {
+                val value = secureSettings
+                        .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, updateUser) != 0
+                userSetupComplete.put(updateUser, value)
+            }
+        }
+    }
+
+    /**
+     * Adds a listener.
+     *
+     * The listener will not be called when this happens.
+     */
+    override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
+        synchronized(lock) {
+            listeners.add(listener)
+        }
+    }
+
+    override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
+        synchronized(lock) {
+            listeners.remove(listener)
+        }
+    }
+
+    override fun isDeviceProvisioned(): Boolean {
+        return deviceProvisioned.get()
+    }
+
+    override fun isUserSetup(user: Int): Boolean {
+        val index = synchronized(lock) {
+            userSetupComplete.indexOfKey(user)
+        }
+        return if (index < 0) {
+            val value = secureSettings
+                    .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+            synchronized(lock) {
+                userSetupComplete.put(user, value)
+            }
+            value
+        } else {
+            synchronized(lock) {
+                userSetupComplete.get(user, false)
+            }
+        }
+    }
+
+    override fun isCurrentUserSetup(): Boolean {
+        return isUserSetup(currentUser)
+    }
+
+    override fun onDeviceProvisionedChanged() {
+        dispatchChange(
+                DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+        )
+    }
+
+    override fun onUserSetupChanged() {
+        dispatchChange(DeviceProvisionedController.DeviceProvisionedListener::onUserSetupChanged)
+    }
+
+    override fun onUserSwitched() {
+        dispatchChange(DeviceProvisionedController.DeviceProvisionedListener::onUserSwitched)
+    }
+
+    protected fun dispatchChange(
+        callback: DeviceProvisionedController.DeviceProvisionedListener.() -> Unit
+    ) {
+        val listenersCopy = synchronized(lock) {
+            ArrayList(listeners)
+        }
+        mainExecutor.execute {
+            listenersCopy.forEach(callback)
+        }
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.println("Device provisioned: ${deviceProvisioned.get()}")
+        synchronized(lock) {
+            pw.println("User setup complete: $userSetupComplete")
+            pw.println("Listeners: $listeners")
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 8912448..923aff1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -180,9 +180,13 @@
         return new Recents(context, recentsImplementation, commandQueue);
     }
 
-    @Binds
-    abstract DeviceProvisionedController bindDeviceProvisionedController(
-            DeviceProvisionedControllerImpl deviceProvisionedController);
+    @SysUISingleton
+    @Provides
+    static DeviceProvisionedController providesDeviceProvisionedController(
+            DeviceProvisionedControllerImpl deviceProvisionedController) {
+        deviceProvisionedController.init();
+        return deviceProvisionedController;
+    }
 
     @Binds
     abstract KeyguardViewController bindKeyguardViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index b23aa20..7e4ec67 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -20,15 +20,19 @@
 import android.hardware.SensorManager
 import android.hardware.devicestate.DeviceStateManager
 import android.os.Handler
+import android.view.IWindowManager
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.LifecycleScreenStatusProvider
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
 import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
 import dagger.Lazy
 import dagger.Module
 import dagger.Provides
 import java.util.Optional
 import java.util.concurrent.Executor
+import javax.inject.Named
 import javax.inject.Singleton
 
 @Module
@@ -62,6 +66,27 @@
 
     @Provides
     @Singleton
+    fun provideNaturalRotationProgressProvider(
+        context: Context,
+        windowManager: IWindowManager,
+        unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider
+    ): NaturalRotationUnfoldProgressProvider =
+        NaturalRotationUnfoldProgressProvider(
+            context,
+            windowManager,
+            unfoldTransitionProgressProvider
+        )
+
+    @Provides
+    @Named(UNFOLD_STATUS_BAR)
+    @Singleton
+    fun provideStatusBarScopedTransitionProvider(
+        source: NaturalRotationUnfoldProgressProvider
+    ): ScopedUnfoldTransitionProgressProvider =
+        ScopedUnfoldTransitionProgressProvider(source)
+
+    @Provides
+    @Singleton
     fun provideShellProgressProvider(
         config: UnfoldTransitionConfig,
         provider: Lazy<UnfoldTransitionProgressProvider>
@@ -72,3 +97,5 @@
             Optional.empty()
         }
 }
+
+const val UNFOLD_STATUS_BAR = "unfold_status_bar"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index da4ccd0..c420a6d 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -112,6 +112,7 @@
                 mOtherProfileIntent.putParcelableArrayListExtra(EXTRA_RESOLVE_INFOS,
                         rListOtherProfile);
             } else {
+                mOtherProfileIntent = new Intent();
                 mOtherProfileIntent.setComponent(ComponentName.unflattenFromString(
                         this.getResources().getString(
                                 com.android.internal.R.string.config_usbConfirmActivity)));
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
deleted file mode 100644
index cdc5d87..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2018 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.systemui.util;
-
-import android.content.Context;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.view.InflateException;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/**
- * Manages inflation that requires dagger injection.
- * See docs/dagger.md for details.
- */
-@SysUISingleton
-public class InjectionInflationController {
-
-    public static final String VIEW_CONTEXT = "view_context";
-    private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
-    private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
-    private final ViewInstanceCreator.Factory mViewInstanceCreatorFactory;
-
-    @Inject
-    public InjectionInflationController(ViewInstanceCreator.Factory viewInstanceCreatorFactory) {
-        mViewInstanceCreatorFactory = viewInstanceCreatorFactory;
-        initInjectionMap();
-    }
-
-    /**
-     * Wraps a {@link LayoutInflater} to support creating dagger injected views.
-     * See docs/dagger.md for details.
-     */
-    public LayoutInflater injectable(LayoutInflater inflater) {
-        LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
-        ret.setPrivateFactory(mFactory);
-        return ret;
-    }
-
-    private void initInjectionMap() {
-        for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
-            if (View.class.isAssignableFrom(method.getReturnType())
-                    && (method.getModifiers() & Modifier.PUBLIC) != 0) {
-                mInjectionMap.put(method.getReturnType().getName(), method);
-            }
-        }
-    }
-
-    /**
-     * Subcomponent that actually creates injected views.
-     */
-    @Subcomponent
-    public interface ViewInstanceCreator {
-
-        /** Factory for creating a ViewInstanceCreator. */
-        @Subcomponent.Factory
-        interface Factory {
-            ViewInstanceCreator build(
-                    @BindsInstance @Named(VIEW_CONTEXT) Context context,
-                    @BindsInstance AttributeSet attributeSet);
-        }
-
-        /**
-         * Creates the NotificationStackScrollLayout.
-         */
-        NotificationStackScrollLayout createNotificationStackScrollLayout();
-    }
-
-
-    private class InjectionFactory implements LayoutInflater.Factory2 {
-
-        @Override
-        public View onCreateView(String name, Context context, AttributeSet attrs) {
-            Method creationMethod = mInjectionMap.get(name);
-            if (creationMethod != null) {
-                try {
-                    return (View) creationMethod.invoke(
-                            mViewInstanceCreatorFactory.build(context, attrs));
-                } catch (IllegalAccessException e) {
-                    throw new InflateException("Could not inflate " + name, e);
-                } catch (InvocationTargetException e) {
-                    throw new InflateException("Could not inflate " + name, e);
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-            return onCreateView(name, context, attrs);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index a1cdfd8..407dc5e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -24,8 +24,10 @@
 import android.content.res.TypedArray;
 import android.provider.Settings;
 import android.view.ContextThemeWrapper;
+import android.view.DisplayCutout;
 import android.view.View;
 
+import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
@@ -186,4 +188,39 @@
         return color;
     }
 
+    /**
+     * Gets the {@link R.dimen#split_shade_header_height}.
+     *
+     * Currently, it's the same as {@link com.android.internal.R.dimen#quick_qs_offset_height}.
+     */
+    public static int getSplitShadeStatusBarHeight(Context context) {
+        return SystemBarUtils.getQuickQsOffsetHeight(context);
+    }
+
+    /**
+     * Gets the {@link R.dimen#qs_header_system_icons_area_height}.
+     *
+     * It's the same as {@link com.android.internal.R.dimen#quick_qs_offset_height} except for
+     * sw600dp-land.
+     */
+    public static int getQsHeaderSystemIconsAreaHeight(Context context) {
+        final Resources res = context.getResources();
+        if (Utils.shouldUseSplitNotificationShade(res)) {
+            return res.getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height);
+        } else {
+            return SystemBarUtils.getQuickQsOffsetHeight(context);
+        }
+    }
+
+    /**
+     * Gets the {@link R.dimen#status_bar_header_height_keyguard}.
+     */
+    public static int getStatusBarHeaderHeightKeyguard(Context context) {
+        final int statusBarHeight = SystemBarUtils.getStatusBarHeight(context);
+        final DisplayCutout cutout = context.getDisplay().getCutout();
+        final int waterfallInsetTop = cutout == null ? 0 : cutout.getWaterfallInsets().top;
+        final int statusBarHeaderHeightKeyguard = context.getResources()
+                .getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard);
+        return Math.max(statusBarHeight, statusBarHeaderHeightKeyguard + waterfallInsetTop);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index fe0a2a4..5e0f427 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -194,7 +194,7 @@
         verifyAttachment(times(1));
 
         listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
-
+        verify(mView).onViewDetached();
         verify(mColorExtractor).removeOnColorsChangedListener(
                 any(ColorExtractor.OnColorsChangedListener.class));
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 35fe1ba..ff4412e9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -17,7 +17,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -40,7 +39,6 @@
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.settings.CurrentUserObservable;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.util.InjectionInflationController;
 
 import org.junit.After;
 import org.junit.Before;
@@ -69,7 +67,6 @@
     private ContentObserver mContentObserver;
     private DockManagerFake mFakeDockManager;
     private MutableLiveData<Integer> mCurrentUser;
-    @Mock InjectionInflationController mMockInjectionInflationController;
     @Mock PluginManager mMockPluginManager;
     @Mock SysuiColorExtractor mMockColorExtractor;
     @Mock ContentResolver mMockContentResolver;
@@ -83,7 +80,6 @@
         MockitoAnnotations.initMocks(this);
 
         LayoutInflater inflater = LayoutInflater.from(getContext());
-        when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater);
 
         mFakeDockManager = new DockManagerFake();
 
@@ -91,7 +87,7 @@
         mCurrentUser.setValue(MAIN_USER_ID);
         when(mMockCurrentUserObserable.getCurrentUser()).thenReturn(mCurrentUser);
 
-        mClockManager = new ClockManager(getContext(), mMockInjectionInflationController,
+        mClockManager = new ClockManager(getContext(), inflater,
                 mMockPluginManager, mMockColorExtractor, mMockContentResolver,
                 mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager);
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt
index 456f32b..3a27e35 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt
@@ -43,7 +43,7 @@
     @Test
     fun loadResources() {
         // Cover constructor taking Resources object.
-        position = SmallClockPosition(context.resources)
+        position = SmallClockPosition(context)
         position.setDarkAmount(1f)
         assertThat(position.preferredY).isGreaterThan(0)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 4098677..14a60be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -278,6 +278,7 @@
         when(mContext.getDisplay()).thenReturn(display);
         final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
         final float center = Math.min(windowBounds.exactCenterX(), windowBounds.exactCenterY());
+        final float displayWidth = windowBounds.width();
         final PointF magnifiedCenter = new PointF(center, center + 5f);
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, magnifiedCenter.x,
@@ -295,7 +296,8 @@
                 ActivityInfo.CONFIG_ORIENTATION));
 
         assertEquals(newRotation, mWindowMagnificationController.mRotation);
-        final PointF expectedCenter = new PointF(magnifiedCenter.y, magnifiedCenter.x);
+        final PointF expectedCenter = new PointF(magnifiedCenter.y,
+                displayWidth - magnifiedCenter.x);
         final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
                 mWindowMagnificationController.getCenterY());
         assertEquals(expectedCenter, actualCenter);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index c4f480d..55ee433 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -91,7 +91,7 @@
     @Test
     public void testA11yDisablesGesture() {
         assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
-        when(mAccessibilityManager.isEnabled()).thenReturn(true);
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
         assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
     }
 
@@ -99,7 +99,7 @@
     @Test
     public void testA11yDisablesTap() {
         assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
-        when(mAccessibilityManager.isEnabled()).thenReturn(true);
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
         assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
     }
 
@@ -107,7 +107,7 @@
     @Test
     public void testA11yDisablesDoubleTap() {
         assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue();
-        when(mAccessibilityManager.isEnabled()).thenReturn(true);
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
         assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
index 172dcda..b3c098c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
@@ -18,23 +18,35 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
 @SmallTest
 public class FeatureFlagManagerTest extends SysuiTestCase {
     FeatureFlagManager mFeatureFlagManager;
 
+    @Mock private DumpManager mDumpManager;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-        mFeatureFlagManager = new FeatureFlagManager();
+        mFeatureFlagManager = new FeatureFlagManager(mDumpManager);
     }
 
     @Test
@@ -43,4 +55,31 @@
         // Again, nothing changes.
         assertThat(mFeatureFlagManager.isEnabled(1, false)).isFalse();
     }
+
+    @Test
+    public void testDump() {
+        // Even if a flag is set before
+        mFeatureFlagManager.setEnabled(1, true);
+
+        // WHEN the flags have been accessed
+        assertFalse(mFeatureFlagManager.isEnabled(1, false));
+        assertTrue(mFeatureFlagManager.isEnabled(2, true));
+
+        // Even if a flag is set after
+        mFeatureFlagManager.setEnabled(2, false);
+
+        // THEN the dump contains the flags and the default values
+        String dump = dumpToString();
+        assertThat(dump).contains(" sysui_flag_1: false\n");
+        assertThat(dump).contains(" sysui_flag_2: true\n");
+    }
+
+    private String dumpToString() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        mFeatureFlagManager.dump(mock(FileDescriptor.class), pw, new String[0]);
+        pw.flush();
+        String dump = sw.toString();
+        return dump;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt
index 9bd5bc7..98b9252 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.idle
 
 import android.hardware.Sensor
-import android.hardware.SensorEvent
 import android.hardware.SensorEventListener
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
@@ -29,11 +28,11 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.reset
 import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.never
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
@@ -42,6 +41,7 @@
 class AmbientLightModeMonitorTest : SysuiTestCase() {
     @Mock private lateinit var sensorManager: AsyncSensorManager
     @Mock private lateinit var sensor: Sensor
+    @Mock private lateinit var algorithm: AmbientLightModeMonitor.DebounceAlgorithm
 
     private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor
 
@@ -50,100 +50,56 @@
         MockitoAnnotations.initMocks(this)
 
         `when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(sensor)
-        ambientLightModeMonitor = AmbientLightModeMonitor(sensorManager)
+
+        ambientLightModeMonitor = AmbientLightModeMonitor(algorithm, sensorManager)
     }
 
     @Test
-    fun shouldTriggerCallbackImmediatelyOnStart() {
+    fun shouldRegisterSensorEventListenerOnStart() {
         val callback = mock(AmbientLightModeMonitor.Callback::class.java)
         ambientLightModeMonitor.start(callback)
 
-        // Monitor just started, should receive UNDECIDED.
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED)
+        verify(sensorManager).registerListener(any(), eq(sensor), anyInt())
+    }
 
-        // Receives SensorEvent, and now mode is LIGHT.
+    @Test
+    fun shouldUnregisterSensorEventListenerOnStop() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        ambientLightModeMonitor.start(callback)
+
         val sensorEventListener = captureSensorEventListener()
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(15f))
 
-        // Stop monitor.
         ambientLightModeMonitor.stop()
 
-        // Restart monitor.
-        reset(callback)
-        ambientLightModeMonitor.start(callback)
-
-        // Verify receiving current mode (LIGHT) immediately.
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+        verify(sensorManager).unregisterListener(eq(sensorEventListener))
     }
 
     @Test
-    fun shouldReportDarkModeWhenSensorValueIsLessThanTen() {
+    fun shouldStartDebounceAlgorithmOnStart() {
         val callback = mock(AmbientLightModeMonitor.Callback::class.java)
         ambientLightModeMonitor.start(callback)
 
-        val sensorEventListener = captureSensorEventListener()
-        reset(callback)
-
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(0f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(1f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(5f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(9.9f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+        verify(algorithm).start(eq(callback))
     }
 
     @Test
-    fun shouldReportLightModeWhenSensorValueIsGreaterThanOrEqualToTen() {
+    fun shouldStopDebounceAlgorithmOnStop() {
         val callback = mock(AmbientLightModeMonitor.Callback::class.java)
         ambientLightModeMonitor.start(callback)
+        ambientLightModeMonitor.stop()
 
-        val sensorEventListener = captureSensorEventListener()
-        reset(callback)
-
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(10f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(10.1f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(15f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(100f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+        verify(algorithm).stop()
     }
 
     @Test
-    fun shouldOnlyTriggerCallbackWhenValueChanges() {
+    fun shouldNotRegisterForSensorUpdatesIfSensorNotAvailable() {
+        `when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(null)
+        val ambientLightModeMonitor = AmbientLightModeMonitor(algorithm, sensorManager)
+
         val callback = mock(AmbientLightModeMonitor.Callback::class.java)
         ambientLightModeMonitor.start(callback)
 
-        val sensorEventListener = captureSensorEventListener()
-
-        // Light mode, should trigger callback.
-        reset(callback)
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(20f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-
-        // Light mode again, should NOT trigger callback.
-        reset(callback)
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(25f))
-        verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-
-        // Dark mode, should trigger callback.
-        reset(callback)
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(2f))
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-
-        // Dark mode again, should not trigger callback.
-        reset(callback)
-        sensorEventListener.onSensorChanged(sensorEventWithSingleValue(3f))
-        verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+        verify(sensorManager, never()).registerListener(any(), any(Sensor::class.java), anyInt())
     }
 
     // Captures [SensorEventListener], assuming it has been registered with [sensorManager].
@@ -152,9 +108,4 @@
         verify(sensorManager).registerListener(captor.capture(), any(), anyInt())
         return captor.value
     }
-
-    // Returns a [SensorEvent] with a single [value].
-    private fun sensorEventWithSingleValue(value: Float): SensorEvent {
-        return SensorEvent(sensor, 1, 1, FloatArray(1) { value })
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt
new file mode 100644
index 0000000..ebc7345
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt
@@ -0,0 +1,358 @@
+/*
+ * 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.systemui.idle
+
+import android.content.res.Resources
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.`when`
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class LightSensorDebounceAlgorithmTest : SysuiTestCase() {
+    @Mock private lateinit var resources: Resources
+
+    private val systemClock = FakeSystemClock()
+    private val executor = FakeExecutor(systemClock)
+
+    private lateinit var algorithm: LightSensorEventsDebounceAlgorithm
+
+    private val mockLightModeThreshold = 5
+    private val mockDarkModeThreshold = 2
+    private val mockLightModeSpan = 100
+    private val mockDarkModeSpan = 50
+    private val mockLightModeFrequency = 10
+    private val mockDarkModeFrequency = 5
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(resources.getInteger(R.integer.config_ambientLightModeThreshold))
+                .thenReturn(mockLightModeThreshold)
+        `when`(resources.getInteger(R.integer.config_ambientDarkModeThreshold))
+                .thenReturn(mockDarkModeThreshold)
+        `when`(resources.getInteger(R.integer.config_ambientLightModeSamplingSpanMillis))
+                .thenReturn(mockLightModeSpan)
+        `when`(resources.getInteger(R.integer.config_ambientDarkModeSamplingSpanMillis))
+                .thenReturn(mockDarkModeSpan)
+        `when`(resources.getInteger(R.integer.config_ambientLightModeSamplingFrequencyMillis))
+                .thenReturn(mockLightModeFrequency)
+        `when`(resources.getInteger(R.integer.config_ambientDarkModeSamplingFrequencyMillis))
+                .thenReturn(mockDarkModeFrequency)
+
+        algorithm = LightSensorEventsDebounceAlgorithm(executor, resources)
+    }
+
+    @Test
+    fun shouldOnlyTriggerCallbackWhenValueChanges() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        algorithm.start(callback)
+
+        // Light mode, should trigger callback.
+        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
+        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+        reset(callback)
+
+        // Light mode again, should NOT trigger callback.
+        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
+        verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+        reset(callback)
+
+        // Dark mode, should trigger callback.
+        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
+        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+        reset(callback)
+
+        // Dark mode again, should not trigger callback.
+        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
+        verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+    }
+
+    @Test
+    fun shouldReportUndecidedWhenNeitherLightNorDarkClaimIsTrue() {
+        algorithm.isDarkMode = false
+        algorithm.isLightMode = false
+
+        assertThat(algorithm.mode).isEqualTo(
+                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED)
+    }
+
+    @Test
+    fun shouldReportDarkModeAsLongAsDarkModeClaimIsTrue() {
+        algorithm.isDarkMode = true
+        algorithm.isLightMode = false
+
+        assertThat(algorithm.mode).isEqualTo(
+                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+
+        algorithm.isLightMode = true
+        assertThat(algorithm.mode).isEqualTo(
+                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+    }
+
+    @Test
+    fun shouldReportLightModeWhenLightModeClaimIsTrueAndDarkModeClaimIsFalse() {
+        algorithm.isLightMode = true
+        algorithm.isDarkMode = false
+
+        assertThat(algorithm.mode).isEqualTo(
+                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+    }
+
+    @Test
+    fun shouldSetIsLightModeToTrueWhenBundleAverageIsGreaterThanThreshold() {
+        // Note: [mockLightModeThreshold] is 5.0.
+        algorithm.bundleAverageLightMode = 5.1
+        assertThat(algorithm.isLightMode).isTrue()
+
+        algorithm.bundleAverageLightMode = 10.0
+        assertThat(algorithm.isLightMode).isTrue()
+
+        algorithm.bundleAverageLightMode = 20.0
+        assertThat(algorithm.isLightMode).isTrue()
+
+        algorithm.bundleAverageLightMode = 5.0
+        assertThat(algorithm.isLightMode).isFalse()
+
+        algorithm.bundleAverageLightMode = 3.0
+        assertThat(algorithm.isLightMode).isFalse()
+
+        algorithm.bundleAverageLightMode = 0.0
+        assertThat(algorithm.isLightMode).isFalse()
+    }
+
+    @Test
+    fun shouldSetIsDarkModeToTrueWhenBundleAverageIsLessThanThreshold() {
+        // Note: [mockDarkModeThreshold] is 2.0.
+        algorithm.bundleAverageDarkMode = 1.9
+        assertThat(algorithm.isDarkMode).isTrue()
+
+        algorithm.bundleAverageDarkMode = 1.0
+        assertThat(algorithm.isDarkMode).isTrue()
+
+        algorithm.bundleAverageDarkMode = 0.0
+        assertThat(algorithm.isDarkMode).isTrue()
+
+        algorithm.bundleAverageDarkMode = 2.0
+        assertThat(algorithm.isDarkMode).isFalse()
+
+        algorithm.bundleAverageDarkMode = 3.0
+        assertThat(algorithm.isDarkMode).isFalse()
+
+        algorithm.bundleAverageDarkMode = 10.0
+        assertThat(algorithm.isDarkMode).isFalse()
+    }
+
+    @Test
+    fun shouldCorrectlyCalculateAverageFromABundle() {
+        // For light mode.
+        algorithm.bundleLightMode = arrayListOf(1.0f, 3.0f, 5.0f, 7.0f)
+        assertThat(algorithm.bundleAverageLightMode).isEqualTo(4.0)
+
+        algorithm.bundleLightMode = arrayListOf(2.0f, 4.0f, 6.0f, 8.0f)
+        assertThat(algorithm.bundleAverageLightMode).isEqualTo(5.0)
+
+        // For dark mode.
+        algorithm.bundleDarkMode = arrayListOf(1.0f, 3.0f, 5.0f, 7.0f, 9.0f)
+        assertThat(algorithm.bundleAverageDarkMode).isEqualTo(5.0)
+
+        algorithm.bundleDarkMode = arrayListOf(2.0f, 4.0f, 6.0f, 8.0f, 10.0f)
+        assertThat(algorithm.bundleAverageDarkMode).isEqualTo(6.0)
+    }
+
+    @Test
+    fun shouldAddSensorEventUpdatesToBundles() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        // On start() one bundle is created for light and dark mode each.
+        algorithm.start(callback)
+        executor.runAllReady()
+
+        // Add 1 more bundle to queue for each mode.
+        algorithm.bundlesQueueLightMode.add(ArrayList())
+        algorithm.bundlesQueueDarkMode.add(ArrayList())
+
+        algorithm.onUpdateLightSensorEvent(1.0f)
+        algorithm.onUpdateLightSensorEvent(1.0f)
+        algorithm.onUpdateLightSensorEvent(2.0f)
+        algorithm.onUpdateLightSensorEvent(3.0f)
+        algorithm.onUpdateLightSensorEvent(4.0f)
+
+        val expectedValues = listOf(1.0f, 1.0f, 2.0f, 3.0f, 4.0f)
+
+        assertBundleContainsAll(algorithm.bundlesQueueLightMode[0], expectedValues)
+        assertBundleContainsAll(algorithm.bundlesQueueLightMode[1], expectedValues)
+        assertBundleContainsAll(algorithm.bundlesQueueDarkMode[0], expectedValues)
+        assertBundleContainsAll(algorithm.bundlesQueueDarkMode[1], expectedValues)
+    }
+
+    @Test
+    fun shouldCorrectlyEnqueueLightModeBundles() {
+        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(0)
+
+        algorithm.enqueueLightModeBundle.run()
+        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(1)
+
+        algorithm.enqueueLightModeBundle.run()
+        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(2)
+
+        algorithm.enqueueLightModeBundle.run()
+        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(3)
+
+        // Verifies dark mode bundles queue is not impacted.
+        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(0)
+    }
+
+    @Test
+    fun shouldCorrectlyEnqueueDarkModeBundles() {
+        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(0)
+
+        algorithm.enqueueDarkModeBundle.run()
+        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(1)
+
+        algorithm.enqueueDarkModeBundle.run()
+        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(2)
+
+        algorithm.enqueueDarkModeBundle.run()
+        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(3)
+
+        // Verifies light mode bundles queue is not impacted.
+        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(0)
+    }
+
+    @Test
+    fun shouldCorrectlyDequeueLightModeBundles() {
+        // Sets up the light mode bundles queue.
+        val bundle1 = arrayListOf(1.0f, 3.0f, 6.0f, 9.0f)
+        val bundle2 = arrayListOf(5.0f, 10f)
+        val bundle3 = arrayListOf(2.0f, 4.0f)
+        algorithm.bundlesQueueLightMode.add(bundle1)
+        algorithm.bundlesQueueLightMode.add(bundle2)
+        algorithm.bundlesQueueLightMode.add(bundle3)
+
+        // The committed bundle should be the first one in queue.
+        algorithm.dequeueLightModeBundle.run()
+        assertBundleContainsAll(algorithm.bundleLightMode, bundle1)
+
+        algorithm.dequeueLightModeBundle.run()
+        assertBundleContainsAll(algorithm.bundleLightMode, bundle2)
+
+        algorithm.dequeueLightModeBundle.run()
+        assertBundleContainsAll(algorithm.bundleLightMode, bundle3)
+
+        // Verifies that the dark mode bundle is not impacted.
+        assertBundleContainsAll(algorithm.bundleDarkMode, listOf())
+    }
+
+    @Test
+    fun shouldCorrectlyDequeueDarkModeBundles() {
+        // Sets up the dark mode bundles queue.
+        val bundle1 = arrayListOf(2.0f, 4.0f)
+        val bundle2 = arrayListOf(5.0f, 10f)
+        val bundle3 = arrayListOf(1.0f, 3.0f, 6.0f, 9.0f)
+        algorithm.bundlesQueueDarkMode.add(bundle1)
+        algorithm.bundlesQueueDarkMode.add(bundle2)
+        algorithm.bundlesQueueDarkMode.add(bundle3)
+
+        // The committed bundle should be the first one in queue.
+        algorithm.dequeueDarkModeBundle.run()
+        assertBundleContainsAll(algorithm.bundleDarkMode, bundle1)
+
+        algorithm.dequeueDarkModeBundle.run()
+        assertBundleContainsAll(algorithm.bundleDarkMode, bundle2)
+
+        algorithm.dequeueDarkModeBundle.run()
+        assertBundleContainsAll(algorithm.bundleDarkMode, bundle3)
+
+        // Verifies that the light mode bundle is not impacted.
+        assertBundleContainsAll(algorithm.bundleLightMode, listOf())
+    }
+
+    @Test
+    fun shouldSetLightSensorLevelFromSensorEventUpdates() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        algorithm.start(callback)
+
+        algorithm.onUpdateLightSensorEvent(1.0f)
+        assertThat(algorithm.lightSensorLevel).isEqualTo(1.0f)
+
+        algorithm.onUpdateLightSensorEvent(10.0f)
+        assertThat(algorithm.lightSensorLevel).isEqualTo(10.0f)
+
+        algorithm.onUpdateLightSensorEvent(0.0f)
+        assertThat(algorithm.lightSensorLevel).isEqualTo(0.0f)
+    }
+
+    @Test
+    fun shouldRippleFromSensorEventUpdatesDownToAmbientLightMode() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        algorithm.start(callback)
+        executor.runAllReady()
+
+        // Sensor event updates.
+        algorithm.onUpdateLightSensorEvent(10.0f)
+        algorithm.onUpdateLightSensorEvent(15.0f)
+        algorithm.onUpdateLightSensorEvent(12.0f)
+        algorithm.onUpdateLightSensorEvent(10.0f)
+
+        // Advances time so both light and dark claims have been calculated.
+        systemClock.advanceTime((mockLightModeSpan + 1).toLong())
+
+        // Verifies the callback is triggered the ambient mode has changed LIGHT.
+        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+    }
+
+    @Test
+    fun shouldRippleFromSensorEventUpdatesDownToAmbientDarkMode() {
+        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
+        algorithm.start(callback)
+        executor.runAllReady()
+
+        // Sensor event updates.
+        algorithm.onUpdateLightSensorEvent(1.0f)
+        algorithm.onUpdateLightSensorEvent(0.5f)
+        algorithm.onUpdateLightSensorEvent(1.2f)
+        algorithm.onUpdateLightSensorEvent(0.8f)
+
+        // Advances time so both light and dark claims have been calculated.
+        systemClock.advanceTime((mockLightModeSpan + 1).toLong())
+
+        // Verifies the callback is triggered the ambient mode has changed DARK.
+        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+    }
+
+    // Asserts that [bundle] contains the same elements as [expected], not necessarily in the same
+    // order.
+    private fun assertBundleContainsAll(bundle: ArrayList<Float>, expected: Collection<Float>) {
+        assertThat(bundle.size).isEqualTo(expected.size)
+        assertThat(bundle.containsAll(expected))
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 90e3db7..5e73dbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.keyguard;
 
+import static com.android.keyguard.LockIconView.ICON_LOCK;
+import static com.android.keyguard.LockIconView.ICON_UNLOCK;
+
 import static junit.framework.Assert.assertEquals;
 
 import static org.mockito.Mockito.any;
@@ -29,7 +32,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.PointF;
-import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.AnimatedStateListDrawable;
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -59,7 +62,8 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import com.airbnb.lottie.LottieAnimationView;
 
@@ -81,7 +85,7 @@
     private static final String UNLOCKED_LABEL = "unlocked";
 
     private @Mock LockIconView mLockIconView;
-    private @Mock AnimatedVectorDrawable mIconDrawable;
+    private @Mock AnimatedStateListDrawable mIconDrawable;
     private @Mock Context mContext;
     private @Mock Resources mResources;
     private @Mock DisplayMetrics mDisplayMetrics;
@@ -94,11 +98,11 @@
     private @Mock DumpManager mDumpManager;
     private @Mock AccessibilityManager mAccessibilityManager;
     private @Mock ConfigurationController mConfigurationController;
-    private @Mock DelayableExecutor mDelayableExecutor;
     private @Mock Vibrator mVibrator;
     private @Mock AuthRippleController mAuthRippleController;
     private @Mock LottieAnimationView mAodFp;
     private @Mock LayoutInflater mLayoutInflater;
+    private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
 
     private LockIconViewController mLockIconViewController;
 
@@ -107,6 +111,14 @@
             ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
     private View.OnAttachStateChangeListener mAttachListener;
 
+    @Captor private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor =
+            ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+    private KeyguardStateController.Callback mKeyguardStateCallback;
+
+    @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor =
+            ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+    private StatusBarStateController.StateListener mStatusBarStateListener;
+
     @Captor private ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor;
     private AuthController.Callback mAuthControllerCallback;
 
@@ -212,6 +224,7 @@
 
         // WHEN all authenticators are registered
         mAuthControllerCallback.onAllAuthenticatorsRegistered();
+        mDelayableExecutor.runAllReady();
 
         // THEN lock icon view location is updated with the same coordinates as fpProps
         verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first));
@@ -271,6 +284,86 @@
         verify(mLockIconView).setContentDescription(UNLOCKED_LABEL);
     }
 
+    @Test
+    public void testLockIconStartState() {
+        // GIVEN lock icon state
+        setupShowLockIcon();
+
+        // WHEN lock icon controller is initialized
+        mLockIconViewController.init();
+        captureAttachListener();
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
+
+        // THEN the lock icon should show
+        verify(mLockIconView).updateIcon(ICON_LOCK, false);
+    }
+
+    @Test
+    public void testLockIcon_updateToUnlock() {
+        // GIVEN starting state for the lock icon
+        setupShowLockIcon();
+
+        // GIVEN lock icon controller is initialized and view is attached
+        mLockIconViewController.init();
+        captureAttachListener();
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
+        captureKeyguardStateCallback();
+        reset(mLockIconView);
+
+        // WHEN the unlocked state changes to canDismissLockScreen=true
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
+        mKeyguardStateCallback.onUnlockedChanged();
+
+        // THEN the unlock should show
+        verify(mLockIconView).updateIcon(ICON_UNLOCK, false);
+    }
+
+    @Test
+    public void testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() {
+        // GIVEN udfps not enrolled
+        setupUdfps();
+        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false);
+
+        // GIVEN starting state for the lock icon
+        setupShowLockIcon();
+
+        // GIVEN lock icon controller is initialized and view is attached
+        mLockIconViewController.init();
+        captureAttachListener();
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
+        captureStatusBarStateListener();
+        reset(mLockIconView);
+
+        // WHEN the dozing state changes
+        mStatusBarStateListener.onDozingChanged(true /* isDozing */);
+
+        // THEN the icon is cleared
+        verify(mLockIconView).clearIcon();
+    }
+
+    @Test
+    public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() {
+        // GIVEN udfps enrolled
+        setupUdfps();
+        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
+
+        // GIVEN starting state for the lock icon
+        setupShowLockIcon();
+
+        // GIVEN lock icon controller is initialized and view is attached
+        mLockIconViewController.init();
+        captureAttachListener();
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
+        captureStatusBarStateListener();
+        reset(mLockIconView);
+
+        // WHEN the dozing state changes
+        mStatusBarStateListener.onDozingChanged(true /* isDozing */);
+
+        // THEN the AOD lock icon should show
+        verify(mLockIconView).updateIcon(ICON_LOCK, true);
+    }
+
     private Pair<Integer, PointF> setupUdfps() {
         final PointF udfpsLocation = new PointF(50, 75);
         final int radius = 33;
@@ -290,6 +383,15 @@
         return new Pair(radius, udfpsLocation);
     }
 
+    private void setupShowLockIcon() {
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+        when(mStatusBarStateController.getDozeAmount()).thenReturn(0f);
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+    }
+
     private void captureAuthControllerCallback() {
         verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture());
         mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue();
@@ -300,6 +402,16 @@
         mAttachListener = mAttachCaptor.getValue();
     }
 
+    private void captureKeyguardStateCallback() {
+        verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture());
+        mKeyguardStateCallback = mKeyguardStateCaptor.getValue();
+    }
+
+    private void captureStatusBarStateListener() {
+        verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture());
+        mStatusBarStateListener = mStatusBarStateCaptor.getValue();
+    }
+
     private void captureKeyguardUpdateMonitorCallback() {
         verify(mKeyguardUpdateMonitor).registerCallback(
                 mKeyguardUpdateMonitorCallbackCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 750600ad..52173c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -212,12 +212,14 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
 
         assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
-                mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2));
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
+                TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(
+                mContext.getString(R.string.media_output_dialog_disconnected));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index c3d60d4..0252420 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -39,7 +39,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.CarrierText;
 import com.android.systemui.Dependency;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
@@ -64,7 +63,6 @@
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.Before;
@@ -186,10 +184,6 @@
         return new QSFragment(
                 new RemoteInputQuickSettingsDisabler(context, mock(ConfigurationController.class),
                         commandQueue),
-                new InjectionInflationController(
-                        SystemUIFactory.getInstance()
-                                .getSysUIComponent()
-                                .createViewInstanceCreatorFactory()),
                 mock(QSTileHost.class),
                 mock(StatusBarStateController.class),
                 commandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index c42b64a..6688960 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -19,6 +19,7 @@
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.filters.SmallTest;
@@ -26,6 +27,8 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 import com.android.wifitrackerlib.WifiEntry;
 
 import org.junit.After;
@@ -64,6 +67,7 @@
     @Mock
     private InternetDialogController mInternetDialogController;
 
+    private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
     private InternetDialog mInternetDialog;
     private View mDialogView;
     private View mSubTitle;
@@ -73,6 +77,7 @@
     private LinearLayout mConnectedWifi;
     private RecyclerView mWifiList;
     private LinearLayout mSeeAll;
+    private LinearLayout mWifiScanNotify;
 
     @Before
     public void setUp() {
@@ -91,7 +96,8 @@
         when(mInternetDialogController.getWifiManager()).thenReturn(mWifiManager);
 
         mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
-                mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler);
+                mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler,
+                mBgExecutor);
         mInternetDialog.mAdapter = mInternetAdapter;
         mInternetDialog.onAccessPointsChanged(mWifiEntries, mInternetWifiEntry);
         mInternetDialog.show();
@@ -104,6 +110,7 @@
         mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
         mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
         mSeeAll = mDialogView.requireViewById(R.id.see_all_layout);
+        mWifiScanNotify = mDialogView.requireViewById(R.id.wifi_scan_notify_layout);
     }
 
     @After
@@ -126,7 +133,7 @@
     public void updateDialog_withApmOn_internetDialogSubTitleGone() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(true);
 
         assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE);
     }
@@ -135,7 +142,7 @@
     public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(true);
 
         assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
     }
@@ -145,7 +152,7 @@
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
         when(mInternetDialogController.hasEthernet()).thenReturn(true);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(true);
 
         assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
     }
@@ -155,7 +162,7 @@
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
         when(mInternetDialogController.hasEthernet()).thenReturn(false);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(true);
 
         assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
     }
@@ -165,7 +172,7 @@
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
         when(mInternetDialogController.hasEthernet()).thenReturn(true);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(true);
 
         assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
     }
@@ -175,7 +182,7 @@
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
         when(mInternetDialogController.hasEthernet()).thenReturn(false);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(true);
 
         assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
     }
@@ -184,7 +191,7 @@
     public void updateDialog_withApmOn_mobileDataLayoutGone() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(true);
 
         assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
     }
@@ -194,7 +201,7 @@
         // The preconditions WiFi ON and Internet WiFi are already in setUp()
         doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(false);
 
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
     }
@@ -205,7 +212,7 @@
         mInternetDialog.onAccessPointsChanged(mWifiEntries, null /* connectedEntry*/);
         doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(false);
 
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
     }
@@ -215,7 +222,7 @@
         // The precondition WiFi ON is already in setUp()
         mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, mInternetWifiEntry);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(false);
 
         assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
@@ -225,7 +232,7 @@
     public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
         // The preconditions WiFi ON and WiFi entries are already in setUp()
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(false);
 
         assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
@@ -236,7 +243,7 @@
         // The preconditions WiFi ON and Internet WiFi are already in setUp()
         when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(false);
 
         assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mWifiToggle.getBackground()).isNotNull();
@@ -247,7 +254,7 @@
         // The preconditions WiFi ON and Internet WiFi are already in setUp()
         when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(false);
 
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
     }
@@ -257,13 +264,57 @@
         // The preconditions WiFi entries are already in setUp()
         when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
 
-        mInternetDialog.updateDialog();
+        mInternetDialog.updateDialog(false);
 
         assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
+    public void updateDialog_wifiOn_hideWifiScanNotify() {
+        // The preconditions WiFi ON and Internet WiFi are already in setUp()
+
+        mInternetDialog.updateDialog(false);
+
+        assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() {
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+        when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
+
+        mInternetDialog.updateDialog(false);
+
+        assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() {
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+        when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
+        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+        mInternetDialog.updateDialog(false);
+
+        assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() {
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+        when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
+        when(mInternetDialogController.isDeviceLocked()).thenReturn(false);
+
+        mInternetDialog.updateDialog(false);
+
+        assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE);
+        TextView wifiScanNotifyText = mDialogView.requireViewById(R.id.wifi_scan_notify_text);
+        assertThat(wifiScanNotifyText.getText().length()).isNotEqualTo(0);
+        assertThat(wifiScanNotifyText.getMovementMethod()).isNotNull();
+    }
+
+    @Test
     public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
         mSeeAll.performClick();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index e5ae65f..dbd5168 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -24,7 +24,7 @@
 import android.view.ViewRootImpl
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators
+import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
@@ -208,7 +208,7 @@
         notificationShadeDepthController.onPanelExpansionChanged(1f, tracking = false)
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(wallpaperController).setNotificationShadeZoom(
-                eq(Interpolators.getNotificationScrimAlpha(0.25f, false /* notifications */)))
+                eq(ShadeInterpolation.getNotificationScrimAlpha(0.25f)))
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 67cab74..b23d07a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -216,7 +216,7 @@
         mCallbackHandler = mock(CallbackHandler.class);
 
         mMockProvisionController = mock(DeviceProvisionedController.class);
-        when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(true);
+        when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true);
         doAnswer(invocation -> {
             mUserCallback = (DeviceProvisionedListener) invocation.getArguments()[0];
             mUserCallback.onUserSetupChanged();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 00dedd9..12f8282 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -21,7 +21,6 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -211,7 +210,7 @@
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
         setConnectivityViaCallbackInNetworkController(
                 NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
-        when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(false);
+        when(mMockProvisionController.isCurrentUserSetup()).thenReturn(false);
         mUserCallback.onUserSetupChanged();
         TestableLooper.get(this).processAllMessages();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index b13c4d9..ff91978 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -448,6 +448,20 @@
         verify(smartspaceView2).registerDataProvider(plugin)
     }
 
+    @Test
+    fun testConnectAttemptBeforeInitializationShouldNotCreateSession() {
+        // GIVEN an uninitalized smartspaceView
+        // WHEN the device is provisioned
+        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
+        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+        deviceProvisionedListener.onDeviceProvisionedChanged()
+
+        // THEN no calls to createSmartspaceSession should occur
+        verify(smartspaceManager, never()).createSmartspaceSession(any())
+        // THEN no listeners should be registered
+        verify(configurationController, never()).addCallback(any())
+    }
+
     private fun connectSession() {
         val view = controller.buildAndConnectView(fakeParent)
         smartspaceView = view as SmartspaceView
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 39d794d..ebeb591 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -60,6 +60,7 @@
 import android.util.ArraySet;
 import android.util.Pair;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -1405,25 +1406,26 @@
             mName = name;
         }
 
+        @NonNull
         @Override
         public String getName() {
             return mName;
         }
 
         @Override
-        public void setCallback(OnEndLifetimeExtensionCallback callback) {
+        public void setCallback(@NonNull OnEndLifetimeExtensionCallback callback) {
             this.callback = callback;
         }
 
         @Override
         public boolean shouldExtendLifetime(
-                NotificationEntry entry,
+                @NonNull NotificationEntry entry,
                 @CancellationReason int reason) {
             return shouldExtendLifetime;
         }
 
         @Override
-        public void cancelLifetimeExtension(NotificationEntry entry) {
+        public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
             if (onCancelLifetimeExtension != null) {
                 onCancelLifetimeExtension.run();
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
new file mode 100644
index 0000000..0cba0703
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.systemui.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
+import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener
+import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
+import com.android.systemui.statusbar.notification.row.NotificationGuts
+import com.android.systemui.util.mockito.argumentCaptor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class GutsCoordinatorTest : SysuiTestCase() {
+    private lateinit var coordinator: GutsCoordinator
+    private lateinit var notifLifetimeExtender: NotifLifetimeExtender
+    private lateinit var notifGutsViewListener: NotifGutsViewListener
+
+    private lateinit var entry1: NotificationEntry
+    private lateinit var entry2: NotificationEntry
+
+    @Mock private lateinit var notifGutsViewManager: NotifGutsViewManager
+    @Mock private lateinit var pipeline: NotifPipeline
+    @Mock private lateinit var dumpManager: DumpManager
+    @Mock private lateinit var logger: GutsCoordinatorLogger
+    @Mock private lateinit var lifetimeExtenderCallback: OnEndLifetimeExtensionCallback
+
+    @Before
+    fun setUp() {
+        initMocks(this)
+        coordinator = GutsCoordinator(notifGutsViewManager, logger, dumpManager)
+        coordinator.attach(pipeline)
+        notifLifetimeExtender = argumentCaptor<NotifLifetimeExtender>().let {
+            verify(pipeline).addNotificationLifetimeExtender(it.capture())
+            it.value!!
+        }
+        notifGutsViewListener = argumentCaptor<NotifGutsViewListener>().let {
+            verify(notifGutsViewManager).setGutsListener(it.capture())
+            it.value!!
+        }
+        notifLifetimeExtender.setCallback(lifetimeExtenderCallback)
+        entry1 = NotificationEntryBuilder().setId(1).build()
+        entry2 = NotificationEntryBuilder().setId(2).build()
+    }
+
+    @Test
+    fun testSimpleLifetimeExtension() {
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        notifGutsViewListener.onGutsClose(entry1)
+        verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+    }
+
+    @Test
+    fun testDoubleOpenLifetimeExtension() {
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        notifGutsViewListener.onGutsClose(entry1)
+        verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+    }
+
+    @Test
+    fun testTwoEntryLifetimeExtension() {
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
+        notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
+        notifGutsViewListener.onGutsOpen(entry2, mock(NotificationGuts::class.java))
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isTrue()
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isTrue()
+        notifGutsViewListener.onGutsClose(entry1)
+        verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isTrue()
+        notifGutsViewListener.onGutsClose(entry2)
+        verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry2)
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry1, 0)).isFalse()
+        assertThat(notifLifetimeExtender.shouldExtendLifetime(entry2, 0)).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 9f42fa4d..185d9cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -58,6 +58,8 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -110,9 +112,20 @@
         Settings.Secure.putIntForUser(mContext.getContentResolver(), NOTIFICATION_HISTORY_ENABLED,
                 1, UserHandle.USER_CURRENT);
 
+
+        // Interact with real instance of AmbientState.
+        mAmbientState = new AmbientState(mContext, mNotificationSectionsManager, mBypassController);
+
         // Inject dependencies before initializing the layout
         mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
         mDependency.injectMockDependency(ShadeController.class);
+        mDependency.injectTestDependency(
+                NotificationSectionsManager.class, mNotificationSectionsManager);
+        mDependency.injectTestDependency(GroupMembershipManager.class, mGroupMembershipManger);
+        mDependency.injectTestDependency(GroupExpansionManager.class, mGroupExpansionManager);
+        mDependency.injectTestDependency(AmbientState.class, mAmbientState);
+        mDependency.injectTestDependency(
+                UnlockedScreenOffAnimationController.class, mUnlockedScreenOffAnimationController);
 
         NotificationShelfController notificationShelfController =
                 mock(NotificationShelfController.class);
@@ -123,22 +136,12 @@
                         mNotificationSection
                 });
 
-        // Interact with real instance of AmbientState.
-        mAmbientState = new AmbientState(mContext, mNotificationSectionsManager, mBypassController);
-
         // The actual class under test.  You may need to work with this class directly when
         // testing anonymous class members of mStackScroller, like mMenuEventListener,
         // which refer to members of NotificationStackScrollLayout. The spy
         // holds a copy of the CUT's instances of these KeyguardBypassController, so they still
         // refer to the CUT's member variables, not the spy's member variables.
-        mStackScrollerInternal = new NotificationStackScrollLayout(
-                getContext(),
-                null,
-                mNotificationSectionsManager,
-                mGroupMembershipManger,
-                mGroupExpansionManager,
-                mAmbientState,
-                mUnlockedScreenOffAnimationController);
+        mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null);
         mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper);
         mStackScroller = spy(mStackScrollerInternal);
         mStackScroller.setShelfController(notificationShelfController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 52c6bac..747acdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -384,8 +384,6 @@
         when(mView.findViewById(R.id.notification_stack_scroller))
                 .thenReturn(mNotificationStackScrollLayout);
         when(mView.findViewById(R.id.communal_host)).thenReturn(mCommunalHostView);
-        when(mNotificationStackScrollLayout.getController())
-                .thenReturn(mNotificationStackScrollLayoutController);
         when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
         when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
                 .thenReturn(mHeadsUpCallback);
@@ -517,6 +515,7 @@
                 mControlsComponent);
         mNotificationPanelViewController.initDependencies(
                 mStatusBar,
+                () -> {},
                 mNotificationShelfController);
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
         mNotificationPanelViewController.setBar(mPanelBar);
@@ -585,6 +584,51 @@
     }
 
     @Test
+    public void onTouchForwardedFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(false);
+
+        boolean returnVal = mTouchHandler.onTouchForwardedFromStatusBar(
+                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
+
+        assertThat(returnVal).isFalse();
+        verify(mView, never()).dispatchTouchEvent(any());
+    }
+
+    @Test
+    public void onTouchForwardedFromStatusBar_viewNotEnabled_returnsTrueAndNoViewEvent() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+        when(mView.isEnabled()).thenReturn(false);
+
+        boolean returnVal = mTouchHandler.onTouchForwardedFromStatusBar(
+                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
+
+        assertThat(returnVal).isTrue();
+        verify(mView, never()).dispatchTouchEvent(any());
+    }
+
+    @Test
+    public void onTouchForwardedFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+        when(mView.isEnabled()).thenReturn(false);
+        MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0);
+
+        mTouchHandler.onTouchForwardedFromStatusBar(event);
+
+        verify(mView).dispatchTouchEvent(event);
+    }
+
+    @Test
+    public void onTouchForwardedFromStatusBar_panelAndViewEnabled_viewReceivesEvent() {
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+        when(mView.isEnabled()).thenReturn(true);
+        MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
+
+        mTouchHandler.onTouchForwardedFromStatusBar(event);
+
+        verify(mView).dispatchTouchEvent(event);
+    }
+
+    @Test
     public void testA11y_initializeNode() {
         AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
         mAccessibiltyDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index c62d73c..6e9bb2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -34,7 +34,6 @@
 
 import com.android.keyguard.LockIconViewController;
 import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.dock.DockManager;
@@ -55,7 +54,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.InjectionInflationController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -117,10 +115,6 @@
         when(mDockManager.isDocked()).thenReturn(false);
 
         mController = new NotificationShadeWindowViewController(
-                new InjectionInflationController(
-                        SystemUIFactory.getInstance()
-                                .getSysUIComponent()
-                                .createViewInstanceCreatorFactory()),
                 mCoordinator,
                 mPulseExpansionHandler,
                 mDynamicPrivacyController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 52a5e06..310a8ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -17,18 +17,25 @@
 package com.android.systemui.statusbar.phone
 
 import android.view.LayoutInflater
+import android.view.MotionEvent
 import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.view.ViewTreeObserver.OnPreDrawListener
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
+import org.mockito.ArgumentCaptor
 import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -36,11 +43,9 @@
 @SmallTest
 class PhoneStatusBarViewControllerTest : SysuiTestCase() {
 
-    private val stateChangeListener = TestStateChangedListener()
+    private val touchEventHandler = TestTouchEventHandler()
 
     @Mock
-    private lateinit var commandQueue: CommandQueue
-    @Mock
     private lateinit var panelViewController: PanelViewController
     @Mock
     private lateinit var panelView: ViewGroup
@@ -49,10 +54,14 @@
 
     @Mock
     private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController
+    @Mock
+    private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider
 
     private lateinit var view: PhoneStatusBarView
     private lateinit var controller: PhoneStatusBarViewController
 
+    private val unfoldConfig = UnfoldConfig()
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -63,58 +72,62 @@
             val parent = FrameLayout(mContext) // add parent to keep layout params
             view = LayoutInflater.from(mContext)
                 .inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
-            view.setPanel(panelViewController)
             view.setScrimController(scrimController)
+            view.setBar(mock(StatusBar::class.java))
         }
 
-        controller = PhoneStatusBarViewController(
-                view,
-                commandQueue,
-                null,
-                stateChangeListener
-        )
+        controller = createController(view)
     }
 
     @Test
-    fun constructor_setsPanelEnabledProviderOnView() {
-        var providerUsed = false
-        `when`(commandQueue.panelsEnabled()).then {
-            providerUsed = true
-            true
-        }
+    fun constructor_setsTouchHandlerOnView() {
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
 
-        // If the constructor correctly set a [PanelEnabledProvider], then it should be used
-        // when [PhoneStatusBarView.panelEnabled] is called.
-        view.panelEnabled()
+        view.onTouchEvent(event)
 
-        assertThat(providerUsed).isTrue()
+        assertThat(touchEventHandler.lastEvent).isEqualTo(event)
     }
 
     @Test
-    fun constructor_moveFromCenterAnimationIsNotNull_moveFromCenterAnimationInitialized() {
-        controller = PhoneStatusBarViewController(
-                view, commandQueue, moveFromCenterAnimation, stateChangeListener
-        )
+    fun onViewAttachedAndDrawn_moveFromCenterAnimationEnabled_moveFromCenterAnimationInitialized() {
+        val view = createViewMock()
+        val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
+        unfoldConfig.isEnabled = true
+        controller = createController(view)
+        controller.init()
 
-        verify(moveFromCenterAnimation).init(any(), any())
+        verify(view.viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
+        argumentCaptor.value.onPreDraw()
+
+        verify(moveFromCenterAnimation).onViewsReady(any(), any())
     }
 
-    @Test
-    fun constructor_setsExpansionStateChangedListenerOnView() {
-        assertThat(stateChangeListener.stateChangeCalled).isFalse()
-
-        // If the constructor correctly set the listener, then it should be used when
-        // [PhoneStatusBarView.panelExpansionChanged] is called.
-        view.panelExpansionChanged(0f, false)
-
-        assertThat(stateChangeListener.stateChangeCalled).isTrue()
+    private fun createViewMock(): PhoneStatusBarView {
+        val view = spy(view)
+        val viewTreeObserver = mock(ViewTreeObserver::class.java)
+        `when`(view.viewTreeObserver).thenReturn(viewTreeObserver)
+        `when`(view.isAttachedToWindow).thenReturn(true)
+        return view
     }
 
-    private class TestStateChangedListener : PhoneStatusBarView.PanelExpansionStateChangedListener {
-        var stateChangeCalled: Boolean = false
+    private fun createController(view: PhoneStatusBarView): PhoneStatusBarViewController {
+        return PhoneStatusBarViewController.Factory(
+            { progressProvider },
+            { moveFromCenterAnimation },
+            unfoldConfig
+        ).create(view, touchEventHandler)
+    }
 
-        override fun onPanelExpansionStateChanged() {
-            stateChangeCalled = true
+    private class UnfoldConfig : UnfoldTransitionConfig {
+        override var isEnabled: Boolean = false
+        override var isHingeAngleEnabled: Boolean = false
+    }
+
+    private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
+        var lastEvent: MotionEvent? = null
+        override fun handleTouchEvent(event: MotionEvent?): Boolean {
+            lastEvent = event
+            return false
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index ec7e07f90..fe34903 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone
 
+import android.view.MotionEvent
 import android.view.ViewGroup
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -48,63 +49,25 @@
         `when`(panelViewController.view).thenReturn(panelView)
 
         view = PhoneStatusBarView(mContext, null)
-        view.setPanel(panelViewController)
         view.setScrimController(scrimController)
         view.setBar(statusBar)
     }
 
     @Test
-    fun panelEnabled_providerReturnsTrue_returnsTrue() {
-        view.setPanelEnabledProvider { true }
+    fun panelExpansionChanged_expansionChangeListenerNotified() {
+        val listener = TestExpansionChangedListener()
+        view.setExpansionChangedListeners(listOf(listener))
+        val fraction = 0.4f
+        val isExpanded = true
 
-        assertThat(view.panelEnabled()).isTrue()
+        view.panelExpansionChanged(fraction, isExpanded)
+
+        assertThat(listener.fraction).isEqualTo(fraction)
+        assertThat(listener.isExpanded).isEqualTo(isExpanded)
     }
 
     @Test
-    fun panelEnabled_providerReturnsFalse_returnsFalse() {
-        view.setPanelEnabledProvider { false }
-
-        assertThat(view.panelEnabled()).isFalse()
-    }
-
-    @Test
-    fun panelEnabled_noProvider_noCrash() {
-        view.panelEnabled()
-        // No assert needed, just testing no crash
-    }
-
-    @Test
-    fun panelExpansionChanged_fracZero_stateChangeListenerNotified() {
-        val listener = TestExpansionStateChangedListener()
-        view.setPanelExpansionStateChangedListener(listener)
-
-        view.panelExpansionChanged(0f, false)
-
-        assertThat(listener.stateChangeCalled).isTrue()
-    }
-
-    @Test
-    fun panelExpansionChanged_fracOne_stateChangeListenerNotified() {
-        val listener = TestExpansionStateChangedListener()
-        view.setPanelExpansionStateChangedListener(listener)
-
-        view.panelExpansionChanged(1f, false)
-
-        assertThat(listener.stateChangeCalled).isTrue()
-    }
-
-    @Test
-    fun panelExpansionChanged_fracHalf_stateChangeListenerNotNotified() {
-        val listener = TestExpansionStateChangedListener()
-        view.setPanelExpansionStateChangedListener(listener)
-
-        view.panelExpansionChanged(0.5f, false)
-
-        assertThat(listener.stateChangeCalled).isFalse()
-    }
-
-    @Test
-    fun panelExpansionChanged_noStateChangeListener_noCrash() {
+    fun panelExpansionChanged_noListeners_noCrash() {
         view.panelExpansionChanged(1f, false)
         // No assert needed, just testing no crash
     }
@@ -144,17 +107,52 @@
     }
 
     @Test
-    fun panelStateChanged_noListener_noCrash() {
-        view.panelExpansionChanged(1f, true)
+    fun onTouchEvent_listenerNotified() {
+        val handler = TestTouchEventHandler()
+        view.setTouchEventHandler(handler)
+
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+        view.onTouchEvent(event)
+
+        assertThat(handler.lastEvent).isEqualTo(event)
+    }
+
+    @Test
+    fun onTouchEvent_listenerReturnsTrue_viewReturnsTrue() {
+        val handler = TestTouchEventHandler()
+        view.setTouchEventHandler(handler)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+        handler.returnValue = true
+
+        assertThat(view.onTouchEvent(event)).isTrue()
+    }
+
+    @Test
+    fun onTouchEvent_listenerReturnsFalse_viewReturnsFalse() {
+        val handler = TestTouchEventHandler()
+        view.setTouchEventHandler(handler)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+        handler.returnValue = false
+
+        assertThat(view.onTouchEvent(event)).isFalse()
+    }
+
+    @Test
+    fun onTouchEvent_noListener_noCrash() {
+        view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
         // No assert needed, just testing no crash
     }
 
-    private class TestExpansionStateChangedListener
-        : PhoneStatusBarView.PanelExpansionStateChangedListener {
-        var stateChangeCalled: Boolean = false
+    private class TestExpansionChangedListener
+        : StatusBar.ExpansionChangedListener {
+        var fraction: Float = 0f
+        var isExpanded: Boolean = false
 
-        override fun onPanelExpansionStateChanged() {
-            stateChangeCalled = true
+        override fun onExpansionChanged(expansion: Float, expanded: Boolean) {
+            this.fraction = expansion
+            this.isExpanded = expanded
         }
     }
 
@@ -164,4 +162,13 @@
             this.state = state
         }
     }
+
+    private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
+        var lastEvent: MotionEvent? = null
+        var returnValue: Boolean = false
+        override fun handleTouchEvent(event: MotionEvent?): Boolean {
+            lastEvent = event
+            return returnValue
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 705112a..6849dab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1063,7 +1063,7 @@
         HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
                 ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
                 ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED,
-                ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED));
+                ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED, ScrimState.AUTH_SCRIMMED_SHADE));
 
         for (ScrimState state : ScrimState.values()) {
             if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
@@ -1087,6 +1087,44 @@
     }
 
     @Test
+    public void testAuthScrim_notifScrimOpaque_whenShadeFullyExpanded() {
+        // GIVEN device has an activity showing ('UNLOCKED' state can occur on the lock screen
+        // with the camera app occluding the keyguard)
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.setRawPanelExpansionFraction(1);
+        // notifications scrim alpha change require calling setQsPosition
+        mScrimController.setQsPosition(0, 300);
+        finishAnimationsImmediately();
+
+        // WHEN the user triggers the auth bouncer
+        mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+        finishAnimationsImmediately();
+
+        assertEquals("Behind scrim should be opaque",
+                mScrimBehind.getViewAlpha(), 1, 0.0);
+        assertEquals("Notifications scrim should be opaque",
+                mNotificationsScrim.getViewAlpha(), 1, 0.0);
+    }
+
+    @Test
+    public void testAuthScrimKeyguard() {
+        // GIVEN device is on the keyguard
+        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        finishAnimationsImmediately();
+
+        // WHEN the user triggers the auth bouncer
+        mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+        finishAnimationsImmediately();
+
+        // THEN the front scrim is updated and the KEYGUARD scrims are the same as the
+        // KEYGUARD scrim state
+        assertScrimAlpha(Map.of(
+                mScrimInFront, SEMI_TRANSPARENT,
+                mScrimBehind, SEMI_TRANSPARENT,
+                mNotificationsScrim, TRANSPARENT));
+    }
+
+    @Test
     public void testScrimsVisible_whenShadeVisible() {
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.setRawPanelExpansionFraction(0.3f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 741d95f..943d3c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -144,6 +144,7 @@
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
 import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
 import com.android.systemui.util.WallpaperController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.concurrency.MessageRouterImpl;
@@ -259,7 +260,7 @@
     @Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
     @Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
     @Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
-    @Mock private Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimationLazy;
+    @Mock private Lazy<NaturalRotationUnfoldProgressProvider> mNaturalRotationProgressProvider;
     @Mock private Lazy<UnfoldTransitionWallpaperController> mUnfoldWallpaperController;
     @Mock private WallpaperController mWallpaperController;
     @Mock private OngoingCallController mOngoingCallController;
@@ -276,6 +277,7 @@
     @Mock private StartingSurface mStartingSurface;
     @Mock private OperatorNameViewController mOperatorNameViewController;
     @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
+    @Mock private PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
     @Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
     @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
     private ShadeController mShadeController;
@@ -314,7 +316,6 @@
 
         mContext.setTheme(R.style.Theme_SystemUI_LightWallpaper);
 
-        when(mStackScroller.getController()).thenReturn(mStackScrollerController);
         when(mStackScrollerController.getView()).thenReturn(mStackScroller);
         when(mStackScrollerController.getNotificationListContainer()).thenReturn(
                 mNotificationListContainer);
@@ -430,6 +431,7 @@
                 mExtensionController,
                 mUserInfoControllerImpl,
                 mOperatorNameViewControllerFactory,
+                mPhoneStatusBarViewControllerFactory,
                 mPhoneStatusBarPolicy,
                 mKeyguardIndicationController,
                 mDemoModeController,
@@ -440,7 +442,7 @@
                 mUnfoldTransitionConfig,
                 mUnfoldLightRevealOverlayAnimationLazy,
                 mUnfoldWallpaperController,
-                mMoveFromCenterAnimationLazy,
+                mNaturalRotationProgressProvider,
                 mWallpaperController,
                 mOngoingCallController,
                 mAnimationScheduler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
new file mode 100644
index 0000000..5129f85
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
@@ -0,0 +1,241 @@
+/*
+ * 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.systemui.statusbar.policy
+
+import android.os.Handler
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class DeviceProvisionedControllerImplTest : SysuiTestCase() {
+
+    companion object {
+        private const val START_USER = 0
+    }
+
+    private lateinit var controller: DeviceProvisionedControllerImpl
+
+    @Mock
+    private lateinit var userTracker: UserTracker
+    @Mock
+    private lateinit var dumpManager: DumpManager
+    @Mock
+    private lateinit var listener: DeviceProvisionedController.DeviceProvisionedListener
+    @Captor
+    private lateinit var userTrackerCallbackCaptor: ArgumentCaptor<UserTracker.Callback>
+
+    private lateinit var mainExecutor: FakeExecutor
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var settings: FakeSettings
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        mainExecutor = FakeExecutor(FakeSystemClock())
+        settings = FakeSettings()
+        `when`(userTracker.userId).thenReturn(START_USER)
+
+        controller = DeviceProvisionedControllerImpl(
+                settings,
+                settings,
+                userTracker,
+                dumpManager,
+                Handler(testableLooper.looper),
+                mainExecutor
+        )
+    }
+
+    @Test
+    fun testNotProvisionedByDefault() {
+        init()
+        assertThat(controller.isDeviceProvisioned).isFalse()
+    }
+
+    @Test
+    fun testNotUserSetupByDefault() {
+        init()
+        assertThat(controller.isUserSetup(START_USER)).isFalse()
+    }
+
+    @Test
+    fun testProvisionedWhenCreated() {
+        settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+        init()
+
+        assertThat(controller.isDeviceProvisioned).isTrue()
+    }
+
+    @Test
+    fun testUserSetupWhenCreated() {
+        settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+        init()
+
+        assertThat(controller.isUserSetup(START_USER))
+    }
+
+    @Test
+    fun testDeviceProvisionedChange() {
+        init()
+
+        settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+        testableLooper.processAllMessages() // background observer
+
+        assertThat(controller.isDeviceProvisioned).isTrue()
+    }
+
+    @Test
+    fun testUserSetupChange() {
+        init()
+
+        settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+        testableLooper.processAllMessages() // background observer
+
+        assertThat(controller.isUserSetup(START_USER)).isTrue()
+    }
+
+    @Test
+    fun testUserSetupChange_otherUser() {
+        init()
+        val otherUser = 10
+
+        settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser)
+        testableLooper.processAllMessages() // background observer
+
+        assertThat(controller.isUserSetup(START_USER)).isFalse()
+        assertThat(controller.isUserSetup(otherUser)).isTrue()
+    }
+
+    @Test
+    fun testCurrentUserSetup() {
+        val otherUser = 10
+        settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser)
+        init()
+
+        assertThat(controller.isCurrentUserSetup).isFalse()
+        switchUser(otherUser)
+        testableLooper.processAllMessages()
+
+        assertThat(controller.isCurrentUserSetup).isTrue()
+    }
+
+    @Test
+    fun testListenerNotCalledOnAdd() {
+        init()
+        controller.addCallback(listener)
+
+        mainExecutor.runAllReady()
+
+        verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onUserSwitched()
+    }
+
+    @Test
+    fun testListenerCalledOnUserSwitched() {
+        init()
+        controller.addCallback(listener)
+
+        switchUser(10)
+
+        testableLooper.processAllMessages()
+        mainExecutor.runAllReady()
+
+        verify(listener).onUserSwitched()
+        verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onDeviceProvisionedChanged()
+    }
+
+    @Test
+    fun testListenerCalledOnUserSetupChanged() {
+        init()
+        controller.addCallback(listener)
+
+        settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+        testableLooper.processAllMessages()
+        mainExecutor.runAllReady()
+
+        verify(listener, never()).onUserSwitched()
+        verify(listener).onUserSetupChanged()
+        verify(listener, never()).onDeviceProvisionedChanged()
+    }
+
+    @Test
+    fun testListenerCalledOnDeviceProvisionedChanged() {
+        init()
+        controller.addCallback(listener)
+
+        settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+        testableLooper.processAllMessages()
+        mainExecutor.runAllReady()
+
+        verify(listener, never()).onUserSwitched()
+        verify(listener, never()).onUserSetupChanged()
+        verify(listener).onDeviceProvisionedChanged()
+    }
+
+    @Test
+    fun testRemoveListener() {
+        init()
+        controller.addCallback(listener)
+        controller.removeCallback(listener)
+
+        switchUser(10)
+        settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+        settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+
+        testableLooper.processAllMessages()
+        mainExecutor.runAllReady()
+
+        verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onUserSwitched()
+    }
+
+    private fun init() {
+        controller.init()
+        verify(userTracker).addCallback(capture(userTrackerCallbackCaptor), any())
+    }
+
+    private fun switchUser(toUser: Int) {
+        `when`(userTracker.userId).thenReturn(toUser)
+        userTrackerCallbackCaptor.value.onUserChanged(toUser, mContext)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
new file mode 100644
index 0000000..a3f17aa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.systemui.unfold.util
+
+import android.testing.AndroidTestingRunner
+import android.view.IRotationWatcher
+import android.view.IWindowManager
+import android.view.Surface
+import androidx.test.filters.SmallTest
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.util.mockito.any
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
+
+    @Mock
+    lateinit var windowManager: IWindowManager
+
+    @Mock
+    lateinit var sourceProvider: UnfoldTransitionProgressProvider
+
+    @Mock
+    lateinit var transitionListener: TransitionProgressListener
+
+    lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
+
+    private val sourceProviderListenerCaptor =
+        ArgumentCaptor.forClass(TransitionProgressListener::class.java)
+    private val rotationWatcherCaptor =
+        ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        progressProvider = NaturalRotationUnfoldProgressProvider(
+            context,
+            windowManager,
+            sourceProvider
+        )
+
+        progressProvider.init()
+
+        verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
+        verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any())
+
+        progressProvider.addCallback(transitionListener)
+    }
+
+    @Test
+    fun testNaturalRotation0_sendTransitionStartedEvent_eventReceived() {
+        onRotationChanged(Surface.ROTATION_0)
+
+        source.onTransitionStarted()
+
+        verify(transitionListener).onTransitionStarted()
+    }
+
+    @Test
+    fun testNaturalRotation0_sendTransitionProgressEvent_eventReceived() {
+        onRotationChanged(Surface.ROTATION_0)
+
+        source.onTransitionProgress(0.5f)
+
+        verify(transitionListener).onTransitionProgress(0.5f)
+    }
+
+    @Test
+    fun testNotNaturalRotation90_sendTransitionStartedEvent_eventNotReceived() {
+        onRotationChanged(Surface.ROTATION_90)
+
+        source.onTransitionStarted()
+
+        verify(transitionListener, never()).onTransitionStarted()
+    }
+
+    @Test
+    fun testNaturalRotation90_sendTransitionProgressEvent_eventNotReceived() {
+        onRotationChanged(Surface.ROTATION_90)
+
+        source.onTransitionProgress(0.5f)
+
+        verify(transitionListener, never()).onTransitionProgress(0.5f)
+    }
+
+    @Test
+    fun testRotationBecameUnnaturalDuringTransition_sendsTransitionFinishedEvent() {
+        onRotationChanged(Surface.ROTATION_0)
+        source.onTransitionStarted()
+        clearInvocations(transitionListener)
+
+        onRotationChanged(Surface.ROTATION_90)
+
+        verify(transitionListener).onTransitionFinished()
+    }
+
+    @Test
+    fun testRotationBecameNaturalDuringTransition_sendsTransitionStartedEvent() {
+        onRotationChanged(Surface.ROTATION_90)
+        source.onTransitionStarted()
+        clearInvocations(transitionListener)
+
+        onRotationChanged(Surface.ROTATION_0)
+
+        verify(transitionListener).onTransitionStarted()
+    }
+
+    private fun onRotationChanged(rotation: Int) {
+        rotationWatcherCaptor.value.onRotationChanged(rotation)
+    }
+
+    private val source: TransitionProgressListener
+        get() = sourceProviderListenerCaptor.value
+}
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml
deleted file mode 100644
index bd52901..0000000
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 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>
-    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
-    <dimen name="quick_qs_offset_height">28dp</dimen>
-    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
-    <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
index 9254b4d..c340432 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
@@ -44,14 +44,6 @@
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height_portrait">48dp</dimen>
-    <dimen name="status_bar_height_landscape">28dp</dimen>
-    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
-    <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
-
 </resources>
 
 
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml
deleted file mode 100644
index bd52901..0000000
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 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>
-    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
-    <dimen name="quick_qs_offset_height">28dp</dimen>
-    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
-    <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
index 80c997a..928d9df 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
@@ -56,14 +56,6 @@
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height_portrait">48dp</dimen>
-    <dimen name="status_bar_height_landscape">28dp</dimen>
-    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
-    <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
-
 </resources>
 
 
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml
deleted file mode 100644
index 2e971de..0000000
--- a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Copyright (C) 2020 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>
-    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
-    <dimen name="quick_qs_offset_height">28dp</dimen>
-    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
-    <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml
index 9f558d0..62f0535 100644
--- a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml
@@ -48,14 +48,6 @@
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height_portrait">136px</dimen>
-    <dimen name="status_bar_height_landscape">28dp</dimen>
-    <!-- Height of area above QQS where battery/time go (equal to status bar) -->
-    <dimen name="quick_qs_offset_height">136px</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">488px</dimen>
-
 </resources>
 
 
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml
deleted file mode 100644
index bd52901..0000000
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 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>
-    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
-    <dimen name="quick_qs_offset_height">28dp</dimen>
-    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
-    <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
index 6fb3c7f..a9f8b4b 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
@@ -47,14 +47,6 @@
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height_portrait">48dp</dimen>
-    <dimen name="status_bar_height_landscape">28dp</dimen>
-    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
-    <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
-
 </resources>
 
 
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml
deleted file mode 100644
index bd52901..0000000
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 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>
-    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
-    <dimen name="quick_qs_offset_height">28dp</dimen>
-    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
-    <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
index 7c29ffb..be7d0e4 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
@@ -47,14 +47,6 @@
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height_portrait">48dp</dimen>
-    <dimen name="status_bar_height_landscape">28dp</dimen>
-    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
-    <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
-
 </resources>
 
 
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml
deleted file mode 100644
index df2f3d1..0000000
--- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Copyright (C) 2020 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>
-    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
-    <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
index 8d0227e..cc51ebe 100644
--- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
@@ -19,16 +19,6 @@
     <string translatable="false" name="config_mainBuiltInDisplayCutout"></string>
     <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation"></string>
 
-    <!-- Height of the status bar in portrait. The height should be
-         Max((status bar content height + waterfall top size), top cutout size) -->
-    <dimen name="status_bar_height_portrait">28dp</dimen>
-    <!-- Max((28 + 20), 0) = 48 -->
-    <dimen name="status_bar_height_landscape">48dp</dimen>
-    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
-    <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
-
     <dimen name="waterfall_display_left_edge_size">20dp</dimen>
     <dimen name="waterfall_display_top_edge_size">0dp</dimen>
     <dimen name="waterfall_display_right_edge_size">20dp</dimen>
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml
deleted file mode 100644
index bd52901..0000000
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-  ~ Copyright (C) 2018 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>
-    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
-    <dimen name="quick_qs_offset_height">28dp</dimen>
-    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
-    <dimen name="quick_qs_total_height">156dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
index 5fb8b9e..78cc7e0 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
@@ -47,14 +47,6 @@
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height_portrait">48dp</dimen>
-    <dimen name="status_bar_height_landscape">28dp</dimen>
-    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
-    <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
-
 </resources>
 
 
diff --git a/packages/overlays/NoCutoutOverlay/res/values/config.xml b/packages/overlays/NoCutoutOverlay/res/values/config.xml
index 9157699..84b91b8 100644
--- a/packages/overlays/NoCutoutOverlay/res/values/config.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values/config.xml
@@ -25,12 +25,4 @@
          by shrinking the display such that it does not overlap the cutout area. -->
     <bool name="config_maskMainBuiltInDisplayCutout">true</bool>
 
-    <!-- Height of the status bar -->
-    <dimen name="status_bar_height_portrait">28dp</dimen>
-    <dimen name="status_bar_height_landscape">28dp</dimen>
-
-    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
-    <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
 </resources>
diff --git a/proto/src/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
similarity index 97%
rename from proto/src/critical_event_log.proto
rename to proto/src/criticalevents/critical_event_log.proto
index cb05a71..27787c2 100644
--- a/proto/src/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -15,7 +15,7 @@
  */
 
 syntax = "proto2";
-package com.android.server.am;
+package com.android.server.criticalevents;
 
 option java_multiple_files = true;
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index aff7eb2..8205d35 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2575,7 +2575,8 @@
         final boolean connect = (userState.isShortcutMagnificationEnabledLocked()
                 || userState.isDisplayMagnificationEnabledLocked())
                 && (userState.getMagnificationCapabilitiesLocked()
-                != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+                != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
+                || userHasMagnificationServicesLocked(userState);
         getWindowMagnificationMgr().requestConnection(connect);
     }
 
@@ -3564,7 +3565,12 @@
             }
         }
 
-        ArrayList<Display> getValidDisplayList() {
+        /**
+         * Gets all currently valid logical displays.
+         *
+         * @return An array list containing all valid logical displays.
+         */
+        public ArrayList<Display> getValidDisplayList() {
             synchronized (mLock) {
                 return mDisplaysList;
             }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 7a38a02..a48172b 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,16 +17,16 @@
 
 package com.android.server.companion;
 
-import static android.Manifest.permission.BIND_COMPANION_DEVICE_SERVICE;
 import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
 import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
-import static android.content.Context.BIND_IMPORTANT;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
+import static android.companion.DeviceId.TYPE_MAC_ADDRESS;
 import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
-import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static com.android.internal.util.CollectionUtils.add;
 import static com.android.internal.util.CollectionUtils.any;
-import static com.android.internal.util.CollectionUtils.emptyIfNull;
 import static com.android.internal.util.CollectionUtils.filter;
 import static com.android.internal.util.CollectionUtils.find;
 import static com.android.internal.util.CollectionUtils.forEach;
@@ -38,6 +38,9 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
+import static java.util.Collections.emptySet;
+import static java.util.Collections.unmodifiableMap;
+import static java.util.Collections.unmodifiableSet;
 import static java.util.Objects.requireNonNull;
 import static java.util.concurrent.TimeUnit.MINUTES;
 
@@ -46,6 +49,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
@@ -61,11 +65,10 @@
 import android.companion.Association;
 import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
-import android.companion.CompanionDeviceService;
+import android.companion.DeviceId;
 import android.companion.DeviceNotAssociatedException;
 import android.companion.ICompanionDeviceDiscoveryService;
 import android.companion.ICompanionDeviceManager;
-import android.companion.ICompanionDeviceService;
 import android.companion.IFindDeviceCallback;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -78,7 +81,6 @@
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.net.NetworkPolicyManager;
@@ -94,20 +96,17 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ShellCallback;
-import android.os.ShellCommand;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.permission.PermissionControllerManager;
 import android.text.BidiFormatter;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.ExceptionUtils;
-import android.util.Log;
 import android.util.PackageUtils;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.Xml;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
@@ -127,45 +126,43 @@
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TimeZone;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 /** @hide */
 @SuppressLint("LongLogTag")
 public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient {
+    static final String LOG_TAG = "CompanionDeviceManagerService";
+    static final boolean DEBUG = false;
 
     private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION;
     static {
         final Map<String, String> map = new ArrayMap<>();
-        map.put(AssociationRequest.DEVICE_PROFILE_WATCH,
-                Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
+        map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
+        map.put(DEVICE_PROFILE_APP_STREAMING,
+                Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING);
 
-        DEVICE_PROFILE_TO_PERMISSION = Collections.unmodifiableMap(map);
+        DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
     }
 
+    /** Range of Association IDs allocated for a user.*/
+    static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000;
+
     private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
             CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
             ".CompanionDeviceDiscoveryService");
@@ -173,10 +170,7 @@
     private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
     private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
 
-    private static final long DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS = 10 * 1000;
-
-    private static final boolean DEBUG = false;
-    private static final String LOG_TAG = "CompanionDeviceManagerService";
+    static final long DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS = 10 * 1000;
 
     private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
 
@@ -186,27 +180,16 @@
     private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5;
     private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min;
 
-    private static final String XML_TAG_ASSOCIATIONS = "associations";
-    private static final String XML_TAG_ASSOCIATION = "association";
-    private static final String XML_ATTR_PACKAGE = "package";
-    private static final String XML_ATTR_DEVICE = "device";
-    private static final String XML_ATTR_PROFILE = "profile";
-    private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
-    private static final String XML_ATTR_TIME_APPROVED = "time_approved";
-    private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
-
     private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     static {
         sDateFormat.setTimeZone(TimeZone.getDefault());
     }
 
     private final CompanionDeviceManagerImpl mImpl;
-    private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
+    // Persistent data store for all Associations.
+    private final PersistentDataStore mPersistentDataStore;
     private PowerWhitelistManager mPowerWhitelistManager;
     private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
-    /** userId -> packageName -> serviceConnector */
-    private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>>
-            mDeviceListenerServiceConnectors;
     private IAppOpsService mAppOpsManager;
     private RoleManager mRoleManager;
     private BluetoothAdapter mBluetoothAdapter;
@@ -231,10 +214,19 @@
 
     private final Object mLock = new Object();
     private final Handler mMainHandler = Handler.getMain();
+    private CompanionDevicePresenceController mCompanionDevicePresenceController;
 
-    /** userId -> [association] */
+    /** Maps a {@link UserIdInt} to a set of associations for the user. */
     @GuardedBy("mLock")
-    private @Nullable SparseArray<Set<Association>> mCachedAssociations = new SparseArray<>();
+    private final SparseArray<Set<Association>> mCachedAssociations = new SparseArray<>();
+    /**
+     * A structure that consist of two nested maps, and effectively maps (userId + packageName) to
+     * a list of IDs that have been previously assigned to associations for that package.
+     * We maintain this structure so that we never re-use association IDs for the same package
+     * (until it's uninstalled).
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<Map<String, Set<Integer>>> mPreviouslyUsedIds = new SparseArray<>();
 
     ActivityTaskManagerInternal mAtmInternal;
     ActivityManagerInternal mAmInternal;
@@ -243,6 +235,8 @@
     public CompanionDeviceManagerService(Context context) {
         super(context);
         mImpl = new CompanionDeviceManagerImpl();
+        mPersistentDataStore = new PersistentDataStore();
+
         mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
         mRoleManager = context.getSystemService(RoleManager.class);
         mAppOpsManager = IAppOpsService.Stub.asInterface(
@@ -253,6 +247,7 @@
         mPermissionControllerManager = requireNonNull(
                 context.getSystemService(PermissionControllerManager.class));
         mUserManager = context.getSystemService(UserManager.class);
+        mCompanionDevicePresenceController = new CompanionDevicePresenceController();
 
         Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
         mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -265,16 +260,6 @@
             }
         };
 
-        mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String,
-                ServiceConnector<ICompanionDeviceService>>>() {
-            @NonNull
-            @Override
-            protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create(
-                    int userId) {
-                return new ArrayMap<>();
-            }
-        };
-
         registerPackageMonitor();
     }
 
@@ -286,11 +271,11 @@
                         + ", uid = " + uid + ")");
                 int userId = getChangingUserId();
                 updateAssociations(
-                        as -> CollectionUtils.filter(as,
-                                a -> !Objects.equals(a.getPackageName(), packageName)),
+                        set -> filterOut(set, it -> it.belongsToPackage(userId, packageName)),
                         userId);
 
-                unbindDevicePresenceListener(packageName, userId);
+                mCompanionDevicePresenceController.unbindDevicePresenceListener(
+                        packageName, userId);
             }
 
             @Override
@@ -305,15 +290,6 @@
         }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
     }
 
-    private void unbindDevicePresenceListener(String packageName, int userId) {
-        ServiceConnector<ICompanionDeviceService> deviceListener =
-                mDeviceListenerServiceConnectors.forUser(userId)
-                        .remove(packageName);
-        if (deviceListener != null) {
-            deviceListener.unbind();
-        }
-    }
-
     @Override
     public void onStart() {
         publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
@@ -469,9 +445,8 @@
 
             }, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> {
                 if (err == null) {
-                    Association association = new Association(userId, deviceAddress, callingPackage,
-                            deviceProfile, false, System.currentTimeMillis());
-                    addAssociation(association, userId);
+                    createAssociationInternal(
+                            userId, deviceAddress, callingPackage, deviceProfile);
                 } else {
                     Slog.e(LOG_TAG, "Failed to discover device(s)", err);
                     callback.onFailure("No devices found: " + err.getMessage());
@@ -551,6 +526,12 @@
             // Device profile can be null.
             if (deviceProfile == null) return;
 
+            if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) {
+                // TODO: remove, when properly supporting this profile.
+                throw new UnsupportedOperationException(
+                        "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
+            }
+
             if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
                 throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
             }
@@ -649,7 +630,7 @@
             checkCallerIsSystemOr(packageName);
 
             int userId = getCallingUserId();
-            Set<Association> deviceAssociations = CollectionUtils.filter(
+            Set<Association> deviceAssociations = filter(
                     getAllAssociations(userId, packageName),
                     association -> deviceAddress.equals(association.getDeviceMacAddress()));
 
@@ -662,16 +643,9 @@
             updateAssociations(associations -> map(associations, association -> {
                 if (Objects.equals(association.getPackageName(), packageName)
                         && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
-                    return new Association(
-                            association.getUserId(),
-                            association.getDeviceMacAddress(),
-                            association.getPackageName(),
-                            association.getDeviceProfile(),
-                            active /* notifyOnDeviceNearby */,
-                            association.getTimeApprovedMs());
-                } else {
-                    return association;
+                    association.setNotifyOnDeviceNearby(active);
                 }
+                return association;
             }), userId);
 
             restartBleScan();
@@ -689,9 +663,7 @@
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES, "createAssociation");
 
-            addAssociation(new Association(
-                    userId, macAddress, packageName, null, false,
-                    System.currentTimeMillis()), userId);
+            createAssociationInternal(userId, macAddress, packageName, null);
         }
 
         private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
@@ -722,7 +694,7 @@
         @Override
         public boolean canPairWithoutPrompt(
                 String packageName, String deviceMacAddress, int userId) {
-            return CollectionUtils.any(
+            return any(
                     getAllAssociations(userId, packageName, deviceMacAddress),
                     a -> System.currentTimeMillis() - a.getTimeApprovedMs()
                             < PAIR_WITHOUT_PROMPT_WINDOW_MS);
@@ -732,7 +704,10 @@
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
                 throws RemoteException {
-            new ShellCmd().exec(this, in, out, err, args, callback, resultReceiver);
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.MANAGE_COMPANION_DEVICES, null);
+            new CompanionDeviceShellCommand(CompanionDeviceManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
         }
 
         @Override
@@ -775,11 +750,13 @@
             }
 
             fout.append("Device Listener Services State:").append('\n');
-            for (int i = 0, size = mDeviceListenerServiceConnectors.size(); i < size; i++) {
-                int userId = mDeviceListenerServiceConnectors.keyAt(i);
+            for (int i = 0, size =  mCompanionDevicePresenceController.mBoundServices.size();
+                    i < size; i++) {
+                int userId = mCompanionDevicePresenceController.mBoundServices.keyAt(i);
                 fout.append("  ")
                         .append("u").append(Integer.toString(userId)).append(": ")
-                        .append(Objects.toString(mDeviceListenerServiceConnectors.valueAt(i)))
+                        .append(Objects.toString(
+                                mCompanionDevicePresenceController.mBoundServices.valueAt(i)))
                         .append('\n');
             }
         }
@@ -793,32 +770,91 @@
         return Binder.getCallingUid() == Process.SYSTEM_UID;
     }
 
-    void addAssociation(Association association, int userId) {
+    void createAssociationInternal(
+            int userId, String deviceMacAddress, String packageName, String deviceProfile) {
+        final Association association = new Association(
+                getNewAssociationIdForPackage(userId, packageName),
+                userId,
+                packageName,
+                Arrays.asList(new DeviceId(TYPE_MAC_ADDRESS, deviceMacAddress)),
+                deviceProfile,
+                /* managedByCompanionApp */false,
+                /* notifyOnDeviceNearby */ false ,
+                System.currentTimeMillis());
+
         updateSpecialAccessPermissionForAssociatedPackage(association);
         recordAssociation(association, userId);
     }
 
-    void removeAssociation(int userId, String pkg, String deviceMacAddress) {
-        updateAssociations(associations -> CollectionUtils.filter(associations, association -> {
-            boolean notMatch = association.getUserId() != userId
-                    || !Objects.equals(association.getDeviceMacAddress(), deviceMacAddress)
-                    || !Objects.equals(association.getPackageName(), pkg);
-            if (!notMatch) {
-                onAssociationPreRemove(association);
+    @GuardedBy("mLock")
+    @NonNull
+    private Set<Integer> getPreviouslyUsedIdsForPackageLocked(
+            @UserIdInt int userId, @NonNull String packageName) {
+        final Set<Integer> previouslyUsedIds = mPreviouslyUsedIds.get(userId).get(packageName);
+        if (previouslyUsedIds != null) return previouslyUsedIds;
+        return emptySet();
+    }
+
+    private int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) {
+        synchronized (mLock) {
+            readPersistedStateForUserIfNeededLocked(userId);
+
+            // First: collect all IDs currently in use for this user's Associations.
+            final SparseBooleanArray usedIds = new SparseBooleanArray();
+            for (Association it : getAllAssociations(userId)) {
+                usedIds.put(it.getAssociationId(), true);
             }
-            return notMatch;
+
+            // Second: collect all IDs that have been previously used for this package (and user).
+            final Set<Integer> previouslyUsedIds =
+                    getPreviouslyUsedIdsForPackageLocked(userId, packageName);
+
+            int id = getFirstAssociationIdForUser(userId);
+            final int lastAvailableIdForUser = getLastAssociationIdForUser(userId);
+
+            // Find first ID that isn't used now AND has never been used for the given package.
+            while (usedIds.get(id) || previouslyUsedIds.contains(id)) {
+                // Increment and try again
+                id++;
+                // ... but first check if the ID is valid (within the range allocated to the user).
+                if (id > lastAvailableIdForUser) {
+                    throw new RuntimeException("Cannot create a new Association ID for "
+                            + packageName + " for user " + userId);
+                }
+            }
+
+            return id;
+        }
+    }
+
+    void removeAssociation(int userId, String packageName, String deviceMacAddress) {
+        updateAssociations(associations -> filterOut(associations, it -> {
+            final boolean match = it.belongsToPackage(userId, packageName)
+                    && Objects.equals(it.getDeviceMacAddress(), deviceMacAddress);
+            if (match) {
+                onAssociationPreRemove(it);
+                markIdAsPreviouslyUsedForPackage(it.getAssociationId(), userId, packageName);
+            }
+            return match;
         }), userId);
         restartBleScan();
     }
 
+    private void markIdAsPreviouslyUsedForPackage(
+            int associationId, @UserIdInt int userId, @NonNull String packageName) {
+        synchronized (mLock) {
+            // Mark as previously used.
+            readPersistedStateForUserIfNeededLocked(userId);
+            mPreviouslyUsedIds.get(userId)
+                    .computeIfAbsent(packageName, it -> new HashSet<>())
+                    .add(associationId);
+        }
+    }
+
     void onAssociationPreRemove(Association association) {
         if (association.isNotifyOnDeviceNearby()) {
-            ServiceConnector<ICompanionDeviceService> serviceConnector =
-                    mDeviceListenerServiceConnectors.forUser(association.getUserId())
-                            .get(association.getPackageName());
-            if (serviceConnector != null) {
-                serviceConnector.unbind();
-            }
+            mCompanionDevicePresenceController.unbindDevicePresenceListener(
+                    association.getPackageName(), association.getUserId());
         }
 
         String deviceProfile = association.getDeviceProfile();
@@ -1001,22 +1037,31 @@
 
     private void recordAssociation(Association association, int userId) {
         Slog.i(LOG_TAG, "recordAssociation(" + association + ")");
-        updateAssociations(associations -> CollectionUtils.add(associations, association), userId);
+        updateAssociations(associations -> add(associations, association), userId);
     }
 
     private void updateAssociations(Function<Set<Association>, Set<Association>> update,
             int userId) {
         synchronized (mLock) {
-            final Set<Association> old = getAllAssociations(userId);
-            Set<Association> associations = new ArraySet<>(old);
-            associations = update.apply(associations);
-            Slog.i(LOG_TAG, "Updating associations: " + old + "  -->  " + associations);
-            mCachedAssociations.put(userId, Collections.unmodifiableSet(associations));
-            BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
-                    CompanionDeviceManagerService::persistAssociations,
-                    this, associations, userId));
+            if (DEBUG) Slog.d(LOG_TAG, "Updating Associations set...");
 
-            updateAtm(userId, associations);
+            final Set<Association> prevAssociations = getAllAssociations(userId);
+            if (DEBUG) Slog.d(LOG_TAG, "  > Before : " + prevAssociations + "...");
+
+            final Set<Association> updatedAssociations = update.apply(
+                    new ArraySet<>(prevAssociations));
+            if (DEBUG) Slog.d(LOG_TAG, "  > After: " + updatedAssociations);
+
+            mCachedAssociations.put(userId, unmodifiableSet(updatedAssociations));
+
+            BackgroundThread.getHandler().sendMessage(
+                    PooledLambda.obtainMessage(
+                            (associations, usedIds) ->
+                                    mPersistentDataStore
+                                            .persistStateForUser(userId, associations, usedIds),
+                            updatedAssociations, deepCopy(mPreviouslyUsedIds.get(userId))));
+
+            updateAtm(userId, updatedAssociations);
         }
     }
 
@@ -1038,64 +1083,34 @@
         }
     }
 
-    private void persistAssociations(Set<Association> associations, int userId) {
-        Slog.i(LOG_TAG, "Writing associations to disk: " + associations);
-        final AtomicFile file = getStorageFileForUser(userId);
-        synchronized (file) {
-            file.write(out -> {
-                XmlSerializer xml = Xml.newSerializer();
-                try {
-                    xml.setOutput(out, StandardCharsets.UTF_8.name());
-                    xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-                    xml.startDocument(null, true);
-                    xml.startTag(null, XML_TAG_ASSOCIATIONS);
-
-                    forEach(associations, association -> {
-                        XmlSerializer tag = xml.startTag(null, XML_TAG_ASSOCIATION)
-                                .attribute(null, XML_ATTR_PACKAGE, association.getPackageName())
-                                .attribute(null, XML_ATTR_DEVICE,
-                                        association.getDeviceMacAddress());
-                        if (association.getDeviceProfile() != null) {
-                            tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
-                            tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY,
-                                    Boolean.toString(
-                                            association.isNotifyOnDeviceNearby()));
-                        }
-                        tag.attribute(null, XML_ATTR_TIME_APPROVED,
-                                Long.toString(association.getTimeApprovedMs()));
-                        tag.endTag(null, XML_TAG_ASSOCIATION);
-                    });
-
-                    xml.endTag(null, XML_TAG_ASSOCIATIONS);
-                    xml.endDocument();
-                } catch (Exception e) {
-                    Slog.e(LOG_TAG, "Error while writing associations file", e);
-                    throw ExceptionUtils.propagate(e);
-                }
-            });
-        }
-    }
-
-    private AtomicFile getStorageFileForUser(int userId) {
-        return mUidToStorage.computeIfAbsent(userId, (u) ->
-                new AtomicFile(new File(
-                        //TODO deprecated method - what's the right replacement?
-                        Environment.getUserSystemDirectory(u),
-                        XML_FILE_NAME)));
-    }
-
-    @Nullable
-    private Set<Association> getAllAssociations(int userId) {
+    @NonNull Set<Association> getAllAssociations(int userId) {
         synchronized (mLock) {
-            if (mCachedAssociations.get(userId) == null) {
-                mCachedAssociations.put(userId, Collections.unmodifiableSet(
-                        emptyIfNull(readAllAssociations(userId))));
-                Slog.i(LOG_TAG, "Read associations from disk: " + mCachedAssociations);
-            }
+            readPersistedStateForUserIfNeededLocked(userId);
+            // This returns non-null, because the readAssociationsInfoForUserIfNeededLocked() method
+            // we just called adds an empty set, if there was no previously saved data.
             return mCachedAssociations.get(userId);
         }
     }
 
+    @GuardedBy("mLock")
+    private void readPersistedStateForUserIfNeededLocked(@UserIdInt int userId) {
+        if (mCachedAssociations.get(userId) != null) return;
+
+        Slog.i(LOG_TAG, "Reading state for user " + userId + "  from the disk");
+
+        final Set<Association> associations = new ArraySet<>();
+        final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>();
+        mPersistentDataStore.readStateForUser(userId, associations, previouslyUsedIds);
+
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "  > associations=" + associations + "\n"
+                    + "  > previouslyUsedIds=" + previouslyUsedIds);
+        }
+
+        mCachedAssociations.put(userId, unmodifiableSet(associations));
+        mPreviouslyUsedIds.append(userId, previouslyUsedIds);
+    }
+
     private List<UserInfo> getAllUsers() {
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -1106,7 +1121,7 @@
     }
 
     private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
-        return CollectionUtils.filter(
+        return filter(
                 getAllAssociations(userId),
                 // Null filter == get all associations
                 a -> packageFilter == null || Objects.equals(packageFilter, a.getPackageName()));
@@ -1125,10 +1140,9 @@
         }
     }
 
-
     private Set<Association> getAllAssociations(
             int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
-        return CollectionUtils.filter(
+        return filter(
                 getAllAssociations(userId),
                 // Null filter == get all associations
                 a -> (packageFilter == null || Objects.equals(packageFilter, a.getPackageName()))
@@ -1136,44 +1150,6 @@
                                 || Objects.equals(addressFilter, a.getDeviceMacAddress())));
     }
 
-    private Set<Association> readAllAssociations(int userId) {
-        final AtomicFile file = getStorageFileForUser(userId);
-
-        if (!file.getBaseFile().exists()) return null;
-
-        ArraySet<Association> result = null;
-        final XmlPullParser parser = Xml.newPullParser();
-        synchronized (file) {
-            try (FileInputStream in = file.openRead()) {
-                parser.setInput(in, StandardCharsets.UTF_8.name());
-                int type;
-                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                    if (type != XmlPullParser.START_TAG
-                            && !XML_TAG_ASSOCIATIONS.equals(parser.getName())) continue;
-
-                    final String appPackage = parser.getAttributeValue(null, XML_ATTR_PACKAGE);
-                    final String deviceAddress = parser.getAttributeValue(null, XML_ATTR_DEVICE);
-
-                    final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
-                    final boolean persistentGrants = Boolean.valueOf(
-                            parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY));
-                    final long timeApproved = parseLongOrDefault(
-                            parser.getAttributeValue(null, XML_ATTR_TIME_APPROVED), 0L);
-
-                    if (appPackage == null || deviceAddress == null) continue;
-
-                    result = ArrayUtils.add(result,
-                            new Association(userId, deviceAddress, appPackage,
-                                    profile, persistentGrants, timeApproved));
-                }
-                return result;
-            } catch (XmlPullParserException | IOException e) {
-                Slog.e(LOG_TAG, "Error while reading associations file", e);
-                return null;
-            }
-        }
-    }
-
     void onDeviceConnected(String address) {
         Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
 
@@ -1233,55 +1209,6 @@
                 >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS;
     }
 
-    private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector(
-            Association a) {
-        return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
-                a.getPackageName(),
-                pkg -> createDeviceListenerServiceConnector(a));
-    }
-
-    private ServiceConnector<ICompanionDeviceService> createDeviceListenerServiceConnector(
-            Association a) {
-        List<ResolveInfo> resolveInfos = getContext().getPackageManager().queryIntentServicesAsUser(
-                new Intent(CompanionDeviceService.SERVICE_INTERFACE), MATCH_ALL, a.getUserId());
-        List<ResolveInfo> packageResolveInfos = filter(resolveInfos,
-                info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName()));
-        if (packageResolveInfos.size() != 1) {
-            Slog.w(LOG_TAG, "Device presence listener package must have exactly one "
-                    + "CompanionDeviceService, but " + a.getPackageName()
-                    + " has " + packageResolveInfos.size());
-            return new ServiceConnector.NoOp<>();
-        }
-        String servicePermission = packageResolveInfos.get(0).serviceInfo.permission;
-        if (!BIND_COMPANION_DEVICE_SERVICE.equals(servicePermission)) {
-            Slog.w(LOG_TAG, "Binding CompanionDeviceService must have "
-                    + BIND_COMPANION_DEVICE_SERVICE + " permission.");
-            return new ServiceConnector.NoOp<>();
-        }
-        ComponentName componentName = packageResolveInfos.get(0).serviceInfo.getComponentName();
-        Slog.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
-        return new ServiceConnector.Impl<ICompanionDeviceService>(getContext(),
-                new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(componentName),
-                BIND_IMPORTANT,
-                a.getUserId(),
-                ICompanionDeviceService.Stub::asInterface) {
-
-            @Override
-            protected long getAutoDisconnectTimeoutMs() {
-                // Service binding is managed manually based on corresponding device being nearby
-                return Long.MAX_VALUE;
-            }
-
-            @Override
-            public void binderDied() {
-                super.binderDied();
-
-                // Re-connect to the service if process gets killed
-                mMainHandler.postDelayed(this::connect, DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
-            }
-        };
-    }
-
     private class BleScanCallback extends ScanCallback {
         @Override
         public void onScanResult(int callbackType, ScanResult result) {
@@ -1345,8 +1272,6 @@
 
         @Override
         public void run() {
-            Slog.i(LOG_TAG, "UnbindDeviceListenersRunnable.run(); devicesNearby = "
-                    + mDevicesLastNearby);
             int size = mDevicesLastNearby.size();
             for (int i = 0; i < size; i++) {
                 String address = mDevicesLastNearby.keyAt(i);
@@ -1355,7 +1280,8 @@
                 if (isDeviceDisappeared(lastNearby)) {
                     for (Association association : getAllAssociations(address)) {
                         if (association.isNotifyOnDeviceNearby()) {
-                            getDeviceListenerServiceConnector(association).unbind();
+                            mCompanionDevicePresenceController.unbindDevicePresenceListener(
+                                    association.getPackageName(), association.getUserId());
                         }
                     }
                 }
@@ -1425,10 +1351,8 @@
             Slog.i(LOG_TAG, "onDeviceNearby(justAppeared, address = " + address + ")");
             for (Association association : getAllAssociations(address)) {
                 if (association.isNotifyOnDeviceNearby()) {
-                    Slog.i(LOG_TAG,
-                            "Sending onDeviceAppeared to " + association.getPackageName() + ")");
-                    getDeviceListenerServiceConnector(association).run(
-                            service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
+                    mCompanionDevicePresenceController.onDeviceNotifyAppeared(association,
+                            getContext(), mMainHandler);
                 }
             }
         }
@@ -1440,10 +1364,8 @@
         boolean hasDeviceListeners = false;
         for (Association association : getAllAssociations(address)) {
             if (association.isNotifyOnDeviceNearby()) {
-                Slog.i(LOG_TAG,
-                        "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
-                getDeviceListenerServiceConnector(association).run(
-                        service -> service.onDeviceDisappeared(address));
+                mCompanionDevicePresenceController.onDeviceNotifyDisappeared(
+                        association, getContext(), mMainHandler);
                 hasDeviceListeners = true;
             }
         }
@@ -1541,86 +1463,15 @@
         return result;
     }
 
-    private static long parseLongOrDefault(String str, long def) {
-        try {
-            return Long.parseLong(str);
-        } catch (NumberFormatException e) {
-            Slog.w(LOG_TAG, "Failed to parse", e);
-            return def;
-        }
+    static int getFirstAssociationIdForUser(@UserIdInt int userId) {
+        // We want the IDs to start from 1, not 0.
+        return userId * ASSOCIATIONS_IDS_PER_USER_RANGE + 1;
     }
 
-    private class ShellCmd extends ShellCommand {
-        public static final String USAGE = "help\n"
-                + "list USER_ID\n"
-                + "associate USER_ID PACKAGE MAC_ADDRESS\n"
-                + "disassociate USER_ID PACKAGE MAC_ADDRESS";
-
-        ShellCmd() {
-            getContext().enforceCallingOrSelfPermission(
-                    android.Manifest.permission.MANAGE_COMPANION_DEVICES, "ShellCmd");
-        }
-
-        @Override
-        public int onCommand(String cmd) {
-            try {
-                switch (cmd) {
-                    case "list": {
-                        forEach(
-                                getAllAssociations(getNextArgInt()),
-                                a -> getOutPrintWriter()
-                                        .println(a.getPackageName() + " "
-                                                + a.getDeviceMacAddress()));
-                    }
-                    break;
-
-                    case "associate": {
-                        int userId = getNextArgInt();
-                        String pkg = getNextArgRequired();
-                        String address = getNextArgRequired();
-                        addAssociation(new Association(userId, address, pkg, null, false,
-                                System.currentTimeMillis()), userId);
-                    }
-                    break;
-
-                    case "disassociate": {
-                        removeAssociation(getNextArgInt(), getNextArgRequired(),
-                                getNextArgRequired());
-                    }
-                    break;
-
-                    case "simulate_connect": {
-                        onDeviceConnected(getNextArgRequired());
-                    }
-                    break;
-
-                    case "simulate_disconnect": {
-                        onDeviceDisconnected(getNextArgRequired());
-                    }
-                    break;
-
-                    default:
-                        return handleDefaultCommands(cmd);
-                }
-                return 0;
-            } catch (Throwable t) {
-                Slog.e(LOG_TAG, "Error running a command: $ " + cmd, t);
-                getErrPrintWriter().println(Log.getStackTraceString(t));
-                return 1;
-            }
-        }
-
-        private int getNextArgInt() {
-            return Integer.parseInt(getNextArgRequired());
-        }
-
-        @Override
-        public void onHelp() {
-            getOutPrintWriter().println(USAGE);
-        }
+    static int getLastAssociationIdForUser(@UserIdInt int userId) {
+        return (userId + 1) * ASSOCIATIONS_IDS_PER_USER_RANGE;
     }
 
-
     private class BluetoothDeviceConnectedListener
             extends BluetoothAdapter.BluetoothConnectionCallback {
         @Override
@@ -1635,4 +1486,15 @@
             CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress());
         }
     }
+
+    private static @NonNull <T> Set<T> filterOut(
+            @NonNull Set<T> set, @NonNull Predicate<? super T> predicate) {
+        return CollectionUtils.filter(set, predicate.negate());
+    }
+
+    private Map<String, Set<Integer>> deepCopy(Map<String, Set<Integer>> orig) {
+        final Map<String, Set<Integer>> copy = new HashMap<>(orig.size(), 1f);
+        forEach(orig, (key, value) -> copy.put(key, new ArraySet<>(value)));
+        return copy;
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
new file mode 100644
index 0000000..328a8b3
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
@@ -0,0 +1,215 @@
+/*
+ * 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.server.companion;
+
+import static android.Manifest.permission.BIND_COMPANION_DEVICE_SERVICE;
+import static android.content.Context.BIND_IMPORTANT;
+
+import static com.android.internal.util.CollectionUtils.filter;
+
+import android.annotation.NonNull;
+import android.companion.Association;
+import android.companion.CompanionDeviceService;
+import android.companion.ICompanionDeviceService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.infra.PerUser;
+import com.android.internal.infra.ServiceConnector;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This class creates/removes {@link ServiceConnector}s between {@link CompanionDeviceService} and
+ * the companion apps. The controller also will notify the companion apps with device status.
+ */
+public class CompanionDevicePresenceController {
+    private static final String LOG_TAG = "CompanionDevicePresenceController";
+    PerUser<ArrayMap<String, List<BoundService>>> mBoundServices;
+    private static final String META_DATA_KEY_PRIMARY = "primary";
+
+    public CompanionDevicePresenceController() {
+        mBoundServices = new PerUser<ArrayMap<String, List<BoundService>>>() {
+            @NonNull
+            @Override
+            protected ArrayMap<String, List<BoundService>> create(int userId) {
+                return new ArrayMap<>();
+            }
+        };
+    }
+
+    void onDeviceNotifyAppeared(Association association, Context context, Handler handler) {
+        ServiceConnector<ICompanionDeviceService> primaryConnector =
+                getPrimaryServiceConnector(association, context, handler);
+        if (primaryConnector != null) {
+            Slog.i(LOG_TAG,
+                    "Sending onDeviceAppeared to " + association.getPackageName() + ")");
+            primaryConnector.run(
+                    service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
+        }
+    }
+
+    void onDeviceNotifyDisappeared(Association association, Context context, Handler handler) {
+        ServiceConnector<ICompanionDeviceService> primaryConnector =
+                getPrimaryServiceConnector(association, context, handler);
+        if (primaryConnector != null) {
+            Slog.i(LOG_TAG,
+                    "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
+            primaryConnector.run(
+                    service -> service.onDeviceDisappeared(association.getDeviceMacAddress()));
+        }
+    }
+
+    void unbindDevicePresenceListener(String packageName, int userId) {
+        List<BoundService> boundServices = mBoundServices.forUser(userId)
+                .remove(packageName);
+        if (boundServices != null) {
+            for (BoundService boundService: boundServices) {
+                Slog.d(LOG_TAG, "Unbinding the serviceConnector: " + boundService.mComponentName);
+                boundService.mServiceConnector.unbind();
+            }
+        }
+    }
+
+    private ServiceConnector<ICompanionDeviceService> getPrimaryServiceConnector(
+            Association association, Context context, Handler handler) {
+        for (BoundService boundService: getDeviceListenerServiceConnector(association, context,
+                handler)) {
+            if (boundService.mIsPrimary) {
+                return boundService.mServiceConnector;
+            }
+        }
+        return null;
+    }
+
+    private List<BoundService> getDeviceListenerServiceConnector(Association a, Context context,
+            Handler handler) {
+        return mBoundServices.forUser(a.getUserId()).computeIfAbsent(
+                a.getPackageName(),
+                pkg -> createDeviceListenerServiceConnector(a, context, handler));
+    }
+
+    private List<BoundService> createDeviceListenerServiceConnector(Association a, Context context,
+            Handler handler) {
+        List<ResolveInfo> resolveInfos = context
+                .getPackageManager()
+                .queryIntentServicesAsUser(new Intent(CompanionDeviceService.SERVICE_INTERFACE),
+                        PackageManager.GET_META_DATA, a.getUserId());
+        List<ResolveInfo> packageResolveInfos = filter(resolveInfos,
+                info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName()));
+        List<BoundService> serviceConnectors = new ArrayList<>();
+        if (!validatePackageInfo(packageResolveInfos, a)) {
+            return serviceConnectors;
+        }
+        for (ResolveInfo packageResolveInfo : packageResolveInfos) {
+            boolean isPrimary = (packageResolveInfo.serviceInfo.metaData != null
+                    && packageResolveInfo.serviceInfo.metaData.getBoolean(META_DATA_KEY_PRIMARY))
+                    || packageResolveInfos.size() == 1;
+            ComponentName componentName = packageResolveInfo.serviceInfo.getComponentName();
+
+            Slog.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
+
+            ServiceConnector<ICompanionDeviceService> serviceConnector =
+                    new ServiceConnector.Impl<ICompanionDeviceService>(context,
+                            new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(
+                                    componentName), BIND_IMPORTANT, a.getUserId(),
+                            ICompanionDeviceService.Stub::asInterface) {
+                        @Override
+                        protected long getAutoDisconnectTimeoutMs() {
+                            // Service binding is managed manually based on corresponding device
+                            // being nearby
+                            return Long.MAX_VALUE;
+                        }
+
+                        @Override
+                        public void binderDied() {
+                            super.binderDied();
+
+                            // Re-connect to the service if process gets killed
+                            handler.postDelayed(
+                                    this::connect,
+                                    CompanionDeviceManagerService
+                                            .DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
+                        }
+                    };
+
+            serviceConnectors.add(new BoundService(componentName, isPrimary, serviceConnector));
+        }
+        return serviceConnectors;
+    }
+
+    private boolean validatePackageInfo(List<ResolveInfo> packageResolveInfos,
+            Association association) {
+        if (packageResolveInfos.size() == 0 || packageResolveInfos.size() > 5) {
+            Slog.e(LOG_TAG, "Device presence listener package must have at least one and not "
+                    + "more than five CompanionDeviceService(s) declared. But "
+                    + association.getPackageName()
+                    + " has " + packageResolveInfos.size());
+            return false;
+        }
+
+        int primaryCount = 0;
+        for (ResolveInfo packageResolveInfo : packageResolveInfos) {
+            String servicePermission = packageResolveInfo.serviceInfo.permission;
+            if (!BIND_COMPANION_DEVICE_SERVICE.equals(servicePermission)) {
+                Slog.e(LOG_TAG, "Binding CompanionDeviceService must have "
+                        + BIND_COMPANION_DEVICE_SERVICE + " permission.");
+                return false;
+            }
+
+            if (packageResolveInfo.serviceInfo.metaData != null
+                    && packageResolveInfo.serviceInfo.metaData.getBoolean(META_DATA_KEY_PRIMARY)) {
+                primaryCount++;
+                if (primaryCount > 1) {
+                    Slog.e(LOG_TAG, "Must have exactly one primary CompanionDeviceService "
+                            + "to be bound but "
+                            + association.getPackageName() + "has " + primaryCount);
+                    return false;
+                }
+            }
+        }
+
+        if (packageResolveInfos.size() == 1 && primaryCount != 0) {
+            Slog.w(LOG_TAG, "Do not need the primary metadata if there's only one"
+                    + " CompanionDeviceService " + "but " + association.getPackageName()
+                    + " has " + primaryCount);
+        }
+
+        return true;
+    }
+
+    private static class BoundService {
+        private final ComponentName mComponentName;
+        private final boolean mIsPrimary;
+        private final ServiceConnector<ICompanionDeviceService> mServiceConnector;
+
+        BoundService(ComponentName componentName,
+                boolean isPrimary,  ServiceConnector<ICompanionDeviceService> serviceConnector) {
+            this.mComponentName = componentName;
+            this.mIsPrimary = isPrimary;
+            this.mServiceConnector = serviceConnector;
+        }
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
new file mode 100644
index 0000000..e143f5e
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -0,0 +1,99 @@
+/*
+ * 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.server.companion;
+
+import static com.android.internal.util.CollectionUtils.forEach;
+import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
+
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+
+class CompanionDeviceShellCommand extends android.os.ShellCommand {
+    private final CompanionDeviceManagerService mService;
+
+    CompanionDeviceShellCommand(CompanionDeviceManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        try {
+            switch (cmd) {
+                case "list": {
+                    forEach(
+                            mService.getAllAssociations(getNextArgInt()),
+                            a -> getOutPrintWriter()
+                                    .println(a.getPackageName() + " "
+                                            + a.getDeviceMacAddress()));
+                }
+                break;
+
+                case "associate": {
+                    int userId = getNextArgInt();
+                    String packageName = getNextArgRequired();
+                    String address = getNextArgRequired();
+                    mService.createAssociationInternal(userId, address, packageName, null);
+                }
+                break;
+
+                case "disassociate": {
+                    mService.removeAssociation(getNextArgInt(), getNextArgRequired(),
+                            getNextArgRequired());
+                }
+                break;
+
+                case "simulate_connect": {
+                    mService.onDeviceConnected(getNextArgRequired());
+                }
+                break;
+
+                case "simulate_disconnect": {
+                    mService.onDeviceDisconnected(getNextArgRequired());
+                }
+                break;
+
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+            return 0;
+        } catch (Throwable t) {
+            Slog.e(LOG_TAG, "Error running a command: $ " + cmd, t);
+            getErrPrintWriter().println(Log.getStackTraceString(t));
+            return 1;
+        }
+    }
+
+    private int getNextArgInt() {
+        return Integer.parseInt(getNextArgRequired());
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Companion Device Manager (companiondevice) commands:");
+        pw.println("  help");
+        pw.println("      Print this help text.");
+        pw.println("  list USER_ID");
+        pw.println("      List all Associations for a user.");
+        pw.println("  associate USER_ID PACKAGE MAC_ADDRESS");
+        pw.println("      Create a new Association.");
+        pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
+        pw.println("      Remove an existing Association.");
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
new file mode 100644
index 0000000..73d45ad
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -0,0 +1,512 @@
+/*
+ * 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.server.companion;
+
+import static android.companion.DeviceId.TYPE_MAC_ADDRESS;
+
+import static com.android.internal.util.CollectionUtils.forEach;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
+import static com.android.internal.util.XmlUtils.writeStringAttribute;
+
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.Association;
+import android.companion.DeviceId;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * The class responsible for persisting Association records and other related information (such as
+ * previously used IDs) to a disk, and reading the data back from the disk.
+ *
+ * Before Android T the data was stored to `companion_device_manager_associations.xml` file in
+ * {@link Environment#getUserSystemDirectory(int)}
+ * (eg. `/data/system/users/0/companion_device_manager_associations.xml`)
+ * @see #getBaseLegacyStorageFileForUser(int)
+ *
+ * Before Android T the data was stored using the v0 schema.
+ *
+ * @see #readAssociationsV0(TypedXmlPullParser, int, Set)
+ * @see #readAssociationV0(TypedXmlPullParser, int, int, Set)
+ *
+ * The following snippet is a sample of a the file that is using v0 schema.
+ * <pre>{@code
+ * <associations>
+ *   <association
+ *     package="com.sample.companion.app"
+ *     device="AA:BB:CC:DD:EE:00"
+ *     time_approved="1634389553216" />
+ *   <association
+ *     package="com.another.sample.companion.app"
+ *     device="AA:BB:CC:DD:EE:01"
+ *     profile="android.app.role.COMPANION_DEVICE_WATCH"
+ *     notify_device_nearby="false"
+ *     time_approved="1634389752662" />
+ * </associations>
+ * }</pre>
+ *
+ *
+ * Since Android T the data is stored to `companion_device_manager.xml` file in
+ * {@link Environment#getDataSystemDeDirectory(int)}.
+ * (eg. `/data/system_de/0/companion_device_manager.xml`)
+ * @see #getBaseStorageFileForUser(int)
+
+ * Since Android T the data is stored using the v1 schema.
+ * In the v1 schema, a list of the previously used IDs is storead along with the association
+ * records.
+ * In the v1 schema, we no longer store MAC addresses, instead each assocition record may have a
+ * number of DeviceIds.
+ *
+ * @see #CURRENT_PERSISTENCE_VERSION
+ * @see #readAssociationsV1(TypedXmlPullParser, int, Set)
+ * @see #readAssociationV1(TypedXmlPullParser, int, Set)
+ * @see #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map)
+ *
+ * The following snippet is a sample of a the file that is using v0 schema.
+ * <pre>{@code
+ * <state persistence-version="1">
+ *     <associations>
+ *         <association
+ *             id="1"
+ *             package="com.sample.companion.app"
+ *             managed_by_app="false"
+ *             notify_device_nearby="false"
+ *             time_approved="1634389553216">
+ *             <device-id type="mac_address" value="AA:BB:CC:DD:EE:00" />
+ *         </association>
+ *
+ *         <association
+ *             id="3"
+ *             profile="android.app.role.COMPANION_DEVICE_WATCH"
+ *             package="com.sample.companion.another.app"
+ *             managed_by_app="false"
+ *             notify_device_nearby="false"
+ *             time_approved="1634641160229">
+ *             <device-id type="mac_address" value="AA:BB:CC:DD:EE:FF" />
+ *         </association>
+ *     </associations>
+ *
+ *     <previously-used-ids>
+ *         <package package_name="com.sample.companion.app">
+ *             <id>2</id>
+ *         </package>
+ *     </previously-used-ids>
+ * </state>
+ * }</pre>
+ */
+final class PersistentDataStore {
+    private static final String LOG_TAG = CompanionDeviceManagerService.LOG_TAG + ".DataStore";
+    private static final boolean DEBUG = CompanionDeviceManagerService.DEBUG;
+
+    private static final int CURRENT_PERSISTENCE_VERSION = 1;
+
+    private static final String FILE_NAME_LEGACY = "companion_device_manager_associations.xml";
+    private static final String FILE_NAME = "companion_device_manager.xml";
+
+    private static final String XML_TAG_STATE = "state";
+    private static final String XML_TAG_ASSOCIATIONS = "associations";
+    private static final String XML_TAG_ASSOCIATION = "association";
+    private static final String XML_TAG_DEVICE_ID = "device-id";
+    private static final String XML_TAG_PREVIOUSLY_USED_IDS = "previously-used-ids";
+    private static final String XML_TAG_PACKAGE = "package";
+    private static final String XML_TAG_ID = "id";
+
+    private static final String XML_ATTR_PERSISTENCE_VERSION = "persistence-version";
+    private static final String XML_ATTR_ID = "id";
+    // Used in <package> elements, nested within <previously-used-ids> elements.
+    private static final String XML_ATTR_PACKAGE_NAME = "package_name";
+    // Used in <association> elements, nested within <associations> elements.
+    private static final String XML_ATTR_PACKAGE = "package";
+    private static final String XML_ATTR_DEVICE = "device";
+    private static final String XML_ATTR_PROFILE = "profile";
+    private static final String XML_ATTR_MANAGED_BY_APP = "managed_by_app";
+    private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
+    private static final String XML_ATTR_TIME_APPROVED = "time_approved";
+    private static final String XML_ATTR_TYPE = "type";
+    private static final String XML_ATTR_VALUE = "value";
+
+    private final @NonNull ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
+            new ConcurrentHashMap<>();
+
+    /**
+     * Reads previously persisted data for the given user "into" the provided containers.
+     *
+     * @param userId Android UserID
+     * @param associationsOut a container to read the {@link Association}s "into".
+     * @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into".
+     */
+    void readStateForUser(@UserIdInt int userId,
+            @NonNull Set<Association> associationsOut,
+            @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
+        Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
+        final AtomicFile file = getStorageFileForUser(userId);
+        if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
+
+        synchronized (file) {
+            File legacyBaseFile = null;
+            final AtomicFile readFrom;
+            final String rootTag;
+            if (!file.getBaseFile().exists()) {
+                if (DEBUG) Slog.d(LOG_TAG, "  > File does not exist -> Try to read legacy file");
+
+                legacyBaseFile = getBaseLegacyStorageFileForUser(userId);
+                if (DEBUG) Slog.d(LOG_TAG, "  > Legacy file=" + legacyBaseFile.getPath());
+                if (!legacyBaseFile.exists()) {
+                    if (DEBUG) Slog.d(LOG_TAG, "  > Legacy file does not exist -> Abort");
+                    return;
+                }
+
+                readFrom = new AtomicFile(legacyBaseFile);
+                rootTag = XML_TAG_ASSOCIATIONS;
+            } else {
+                readFrom = file;
+                rootTag = XML_TAG_STATE;
+            }
+
+            if (DEBUG) Slog.d(LOG_TAG, "  > Reading associations...");
+            final int version = readStateFromFileLocked(userId, readFrom, rootTag,
+                    associationsOut, previouslyUsedIdsPerPackageOut);
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "  > Done reading: " + associationsOut);
+                if (version < CURRENT_PERSISTENCE_VERSION) {
+                    Slog.d(LOG_TAG, "  > File used old format: v." + version + " -> Re-write");
+                }
+            }
+
+            if (legacyBaseFile != null || version < CURRENT_PERSISTENCE_VERSION) {
+                // The data is either in the legacy file or in the legacy format, or both.
+                // Save the data to right file in using the current format.
+                if (DEBUG) {
+                    Slog.d(LOG_TAG, "  > Writing the data to " + file.getBaseFile().getPath());
+                }
+                persistStateToFileLocked(file, associationsOut, previouslyUsedIdsPerPackageOut);
+
+                if (legacyBaseFile != null) {
+                    // We saved the data to the right file, can delete the old file now.
+                    if (DEBUG) Slog.d(LOG_TAG, "  > Deleting legacy file");
+                    legacyBaseFile.delete();
+                }
+            }
+        }
+    }
+
+    /**
+     * Persisted data to the disk.
+     *
+     * @param userId Android UserID
+     * @param associations a set of user's associations.
+     * @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user.
+     */
+    void persistStateForUser(@UserIdInt int userId, @NonNull Set<Association> associations,
+            @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
+        Slog.i(LOG_TAG, "Writing associations for user " + userId + " to disk");
+        if (DEBUG) Slog.d(LOG_TAG, "  > " + associations);
+
+        final AtomicFile file = getStorageFileForUser(userId);
+        if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
+        synchronized (file) {
+            persistStateToFileLocked(file, associations, previouslyUsedIdsPerPackage);
+        }
+    }
+
+    private int readStateFromFileLocked(@UserIdInt int userId, @NonNull AtomicFile file,
+            @NonNull String rootTag, @Nullable Set<Association> associationsOut,
+            @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
+        try (FileInputStream in = file.openRead()) {
+            final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+
+            XmlUtils.beginDocument(parser, rootTag);
+            final int version = readIntAttribute(parser, XML_ATTR_PERSISTENCE_VERSION, 0);
+            switch (version) {
+                case 0:
+                    readAssociationsV0(parser, userId, associationsOut);
+                    break;
+                case 1:
+                    while (true) {
+                        parser.nextTag();
+                        if (isStartOfTag(parser, XML_TAG_ASSOCIATIONS)) {
+                            readAssociationsV1(parser, userId, associationsOut);
+                        } else if (isStartOfTag(parser, XML_TAG_PREVIOUSLY_USED_IDS)) {
+                            readPreviouslyUsedIdsV1(parser, previouslyUsedIdsPerPackageOut);
+                        } else if (isEndOfTag(parser, rootTag)) {
+                            break;
+                        }
+                    }
+                    break;
+            }
+            return version;
+        } catch (XmlPullParserException | IOException e) {
+            Slog.e(LOG_TAG, "Error while reading associations file", e);
+            return -1;
+        }
+    }
+
+    private void persistStateToFileLocked(@NonNull AtomicFile file,
+            @Nullable Set<Association> associations,
+            @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
+        file.write(out -> {
+            try {
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
+                serializer.setFeature(
+                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+                serializer.startDocument(null, true);
+                serializer.startTag(null, XML_TAG_STATE);
+                writeIntAttribute(serializer,
+                        XML_ATTR_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
+
+                writeAssociations(serializer, associations);
+                writePreviouslyUsedIds(serializer, previouslyUsedIdsPerPackage);
+
+                serializer.endTag(null, XML_TAG_STATE);
+                serializer.endDocument();
+            } catch (Exception e) {
+                Slog.e(LOG_TAG, "Error while writing associations file", e);
+                throw ExceptionUtils.propagate(e);
+            }
+        });
+    }
+
+    private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
+        return mUserIdToStorageFile.computeIfAbsent(userId,
+                u -> new AtomicFile(getBaseStorageFileForUser(userId)));
+    }
+
+    private static @NonNull File getBaseStorageFileForUser(@UserIdInt int userId) {
+        return new File(Environment.getDataSystemDeDirectory(userId), FILE_NAME);
+    }
+
+    private static @NonNull File getBaseLegacyStorageFileForUser(@UserIdInt int userId) {
+        return new File(Environment.getUserSystemDirectory(userId), FILE_NAME_LEGACY);
+    }
+
+    private static void readAssociationsV0(@NonNull TypedXmlPullParser parser,
+            @UserIdInt int userId, @NonNull Set<Association> out)
+            throws XmlPullParserException, IOException {
+        requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
+
+        // Before Android T Associations didn't have IDs, so when we are upgrading from S (reading
+        // from V0) we need to generate and assign IDs to the existing Associations.
+        // It's safe to do it here, because CDM cannot create new Associations before it reads
+        // existing ones from the backup files. And the fact that we are reading from a V0 file,
+        // means that CDM hasn't assigned any IDs yet, so we can just start from the first available
+        // id for each user (eg. 1 for user 0; 100 001 - for user 1; 200 001 - for user 2; etc).
+        int associationId = CompanionDeviceManagerService.getFirstAssociationIdForUser(userId);
+        while (true) {
+            parser.nextTag();
+            if (isEndOfTag(parser, XML_TAG_ASSOCIATIONS)) break;
+            if (!isStartOfTag(parser, XML_TAG_ASSOCIATION)) continue;
+
+            readAssociationV0(parser, userId, associationId++, out);
+        }
+    }
+
+    private static void readAssociationV0(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
+            int associationId, @NonNull Set<Association> out) throws XmlPullParserException {
+        requireStartOfTag(parser, XML_TAG_ASSOCIATION);
+
+        final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
+        // In v0, CDM did not have a notion of a DeviceId yet, instead each Association had a MAC
+        // address.
+        final String deviceAddress = readStringAttribute(parser, XML_ATTR_DEVICE);
+
+        if (appPackage == null || deviceAddress == null) return;
+
+        final String profile = readStringAttribute(parser, XML_ATTR_PROFILE);
+        final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
+        final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
+
+        // "Convert" MAC address into a DeviceId.
+        final List<DeviceId> deviceIds = Arrays.asList(
+                new DeviceId(TYPE_MAC_ADDRESS, deviceAddress));
+        out.add(new Association(associationId, userId, appPackage, deviceIds, profile,
+                /* managedByCompanionApp */false, notify, timeApproved));
+    }
+
+    private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
+            @UserIdInt int userId, @NonNull Set<Association> out)
+            throws XmlPullParserException, IOException {
+        requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
+
+        while (true) {
+            parser.nextTag();
+            if (isEndOfTag(parser, XML_TAG_ASSOCIATIONS)) break;
+            if (!isStartOfTag(parser, XML_TAG_ASSOCIATION)) continue;
+
+            readAssociationV1(parser, userId, out);
+        }
+    }
+
+    private static void readAssociationV1(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
+            @NonNull Set<Association> out) throws XmlPullParserException, IOException {
+        requireStartOfTag(parser, XML_TAG_ASSOCIATION);
+
+        final int associationId = readIntAttribute(parser, XML_ATTR_ID);
+        final String profile = readStringAttribute(parser, XML_ATTR_PROFILE);
+        final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
+        final boolean managedByApp = readBooleanAttribute(parser, XML_ATTR_MANAGED_BY_APP);
+        final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
+        final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
+
+        final List<DeviceId> deviceIds = new ArrayList<>();
+        while (true) {
+            parser.nextTag();
+            if (isEndOfTag(parser, XML_TAG_ASSOCIATION)) break;
+            if (!isStartOfTag(parser, XML_TAG_DEVICE_ID)) continue;
+
+            final String type = readStringAttribute(parser, XML_ATTR_TYPE);
+            final String value = readStringAttribute(parser, XML_ATTR_VALUE);
+            deviceIds.add(new DeviceId(type, value));
+        }
+
+        out.add(new Association(associationId, userId, appPackage, deviceIds, profile, managedByApp,
+                notify, timeApproved));
+    }
+
+    private static void readPreviouslyUsedIdsV1(@NonNull TypedXmlPullParser parser,
+            @NonNull Map<String, Set<Integer>> out) throws XmlPullParserException, IOException {
+        requireStartOfTag(parser, XML_TAG_PREVIOUSLY_USED_IDS);
+
+        while (true) {
+            parser.nextTag();
+            if (isEndOfTag(parser, XML_TAG_PREVIOUSLY_USED_IDS)) break;
+            if (!isStartOfTag(parser, XML_TAG_PACKAGE)) continue;
+
+            final String packageName = readStringAttribute(parser, XML_ATTR_PACKAGE_NAME);
+            final Set<Integer> usedIds = new HashSet<>();
+
+            while (true) {
+                parser.nextTag();
+                if (isEndOfTag(parser, XML_TAG_PACKAGE)) break;
+                if (!isStartOfTag(parser, XML_TAG_ID)) continue;
+
+                parser.nextToken();
+                final int id = Integer.parseInt(parser.getText());
+                usedIds.add(id);
+            }
+
+            out.put(packageName, usedIds);
+        }
+    }
+
+    private static void writeAssociations(@NonNull XmlSerializer parent,
+            @Nullable Set<Association> associations) throws IOException {
+        final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATIONS);
+        forEach(associations, it -> writeAssociation(serializer, it));
+        serializer.endTag(null, XML_TAG_ASSOCIATIONS);
+    }
+
+    private static void writeAssociation(@NonNull XmlSerializer parent, @NonNull Association a)
+            throws IOException {
+        final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATION);
+
+        writeIntAttribute(serializer, XML_ATTR_ID, a.getAssociationId());
+        writeStringAttribute(serializer, XML_ATTR_PROFILE, a.getDeviceProfile());
+        writeStringAttribute(serializer, XML_ATTR_PACKAGE, a.getPackageName());
+        writeBooleanAttribute(serializer, XML_ATTR_MANAGED_BY_APP, a.isManagedByCompanionApp());
+        writeBooleanAttribute(
+                serializer, XML_ATTR_NOTIFY_DEVICE_NEARBY, a.isNotifyOnDeviceNearby());
+        writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs());
+
+        final List<DeviceId> deviceIds = a.getDeviceIds();
+        for (int i = 0, size = deviceIds.size(); i < size; i++) {
+            writeDeviceId(serializer, deviceIds.get(i));
+        }
+
+        serializer.endTag(null, XML_TAG_ASSOCIATION);
+    }
+
+    private static void writeDeviceId(@NonNull XmlSerializer parent, @NonNull DeviceId deviceId)
+            throws IOException {
+        final XmlSerializer serializer = parent.startTag(null, XML_TAG_DEVICE_ID);
+
+        writeStringAttribute(serializer, XML_ATTR_TYPE, deviceId.getType());
+        writeStringAttribute(serializer, XML_ATTR_VALUE, deviceId.getValue());
+
+        serializer.endTag(null, XML_TAG_DEVICE_ID);
+    }
+
+    private static void writePreviouslyUsedIds(@NonNull XmlSerializer parent,
+            @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) throws IOException {
+        final XmlSerializer serializer = parent.startTag(null, XML_TAG_PREVIOUSLY_USED_IDS);
+        for (Map.Entry<String, Set<Integer>> entry : previouslyUsedIdsPerPackage.entrySet()) {
+            writePreviouslyUsedIdsForPackage(serializer, entry.getKey(), entry.getValue());
+        }
+        serializer.endTag(null, XML_TAG_PREVIOUSLY_USED_IDS);
+    }
+
+    private static void writePreviouslyUsedIdsForPackage(@NonNull XmlSerializer parent,
+            @NonNull String packageName, @NonNull Set<Integer> previouslyUsedIds)
+            throws IOException {
+        final XmlSerializer serializer = parent.startTag(null, XML_TAG_PACKAGE);
+        writeStringAttribute(serializer, XML_ATTR_PACKAGE_NAME, packageName);
+        forEach(previouslyUsedIds, id -> serializer.startTag(null, XML_TAG_ID)
+                .text(Integer.toString(id))
+                .endTag(null, XML_TAG_ID));
+        serializer.endTag(null, XML_TAG_PACKAGE);
+    }
+
+    private static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+            throws XmlPullParserException {
+        return parser.getEventType() == START_TAG && tag.equals(parser.getName());
+    }
+
+    private static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+            throws XmlPullParserException {
+        return parser.getEventType() == END_TAG && tag.equals(parser.getName());
+    }
+
+    private static void requireStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+            throws XmlPullParserException {
+        if (isStartOfTag(parser, tag)) return;
+        throw new XmlPullParserException(
+                "Should be at the start of \"" + XML_TAG_ASSOCIATIONS + "\" tag");
+    }
+}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 95dc667..b641377 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -16,6 +16,9 @@
 # ServiceWatcher
 per-file ServiceWatcher.java = sooniln@google.com
 
+# Health
+per-file BatteryService.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
+
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
 per-file *Battery* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index e7b078a..0fde6fa 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -45,8 +45,8 @@
 import com.android.internal.os.ZygoteConnectionConstants;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.am.ActivityManagerService;
-import com.android.server.am.CriticalEventLog;
 import com.android.server.am.TraceErrorLogger;
+import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.wm.SurfaceAnimationThread;
 
 import java.io.BufferedReader;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b194930..f6c1106 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -382,6 +382,7 @@
 import com.android.server.appop.AppOpsService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
+import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.graphics.fonts.FontManagerInternal;
 import com.android.server.job.JobSchedulerInternal;
@@ -2394,7 +2395,6 @@
     private void start() {
         removeAllProcessGroups();
 
-        CriticalEventLog.init();
         mBatteryStatsService.publish();
         mAppOpsService.publish();
         Slog.d("AppOps", "AppOpsService published");
@@ -2404,6 +2404,7 @@
         mActivityTaskManager.onActivityManagerInternalAdded();
         mPendingIntentController.onActivityManagerInternalAdded();
         mAppProfiler.onActivityManagerInternalAdded();
+        CriticalEventLog.init();
     }
 
     public void initPowerManagement() {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 6fe9f8e..b7b4870 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -683,12 +683,10 @@
             if (measuredEnergyDeltas != null) {
                 final long[] displayChargeUC = measuredEnergyDeltas.displayChargeUC;
                 if (displayChargeUC != null && displayChargeUC.length > 0) {
-                    // TODO (b/194107383): pass all display ordinals to mStats with
-                    //  displayScreenStates
-                    final long primaryDisplayChargeUC = displayChargeUC[0];
-                    // If updating, pass in what BatteryExternalStatsWorker thinks screenState is.
-                    mStats.updateDisplayMeasuredEnergyStatsLocked(primaryDisplayChargeUC,
-                            screenState, elapsedRealtime);
+                    // If updating, pass in what BatteryExternalStatsWorker thinks
+                    // displayScreenStates is.
+                    mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC,
+                            displayScreenStates, elapsedRealtime);
                 }
 
                 final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 5fac879..b7ea3dc 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -49,6 +49,7 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.MemoryPressureUtil;
+import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.wm.WindowProcessController;
 
 import java.io.File;
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index fc02f19..4ef36d6 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -87,6 +87,7 @@
         DeviceConfig.NAMESPACE_LMKD_NATIVE,
         DeviceConfig.NAMESPACE_MEDIA_NATIVE,
         DeviceConfig.NAMESPACE_NETD_NATIVE,
+        DeviceConfig.NAMESPACE_NNAPI_NATIVE,
         DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bc30f87..34277a2 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6163,6 +6163,11 @@
         if (pkgName == null) {
             pkgName = "";
         }
+        if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) {
+            avrcpSupportsAbsoluteVolume(device.getAddress(),
+                    deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+            return;
+        }
 
         int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
                 device.getType());
@@ -7841,7 +7846,7 @@
         }
     }
 
-    public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
+    private void avrcpSupportsAbsoluteVolume(String address, boolean support) {
         // address is not used for now, but may be used when multiple a2dp devices are supported
         sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
                 + address + " support=" + support));
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 85d6d7f..031f6ee 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -360,6 +360,43 @@
         }
     }
 
+    /**
+     * Only call this method on interfaces where lockout does not come from onError, I.E. the
+     * old HIDL implementation.
+     */
+    protected void onLockoutTimed(long durationMillis) {
+        final ClientMonitorCallbackConverter listener = getListener();
+        final CoexCoordinator coordinator = CoexCoordinator.getInstance();
+        coordinator.onAuthenticationError(this, BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+                new CoexCoordinator.ErrorCallback() {
+            @Override
+            public void sendHapticFeedback() {
+                if (listener != null && mShouldVibrate) {
+                    vibrateError();
+                }
+            }
+        });
+    }
+
+    /**
+     * Only call this method on interfaces where lockout does not come from onError, I.E. the
+     * old HIDL implementation.
+     */
+    protected void onLockoutPermanent() {
+        final ClientMonitorCallbackConverter listener = getListener();
+        final CoexCoordinator coordinator = CoexCoordinator.getInstance();
+        coordinator.onAuthenticationError(this,
+                BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
+                new CoexCoordinator.ErrorCallback() {
+            @Override
+            public void sendHapticFeedback() {
+                if (listener != null && mShouldVibrate) {
+                    vibrateError();
+                }
+            }
+        });
+    }
+
     private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) {
         if (listener == null) {
             Slog.e(TAG, "Unable to sendAuthenticationCanceled, listener null");
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index cbceba6..97d791b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -225,6 +225,7 @@
 
     @Override
     public void onLockoutTimed(long durationMillis) {
+        super.onLockoutTimed(durationMillis);
         mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
         // Lockout metrics are logged as an error code.
         final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT;
@@ -239,6 +240,7 @@
 
     @Override
     public void onLockoutPermanent() {
+        super.onLockoutPermanent();
         mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
         // Lockout metrics are logged as an error code.
         final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 42b676f..9d2cff9 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.camera;
 
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.os.Build.VERSION_CODES.M;
 
 import android.annotation.IntDef;
@@ -39,7 +40,9 @@
 import android.hardware.CameraStreamStats;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceProxy;
+import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.hardware.display.DisplayManager;
@@ -346,13 +349,13 @@
 
     private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
 
-    private final class TaskInfo {
-        private int frontTaskId;
-        private boolean isResizeable;
-        private boolean isFixedOrientationLandscape;
-        private boolean isFixedOrientationPortrait;
-        private int displayId;
-        private int userId;
+    public static final class TaskInfo {
+        public int frontTaskId;
+        public boolean isResizeable;
+        public boolean isFixedOrientationLandscape;
+        public boolean isFixedOrientationPortrait;
+        public int displayId;
+        public int userId;
     }
 
     private final class TaskStateHandler extends TaskStackListener {
@@ -367,7 +370,8 @@
             synchronized (mMapLock) {
                 TaskInfo info = new TaskInfo();
                 info.frontTaskId = taskInfo.taskId;
-                info.isResizeable = taskInfo.isResizeable;
+                info.isResizeable =
+                        (taskInfo.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
                 info.displayId = taskInfo.displayId;
                 info.userId = taskInfo.userId;
                 info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
@@ -427,97 +431,108 @@
         }
     };
 
-    private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
-        private boolean isMOrBelow(Context ctx, String packageName) {
-            try {
-                return ctx.getPackageManager().getPackageInfo(
-                        packageName, 0).applicationInfo.targetSdkVersion <= M;
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(TAG,"Package name not found!");
-            }
-            return false;
+    private static boolean isMOrBelow(Context ctx, String packageName) {
+        try {
+            return ctx.getPackageManager().getPackageInfo(
+                    packageName, 0).applicationInfo.targetSdkVersion <= M;
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.e(TAG,"Package name not found!");
+        }
+        return false;
+    }
+
+    /**
+     * Estimate the app crop-rotate-scale compensation value.
+     */
+    public static int getCropRotateScale(@NonNull Context ctx, @NonNull String packageName,
+            @Nullable TaskInfo taskInfo, int displayRotation, int lensFacing,
+            boolean ignoreResizableAndSdkCheck) {
+        if (taskInfo == null) {
+            return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
         }
 
-        /**
-         * Gets whether crop-rotate-scale is needed.
-         */
-        private boolean getNeedCropRotateScale(@NonNull Context ctx, @NonNull String packageName,
-                @Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing,
-                boolean ignoreResizableAndSdkCheck) {
-            if (taskInfo == null) {
-                return false;
-            }
+        // External cameras do not need crop-rotate-scale.
+        if (lensFacing != CameraMetadata.LENS_FACING_FRONT
+                && lensFacing != CameraMetadata.LENS_FACING_BACK) {
+            Log.v(TAG, "lensFacing=" + lensFacing + ". Crop-rotate-scale is disabled.");
+            return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+        }
 
-            // External cameras do not need crop-rotate-scale.
-            if (lensFacing != CameraMetadata.LENS_FACING_FRONT
-                    && lensFacing != CameraMetadata.LENS_FACING_BACK) {
-                Log.v(TAG, "lensFacing=" + lensFacing + ". Crop-rotate-scale is disabled.");
-                return false;
-            }
-
-            // In case the activity behavior is not explicitly overridden, enable the
-            // crop-rotate-scale workaround if the app targets M (or below) or is not
-            // resizeable.
-            if (!ignoreResizableAndSdkCheck && !isMOrBelow(ctx, packageName) &&
-                    taskInfo.isResizeable) {
-                Slog.v(TAG,
-                        "The activity is N or above and claims to support resizeable-activity. "
-                                + "Crop-rotate-scale is disabled.");
-                return false;
-            }
-
-            DisplayManager displayManager = ctx.getSystemService(DisplayManager.class);
-            int rotationDegree = 0;
-            if (displayManager != null) {
-                Display display = displayManager.getDisplay(taskInfo.displayId);
-                if (display == null) {
-                    Slog.e(TAG, "Invalid display id: " + taskInfo.displayId);
-                    return false;
-                }
-
-                int rotation = display.getRotation();
-                switch (rotation) {
-                    case Surface.ROTATION_0:
-                        rotationDegree = 0;
-                        break;
-                    case Surface.ROTATION_90:
-                        rotationDegree = 90;
-                        break;
-                    case Surface.ROTATION_180:
-                        rotationDegree = 180;
-                        break;
-                    case Surface.ROTATION_270:
-                        rotationDegree = 270;
-                        break;
-                }
-            } else {
-                Slog.e(TAG, "Failed to query display manager!");
-                return false;
-            }
-
-            // Here we only need to know whether the camera is landscape or portrait. Therefore we
-            // don't need to consider whether it is a front or back camera. The formula works for
-            // both.
-            boolean landscapeCamera = ((rotationDegree + sensorOrientation) % 180 == 0);
+        // In case the activity behavior is not explicitly overridden, enable the
+        // crop-rotate-scale workaround if the app targets M (or below) or is not
+        // resizeable.
+        if (!ignoreResizableAndSdkCheck && !isMOrBelow(ctx, packageName) &&
+                taskInfo.isResizeable) {
             Slog.v(TAG,
-                    "Display.getRotation()=" + rotationDegree
-                            + " CameraCharacteristics.SENSOR_ORIENTATION=" + sensorOrientation
-                            + " isFixedOrientationPortrait=" + taskInfo.isFixedOrientationPortrait
-                            + " isFixedOrientationLandscape=" +
-                            taskInfo.isFixedOrientationLandscape);
-            // We need to do crop-rotate-scale when camera is landscape and activity is portrait or
-            // vice versa.
-            return (taskInfo.isFixedOrientationPortrait && landscapeCamera)
-                    || (taskInfo.isFixedOrientationLandscape && !landscapeCamera);
+                    "The activity is N or above and claims to support resizeable-activity. "
+                            + "Crop-rotate-scale is disabled.");
+            return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
         }
 
+        if (!taskInfo.isFixedOrientationPortrait && !taskInfo.isFixedOrientationLandscape) {
+            Log.v(TAG, "Non-fixed orientation activity. Crop-rotate-scale is disabled.");
+            return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+        }
+
+        int rotationDegree;
+        switch (displayRotation) {
+            case Surface.ROTATION_0:
+                rotationDegree = 0;
+                break;
+            case Surface.ROTATION_90:
+                rotationDegree = 90;
+                break;
+            case Surface.ROTATION_180:
+                rotationDegree = 180;
+                break;
+            case Surface.ROTATION_270:
+                rotationDegree = 270;
+                break;
+            default:
+                Log.e(TAG, "Unsupported display rotation: " + displayRotation);
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+        }
+
+        Slog.v(TAG,
+                "Display.getRotation()=" + rotationDegree
+                        + " isFixedOrientationPortrait=" + taskInfo.isFixedOrientationPortrait
+                        + " isFixedOrientationLandscape=" +
+                        taskInfo.isFixedOrientationLandscape);
+        // We are trying to estimate the necessary rotation compensation for clients that
+        // don't handle various display orientations.
+        // The logic that is missing on client side is similar to the reference code
+        // in {@link android.hardware.Camera#setDisplayOrientation} where "info.orientation"
+        // is already applied in "CameraUtils::getRotationTransform".
+        // Care should be taken to reverse the rotation direction depending on the camera
+        // lens facing.
+        if (rotationDegree == 0) {
+            return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+        }
+        if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
+            // Switch direction for front facing cameras
+            rotationDegree = 360 - rotationDegree;
+        }
+
+        switch (rotationDegree) {
+            case 90:
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_90;
+            case 270:
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_270;
+            case 180:
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_180;
+            case 0:
+            default:
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+        }
+    }
+
+    private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
         @Override
-        public boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation,
-                int lensFacing) {
+        public int getRotateAndCropOverride(String packageName, int lensFacing) {
             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
                 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
                         " camera service UID!");
-                return false;
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
             }
 
             // TODO: Modify the sensor orientation in camera characteristics along with any 3A
@@ -531,10 +546,10 @@
                 if (CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_ROTATE_AND_CROP, packageName,
                         UserHandle.getUserHandleForUid(taskInfo.userId))) {
                     Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP enabled!");
-                    return true;
+                    return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
                 } else {
                     Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP disabled!");
-                    return false;
+                    return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
                 }
             }
             boolean ignoreResizableAndSdkCheck = false;
@@ -544,7 +559,23 @@
                 Slog.v(TAG, "OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK enabled!");
                 ignoreResizableAndSdkCheck = true;
             }
-            return getNeedCropRotateScale(mContext, packageName, taskInfo, sensorOrientation,
+
+            DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+            int displayRotation;
+            if (displayManager != null) {
+                Display display = displayManager.getDisplay(taskInfo.displayId);
+                if (display == null) {
+                    Slog.e(TAG, "Invalid display id: " + taskInfo.displayId);
+                    return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+                }
+
+                displayRotation = display.getRotation();
+            } else {
+                Slog.e(TAG, "Failed to query display manager!");
+                return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
+            }
+
+            return getCropRotateScale(mContext, packageName, taskInfo, displayRotation,
                     lensFacing, ignoreResizableAndSdkCheck);
         }
 
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index dc2b418..d77e3b5 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -129,17 +129,19 @@
     }
 
     private boolean isActivityAllowed(ActivityInfo activityInfo) {
-        if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return true;
-
-        // If the activity doesn't have showWhenLocked enabled, disallow the activity.
-        final boolean showWhenLocked =
-                (activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
-        if (!showWhenLocked) {
-            return false;
-        }
-
-        // Check the cached user preferences to see if the user has allowed this app.
-        return mEnabledApps.contains(activityInfo.applicationInfo.packageName);
+        return true;
+        // TODO(b/191994709): Uncomment the lines below once Dreams and Assistant have been fixed.
+//        if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return true;
+//
+//        // If the activity doesn't have showWhenLocked enabled, disallow the activity.
+//        final boolean showWhenLocked =
+//                (activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
+//        if (!showWhenLocked) {
+//            return false;
+//        }
+//
+//        // Check the cached user preferences to see if the user has allowed this app.
+//        return mEnabledApps.contains(activityInfo.applicationInfo.packageName);
     }
 
     private final class SettingsObserver extends ContentObserver {
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 091e6c4..a56a8ea 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -227,7 +227,7 @@
             subscriberId = tele.getSubscriberId();
             mNetworkTemplate = new NetworkTemplate(
                     NetworkTemplate.MATCH_MOBILE, subscriberId, new String[] { subscriberId },
-                    null, NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                    null, NetworkStats.METERED_YES, NetworkStats.ROAMING_ALL,
                     NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
                     SUBSCRIBER_ID_MATCH_RULE_EXACT);
             mUsageCallback = new UsageCallback() {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 565c9ae..243a336b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1270,6 +1270,9 @@
             capsBuilder.addCapability(NET_CAPABILITY_NOT_METERED);
         }
 
+        capsBuilder.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
+                ? Arrays.asList(mConfig.underlyingNetworks) : null);
+
         mNetworkCapabilities = capsBuilder.build();
         mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
                 mNetworkCapabilities, lp,
@@ -1290,8 +1293,6 @@
         } finally {
             Binder.restoreCallingIdentity(token);
         }
-        mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
-                ? Arrays.asList(mConfig.underlyingNetworks) : null);
         updateState(DetailedState.CONNECTED, "agentConnect");
     }
 
diff --git a/services/core/java/com/android/server/am/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
similarity index 96%
rename from services/core/java/com/android/server/am/CriticalEventLog.java
rename to services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index 6b69559..d5fe9c9 100644
--- a/services/core/java/com/android/server/am/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.am;
+package com.android.server.criticalevents;
 
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -23,11 +23,11 @@
 import com.android.framework.protobuf.nano.MessageNanoPrinter;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.RingBuffer;
-import com.android.server.am.nano.CriticalEventLogProto;
-import com.android.server.am.nano.CriticalEventLogStorageProto;
-import com.android.server.am.nano.CriticalEventProto;
-import com.android.server.am.nano.CriticalEventProto.HalfWatchdog;
-import com.android.server.am.nano.CriticalEventProto.Watchdog;
+import com.android.server.criticalevents.nano.CriticalEventLogProto;
+import com.android.server.criticalevents.nano.CriticalEventLogStorageProto;
+import com.android.server.criticalevents.nano.CriticalEventProto;
+import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
 
 import java.io.File;
 import java.io.FileOutputStream;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 63d32c8..768587a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1052,11 +1052,6 @@
         }
         assert(state != Display.STATE_UNKNOWN);
 
-        // Initialize things the first time the power state is changed.
-        if (mustInitialize) {
-            initialize(state);
-        }
-
         // Apply the proximity sensor.
         if (mProximitySensor != null) {
             if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
@@ -1107,6 +1102,11 @@
             state = Display.STATE_OFF;
         }
 
+        // Initialize things the first time the power state is changed.
+        if (mustInitialize) {
+            initialize(state);
+        }
+
         // Animate the screen state change unless already animating.
         // The transition may be deferred, so after this point we will use the
         // actual state instead of the desired one.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 550f1b9..65ec1c0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -408,11 +408,11 @@
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
                 R.bool.config_cecRcProfileSourceRootMenu_userConfigurable);
         rcProfileSourceRootMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
                 R.bool.config_cecRcProfileSourceRootMenuHandled_allowed,
                 R.bool.config_cecRcProfileSourceRootMenuHandled_default);
         rcProfileSourceRootMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
                 R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed,
                 R.bool.config_cecRcProfileSourceRootMenuNotHandled_default);
 
@@ -420,11 +420,11 @@
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
                 R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable);
         rcProfileSourceSetupMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
                 R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed,
                 R.bool.config_cecRcProfileSourceSetupMenuHandled_default);
         rcProfileSourceSetupMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
                 R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed,
                 R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default);
 
@@ -432,11 +432,11 @@
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
                 R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable);
         rcProfileSourceContentsMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
                 R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed,
                 R.bool.config_cecRcProfileSourceContentsMenuHandled_default);
         rcProfileSourceContentsMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
                 R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed,
                 R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default);
 
@@ -444,11 +444,11 @@
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
                 R.bool.config_cecRcProfileSourceTopMenu_userConfigurable);
         rcProfileSourceTopMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
                 R.bool.config_cecRcProfileSourceTopMenuHandled_allowed,
                 R.bool.config_cecRcProfileSourceTopMenuHandled_default);
         rcProfileSourceTopMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
                 R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed,
                 R.bool.config_cecRcProfileSourceTopMenuNotHandled_default);
 
@@ -457,14 +457,194 @@
                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable);
         rcProfileSourceMediaContextSensitiveMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed,
                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default);
         rcProfileSourceMediaContextSensitiveMenu.registerValue(
-                HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED,
+                HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed,
                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
 
+        Setting querySadLpcm = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM,
+                R.bool.config_cecQuerySadLpcm_userConfigurable);
+        querySadLpcm.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadLpcmEnabled_allowed,
+                R.bool.config_cecQuerySadLpcmEnabled_default);
+        querySadLpcm.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadLpcmDisabled_allowed,
+                R.bool.config_cecQuerySadLpcmDisabled_default);
+
+        Setting querySadDd = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD,
+                R.bool.config_cecQuerySadDd_userConfigurable);
+        querySadDd.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadDdEnabled_allowed,
+                R.bool.config_cecQuerySadDdEnabled_default);
+        querySadDd.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadDdDisabled_allowed,
+                R.bool.config_cecQuerySadDdDisabled_default);
+
+        Setting querySadMpeg1 = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
+                R.bool.config_cecQuerySadMpeg1_userConfigurable);
+        querySadMpeg1.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadMpeg1Enabled_allowed,
+                R.bool.config_cecQuerySadMpeg1Enabled_default);
+        querySadMpeg1.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadMpeg1Disabled_allowed,
+                R.bool.config_cecQuerySadMpeg1Disabled_default);
+
+        Setting querySadMp3 = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3,
+                R.bool.config_cecQuerySadMp3_userConfigurable);
+        querySadMp3.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadMp3Enabled_allowed,
+                R.bool.config_cecQuerySadMp3Enabled_default);
+        querySadMp3.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadMp3Disabled_allowed,
+                R.bool.config_cecQuerySadMp3Disabled_default);
+
+        Setting querySadMpeg2 = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2,
+                R.bool.config_cecQuerySadMpeg2_userConfigurable);
+        querySadMpeg2.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadMpeg2Enabled_allowed,
+                R.bool.config_cecQuerySadMpeg2Enabled_default);
+        querySadMpeg2.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadMpeg2Disabled_allowed,
+                R.bool.config_cecQuerySadMpeg2Disabled_default);
+
+        Setting querySadAac = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC,
+                R.bool.config_cecQuerySadAac_userConfigurable);
+        querySadAac.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadAacEnabled_allowed,
+                R.bool.config_cecQuerySadAacEnabled_default);
+        querySadAac.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadAacDisabled_allowed,
+                R.bool.config_cecQuerySadAacDisabled_default);
+
+        Setting querySadDts = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS,
+                R.bool.config_cecQuerySadDts_userConfigurable);
+        querySadDts.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadDtsEnabled_allowed,
+                R.bool.config_cecQuerySadDtsEnabled_default);
+        querySadDts.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadDtsDisabled_allowed,
+                R.bool.config_cecQuerySadDtsDisabled_default);
+
+        Setting querySadAtrac = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC,
+                R.bool.config_cecQuerySadAtrac_userConfigurable);
+        querySadAtrac.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadAtracEnabled_allowed,
+                R.bool.config_cecQuerySadAtracEnabled_default);
+        querySadAtrac.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadAtracDisabled_allowed,
+                R.bool.config_cecQuerySadAtracDisabled_default);
+
+        Setting querySadOnebitaudio = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
+                R.bool.config_cecQuerySadOnebitaudio_userConfigurable);
+        querySadOnebitaudio.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadOnebitaudioEnabled_allowed,
+                R.bool.config_cecQuerySadOnebitaudioEnabled_default);
+        querySadOnebitaudio.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadOnebitaudioDisabled_allowed,
+                R.bool.config_cecQuerySadOnebitaudioDisabled_default);
+
+        Setting querySadDdp = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP,
+                R.bool.config_cecQuerySadDdp_userConfigurable);
+        querySadDdp.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadDdpEnabled_allowed,
+                R.bool.config_cecQuerySadDdpEnabled_default);
+        querySadDdp.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadDdpDisabled_allowed,
+                R.bool.config_cecQuerySadDdpDisabled_default);
+
+        Setting querySadDtshd = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD,
+                R.bool.config_cecQuerySadDtshd_userConfigurable);
+        querySadDtshd.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadDtshdEnabled_allowed,
+                R.bool.config_cecQuerySadDtshdEnabled_default);
+        querySadDtshd.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadDtshdDisabled_allowed,
+                R.bool.config_cecQuerySadDtshdDisabled_default);
+
+        Setting querySadTruehd = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
+                R.bool.config_cecQuerySadTruehd_userConfigurable);
+        querySadTruehd.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadTruehdEnabled_allowed,
+                R.bool.config_cecQuerySadTruehdEnabled_default);
+        querySadTruehd.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadTruehdDisabled_allowed,
+                R.bool.config_cecQuerySadTruehdDisabled_default);
+
+        Setting querySadDst = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
+                R.bool.config_cecQuerySadDst_userConfigurable);
+        querySadDst.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadDstEnabled_allowed,
+                R.bool.config_cecQuerySadDstEnabled_default);
+        querySadDst.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadDstDisabled_allowed,
+                R.bool.config_cecQuerySadDstDisabled_default);
+
+        Setting querySadWmapro = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
+                R.bool.config_cecQuerySadWmapro_userConfigurable);
+        querySadWmapro.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadWmaproEnabled_allowed,
+                R.bool.config_cecQuerySadWmaproEnabled_default);
+        querySadWmapro.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadWmaproDisabled_allowed,
+                R.bool.config_cecQuerySadWmaproDisabled_default);
+
+        Setting querySadMax = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX,
+                R.bool.config_cecQuerySadMax_userConfigurable);
+        querySadMax.registerValue(
+                HdmiControlManager.QUERY_SAD_ENABLED,
+                R.bool.config_cecQuerySadMaxEnabled_allowed,
+                R.bool.config_cecQuerySadMaxEnabled_default);
+        querySadMax.registerValue(
+                HdmiControlManager.QUERY_SAD_DISABLED,
+                R.bool.config_cecQuerySadMaxDisabled_allowed,
+                R.bool.config_cecQuerySadMaxDisabled_default);
+
         verifySettings();
     }
 
@@ -530,6 +710,36 @@
             case HdmiControlManager
                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
                 return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO:
+                return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX:
+                return STORAGE_SHARED_PREFS;
             default:
                 throw new VerificationException("Invalid CEC setting '" + setting.getName()
                         + "' storage.");
@@ -571,6 +781,36 @@
             case HdmiControlManager
                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
                 return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO:
+                return setting.getName();
+            case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX:
+                return setting.getName();
             default:
                 throw new VerificationException("Invalid CEC setting '" + setting.getName()
                     + "' storage key.");
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index ab8217a..f356c36 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -151,6 +151,7 @@
     private int mActiveRoutingPath;
 
     protected final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
+    @VisibleForTesting
     protected final Object mLock;
 
     // A collection of FeatureAction.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index d4fa1df..4f55249 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -332,27 +332,27 @@
         HdmiCecConfig hdmiCecConfig = mService.getHdmiCecConfig();
         if (hdmiCecConfig.getIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU)
-                == HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_HANDLED) {
+                == HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED) {
             features.add(Constants.RC_PROFILE_SOURCE_HANDLES_ROOT_MENU);
         }
         if (hdmiCecConfig.getIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU)
-                == HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_HANDLED) {
+                == HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED) {
             features.add(Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU);
         }
         if (hdmiCecConfig.getIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU)
-                == HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED) {
+                == HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED) {
             features.add(Constants.RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU);
         }
         if (hdmiCecConfig.getIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU)
-                == HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_HANDLED) {
+                == HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED) {
             features.add(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU);
         }
         if (hdmiCecConfig.getIntValue(HdmiControlManager
                 .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU)
-                == HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED) {
+                == HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED) {
             features.add(Constants.RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
         }
         return features;
diff --git a/services/core/java/com/android/server/hdmi/RequestSadAction.java b/services/core/java/com/android/server/hdmi/RequestSadAction.java
index 4d36078..702c000 100644
--- a/services/core/java/com/android/server/hdmi/RequestSadAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestSadAction.java
@@ -16,10 +16,10 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.hdmi.HdmiControlManager;
 import android.util.Slog;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -35,27 +35,11 @@
 
     // State in which the action is waiting for <Report Short Audio Descriptor>.
     private static final int STATE_WAITING_FOR_REPORT_SAD = 1;
-
-    private static final List<Integer> ALL_CEC_CODECS = new ArrayList<Integer>(Arrays.asList(
-            Constants.AUDIO_CODEC_LPCM,
-            Constants.AUDIO_CODEC_DD,
-            Constants.AUDIO_CODEC_MPEG1,
-            Constants.AUDIO_CODEC_MP3,
-            Constants.AUDIO_CODEC_MPEG2,
-            Constants.AUDIO_CODEC_AAC,
-            Constants.AUDIO_CODEC_DTS,
-            Constants.AUDIO_CODEC_ATRAC,
-            Constants.AUDIO_CODEC_ONEBITAUDIO,
-            Constants.AUDIO_CODEC_DDP,
-            Constants.AUDIO_CODEC_DTSHD,
-            Constants.AUDIO_CODEC_TRUEHD,
-            Constants.AUDIO_CODEC_DST,
-            Constants.AUDIO_CODEC_WMAPRO,
-            Constants.AUDIO_CODEC_MAX));
     private static final int MAX_SAD_PER_REQUEST = 4;
     private static final int RETRY_COUNTER_MAX = 1;
     private final int mTargetAddress;
     private final RequestSadCallback mCallback;
+    private final List<Integer> mCecCodecsToQuery = new ArrayList<>();
     // List of all valid SADs reported by the target device. Not parsed nor deduplicated.
     private final List<byte[]> mSupportedSads = new ArrayList<>();
     private int mQueriedSadCount = 0; // Number of SADs queries that has already been completed
@@ -71,9 +55,84 @@
         super(source);
         mTargetAddress = targetAddress;
         mCallback = Objects.requireNonNull(callback);
+        HdmiCecConfig hdmiCecConfig = localDevice().mService.getHdmiCecConfig();
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_LPCM);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_DD);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_MPEG1);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_MP3);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_MPEG2);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_AAC);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_DTS);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_ATRAC);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_ONEBITAUDIO);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_DDP);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_DTSHD);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_TRUEHD);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_DST);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_WMAPRO);
+        }
+        if (hdmiCecConfig.getIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX)
+                == HdmiControlManager.QUERY_SAD_ENABLED) {
+            mCecCodecsToQuery.add(Constants.AUDIO_CODEC_MAX);
+        }
     }
 
-
     @Override
     boolean start() {
         querySad();
@@ -81,13 +140,13 @@
     }
 
     private void querySad() {
-        if (mQueriedSadCount >= ALL_CEC_CODECS.size()) {
+        if (mQueriedSadCount >= mCecCodecsToQuery.size()) {
             wrapUpAndFinish();
             return;
         }
-        int[] codecsToQuery = ALL_CEC_CODECS.subList(mQueriedSadCount,
-                Math.min(ALL_CEC_CODECS.size(), mQueriedSadCount + MAX_SAD_PER_REQUEST))
-                    .stream().mapToInt(i -> i).toArray();
+        int[] codecsToQuery = mCecCodecsToQuery.subList(mQueriedSadCount,
+                Math.min(mCecCodecsToQuery.size(), mQueriedSadCount + MAX_SAD_PER_REQUEST))
+                .stream().mapToInt(i -> i).toArray();
         sendCommand(HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(getSourceAddress(),
                 mTargetAddress, codecsToQuery));
         mState = STATE_WAITING_FOR_REPORT_SAD;
@@ -111,9 +170,10 @@
                     byte[] sad = new byte[]{cmd.getParams()[i], cmd.getParams()[i + 1],
                             cmd.getParams()[i + 2]};
                     updateResult(sad);
+                } else {
+                    // Don't include invalid codecs in the result. Don't query again.
+                    Slog.w(TAG, "Dropped invalid codec " + cmd.getParams()[i] + ".");
                 }
-                // Don't include invalid codecs in the result. Don't query again.
-                Slog.w(TAG, "Received invalid codec " + cmd.getParams()[i] + ".");
             }
             mQueriedSadCount += MAX_SAD_PER_REQUEST;
             mTimeoutRetry = 0;
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 0045499..ffeaad1 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -18,11 +18,14 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.app.ILocaleManager;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.LocaleList;
@@ -142,7 +145,6 @@
         }
     }
 
-
     private void setApplicationLocalesUnchecked(@NonNull String appPackageName,
             @UserIdInt int userId, @NonNull LocaleList locales) {
         if (DEBUG) {
@@ -152,9 +154,76 @@
         final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
                 mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName,
                         userId);
-        updater.setLocales(locales).commit();
+        boolean isSuccess = updater.setLocales(locales).commit();
+
+        //We want to send the broadcasts only if config was actually updated on commit.
+        if (isSuccess) {
+            notifyAppWhoseLocaleChanged(appPackageName, userId, locales);
+            notifyInstallerOfAppWhoseLocaleChanged(appPackageName, userId, locales);
+            notifyRegisteredReceivers(appPackageName, userId, locales);
+        }
     }
 
+    /**
+     * Sends an implicit broadcast with action
+     * {@link android.content.Intent#ACTION_APPLICATION_LOCALE_CHANGED}
+     * to receivers with {@link android.Manifest.permission#READ_APP_SPECIFIC_LOCALES}.
+     */
+    private void notifyRegisteredReceivers(String appPackageName, int userId,
+            LocaleList locales) {
+        Intent intent = createBaseIntent(Intent.ACTION_APPLICATION_LOCALE_CHANGED,
+                appPackageName, locales);
+        mContext.sendBroadcastAsUser(intent, UserHandle.of(userId),
+                Manifest.permission.READ_APP_SPECIFIC_LOCALES);
+    }
+
+    /**
+     * Sends an explicit broadcast with action
+     * {@link android.content.Intent#ACTION_APPLICATION_LOCALE_CHANGED} to
+     * the installer (as per {@link android.content.pm.InstallSourceInfo#getInstallingPackageName})
+     * of app whose locale has changed.
+     *
+     * <p><b>Note:</b> This is can be used by installers to deal with cases such as
+     * language-based APK Splits.
+     */
+    private void notifyInstallerOfAppWhoseLocaleChanged(String appPackageName, int userId,
+            LocaleList locales) {
+        try {
+            String installingPackageName = mContext.getPackageManager()
+                    .getInstallSourceInfo(appPackageName).getInstallingPackageName();
+            if (installingPackageName != null) {
+                Intent intent = createBaseIntent(Intent.ACTION_APPLICATION_LOCALE_CHANGED,
+                        appPackageName, locales);
+                //Set package name to ensure that only installer of the app receives this intent.
+                intent.setPackage(installingPackageName);
+                mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Package not found " + appPackageName);
+        }
+    }
+
+    /**
+     * Sends an explicit broadcast with action {@link android.content.Intent#ACTION_LOCALE_CHANGED}
+     * to the app whose locale has changed.
+     */
+    private void notifyAppWhoseLocaleChanged(String appPackageName, int userId,
+            LocaleList locales) {
+        Intent intent = createBaseIntent(Intent.ACTION_LOCALE_CHANGED, appPackageName, locales);
+        //Set package name to ensure that only the app whose locale changed receives this intent.
+        intent.setPackage(appPackageName);
+        intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+        mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+    }
+
+    private static Intent createBaseIntent(String intentAction, String appPackageName,
+            LocaleList locales) {
+        return new Intent(intentAction)
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, appPackageName)
+                .putExtra(Intent.EXTRA_LOCALE_LIST, locales)
+                .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                        | Intent.FLAG_RECEIVER_FOREGROUND);
+    }
 
     /**
      * Checks if the package is owned by the calling app or not for the given user id.
@@ -175,7 +244,7 @@
     }
 
     private void enforceChangeConfigurationPermission() {
-        mContext.enforceCallingPermission(
+        mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_CONFIGURATION, "setApplicationLocales");
     }
 
@@ -231,7 +300,7 @@
     }
 
     private void enforceReadAppSpecificLocalesPermission() {
-        mContext.enforceCallingPermission(
+        mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.READ_APP_SPECIFIC_LOCALES,
                 "getApplicationLocales");
     }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 21f61ca..c6f8975 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -301,8 +301,8 @@
         if (DEBUG) {
             Slog.d(TAG, this + ": Service binding died");
         }
+        unbind();
         if (shouldBind()) {
-            unbind();
             bind();
         }
     }
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index eb0b2bb..14f5214 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -344,6 +344,25 @@
 
     // Binder call
     @Override
+    public void setBluetoothA2dpOn(IMediaRouterClient client, boolean on) {
+        if (client == null) {
+            throw new IllegalArgumentException("client must not be null");
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                mAudioService.setBluetoothA2dpOn(on);
+            }
+        } catch (RemoteException ex) {
+            Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn. on=" + on);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Binder call
+    @Override
     public void setDiscoveryRequest(IMediaRouterClient client,
             int routeTypes, boolean activeScan) {
         if (client == null) {
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index 8e7c4ff..fd141bd7 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -306,6 +306,7 @@
                     return LOGGING_LEVEL_EVERYTHING;
                 }
                 if (mMode == MEDIA_METRICS_MODE_OFF) {
+                    Slog.v(TAG, "Logging level blocked: MEDIA_METRICS_MODE_OFF");
                     return LOGGING_LEVEL_BLOCKED;
                 }
 
@@ -326,6 +327,8 @@
                         mBlockList = getListLocked(PLAYER_METRICS_APP_BLOCKLIST);
                         if (mBlockList == null) {
                             // failed to get the blocklist. Block it.
+                            Slog.v(TAG, "Logging level blocked: Failed to get "
+                                    + "PLAYER_METRICS_APP_BLOCKLIST.");
                             return LOGGING_LEVEL_BLOCKED;
                         }
                     }
@@ -339,6 +342,8 @@
                                 getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST);
                         if (mNoUidBlocklist == null) {
                             // failed to get the blocklist. Block it.
+                            Slog.v(TAG, "Logging level blocked: Failed to get "
+                                    + "PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST.");
                             return LOGGING_LEVEL_BLOCKED;
                         }
                     }
@@ -358,6 +363,8 @@
                                 getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST);
                         if (mNoUidAllowlist == null) {
                             // failed to get the allowlist. Block it.
+                            Slog.v(TAG, "Logging level blocked: Failed to get "
+                                    + "PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST.");
                             return LOGGING_LEVEL_BLOCKED;
                         }
                     }
@@ -372,6 +379,8 @@
                         mAllowlist = getListLocked(PLAYER_METRICS_APP_ALLOWLIST);
                         if (mAllowlist == null) {
                             // failed to get the allowlist. Block it.
+                            Slog.v(TAG, "Logging level blocked: Failed to get "
+                                    + "PLAYER_METRICS_APP_ALLOWLIST.");
                             return LOGGING_LEVEL_BLOCKED;
                         }
                     }
@@ -381,10 +390,12 @@
                         return level;
                     }
                     // Not detected in any allowlist. Block.
+                    Slog.v(TAG, "Logging level blocked: Not detected in any allowlist.");
                     return LOGGING_LEVEL_BLOCKED;
                 }
             }
             // Blocked by default.
+            Slog.v(TAG, "Logging level blocked: Blocked by default.");
             return LOGGING_LEVEL_BLOCKED;
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b880a61..f701c2a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -479,6 +479,7 @@
     private ActivityManagerInternal mAmi;
     private IPackageManager mPackageManager;
     private PackageManager mPackageManagerClient;
+    private PackageManagerInternal mPackageManagerInternal;
     AudioManager mAudioManager;
     AudioManagerInternal mAudioManagerInternal;
     // Can be null for wear
@@ -2212,6 +2213,7 @@
         mUgmInternal = ugmInternal;
         mPackageManager = packageManager;
         mPackageManagerClient = packageManagerClient;
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mAppOps = appOps;
         mAppOpsService = iAppOps;
         try {
@@ -2292,10 +2294,12 @@
                 });
             }
         });
+        mPermissionHelper = permissionHelper;
         mPreferencesHelper = new PreferencesHelper(getContext(),
                 mPackageManagerClient,
                 mRankingHandler,
                 mZenModeHelper,
+                mPermissionHelper,
                 new NotificationChannelLoggerImpl(),
                 mAppOps,
                 new SysUiStatsEvent.BuilderFactory());
@@ -2309,7 +2313,6 @@
         mGroupHelper = groupHelper;
         mVibratorHelper = new VibratorHelper(getContext());
         mHistoryManager = historyManager;
-        mPermissionHelper = permissionHelper;
 
         // This is a ManagedServices object that keeps track of the listeners.
         mListeners = notificationListeners;
@@ -2821,6 +2824,12 @@
                 mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
 
         mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true);
+        if (mEnableAppSettingMigration) {
+            if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
+                mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
+                        channel.getImportance() != IMPORTANCE_NONE, true);
+            }
+        }
         maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
 
         if (!fromListener) {
@@ -3481,7 +3490,7 @@
 
                 mPreferencesHelper.setEnabled(pkg, uid, enabled);
                 // TODO (b/194833441): this is being ignored by app ops now that the permission
-                // exists
+                // exists, so send the broadcast manually
                 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
                         enabled ? MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
 
@@ -3541,11 +3550,7 @@
                         "canNotifyAsPackage for uid " + uid);
             }
 
-            if (mEnableAppSettingMigration) {
-                return mPermissionHelper.hasPermission(uid);
-            } else {
-                return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
-            }
+            return areNotificationsEnabledForPackageInt(pkg, uid);
         }
 
         /**
@@ -4082,15 +4087,13 @@
         }
 
         @Override
-        public int getAppsBypassingDndCount(int userId) {
-            checkCallerIsSystem();
-            return mPreferencesHelper.getAppsBypassingDndCount(userId);
-        }
-
-        @Override
         public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(
                 String pkg, int userId) {
             checkCallerIsSystem();
+            if (!areNotificationsEnabledForPackage(pkg,
+                    mPackageManagerInternal.getPackageUid(pkg, 0, userId))) {
+                return ParceledListSlice.emptyList();
+            }
             return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, userId);
         }
 
@@ -6731,13 +6734,30 @@
 
 
         // blocked apps
-        if (isBlocked(r, mUsageStats)) {
+        boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
+        synchronized (mNotificationLock) {
+            isBlocked |= isRecordBlockedLocked(r);
+        }
+        if (isBlocked) {
+            if (DBG) {
+                Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
+                        + " by user request.");
+            }
+            mUsageStats.registerBlocked(r);
             return false;
         }
 
         return true;
     }
 
+    private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) {
+        if (mEnableAppSettingMigration) {
+            return mPermissionHelper.hasPermission(uid);
+        } else {
+            return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
+        }
+    }
+
     protected int getNotificationCount(String pkg, int userId, int excludedId,
             String excludedTag) {
         int count = 0;
@@ -6766,24 +6786,15 @@
         return count;
     }
 
-    protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
-        if (isBlocked(r)) {
-            if (DBG) {
-                Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
-                        + " by user request.");
-            }
-            usageStats.registerBlocked(r);
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isBlocked(NotificationRecord r) {
+    /**
+     * Checks whether a notification is banned at a group or channel level or if the NAS or system
+     * has blocked the notification.
+     */
+    @GuardedBy("mNotificationLock")
+    boolean isRecordBlockedLocked(NotificationRecord r) {
         final String pkg = r.getSbn().getPackageName();
         final int callingUid = r.getSbn().getUid();
         return mPreferencesHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
-                || mPreferencesHelper.getImportance(pkg, callingUid)
-                == NotificationManager.IMPORTANCE_NONE
                 || r.getImportance() == NotificationManager.IMPORTANCE_NONE;
     }
 
@@ -7094,6 +7105,9 @@
 
         @Override
         public void run() {
+            String pkg = StatusBarNotification.getPkgFromKey(key);
+            int uid = StatusBarNotification.getUidFromKey(key);
+            boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid);
             synchronized (mNotificationLock) {
                 try {
                     NotificationRecord r = null;
@@ -7110,8 +7124,11 @@
                         return;
                     }
 
-                    if (isBlocked(r)) {
-                        Slog.i(TAG, "notification blocked by assistant request");
+                    if (appBanned || isRecordBlockedLocked(r)) {
+                        mUsageStats.registerBlocked(r);
+                        if (DBG) {
+                            Slog.e(TAG, "Suppressing notification from package " + pkg);
+                        }
                         return;
                     }
 
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 5e381ea..a3c009a 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -58,6 +58,10 @@
         mMigrationEnabled = migrationEnabled;
     }
 
+    public boolean isMigrationEnabled() {
+        return mMigrationEnabled;
+    }
+
     /**
      * Returns whether the given uid holds the notification permission. Must not be called
      * with a lock held.
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 6cf7d06..c74813a 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -169,6 +169,7 @@
     private final PackageManager mPm;
     private final RankingHandler mRankingHandler;
     private final ZenModeHelper mZenModeHelper;
+    private final PermissionHelper mPermissionHelper;
     private final NotificationChannelLogger mNotificationChannelLogger;
     private final AppOpsManager mAppOps;
 
@@ -187,12 +188,14 @@
     private int mCurrentUserId = UserHandle.USER_NULL;
 
     public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
-            ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger,
+            ZenModeHelper zenHelper, PermissionHelper permHelper,
+            NotificationChannelLogger notificationChannelLogger,
             AppOpsManager appOpsManager,
             SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) {
         mContext = context;
         mZenModeHelper = zenHelper;
         mRankingHandler = rankingHandler;
+        mPermissionHelper = permHelper;
         mPm = pm;
         mNotificationChannelLogger = notificationChannelLogger;
         mAppOps = appOpsManager;
@@ -791,6 +794,7 @@
         Objects.requireNonNull(group);
         Objects.requireNonNull(group.getId());
         Objects.requireNonNull(!TextUtils.isEmpty(group.getName()));
+        boolean needsDndChange = false;
         synchronized (mPackagePreferences) {
             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -809,7 +813,7 @@
                     // but the system can
                     if (group.isBlocked() != oldGroup.isBlocked()) {
                         group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
-                        updateChannelsBypassingDnd();
+                        needsDndChange = true;
                     }
                 }
             }
@@ -822,6 +826,9 @@
             }
             r.groups.put(group.getId(), group);
         }
+        if (needsDndChange) {
+            updateChannelsBypassingDnd();
+        }
     }
 
     @Override
@@ -831,7 +838,7 @@
         Objects.requireNonNull(channel);
         Objects.requireNonNull(channel.getId());
         Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
-        boolean needsPolicyFileChange = false, wasUndeleted = false;
+        boolean needsPolicyFileChange = false, wasUndeleted = false, needsDndChange = false;
         synchronized (mPackagePreferences) {
             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -897,7 +904,7 @@
 
                         if (bypassDnd != mAreChannelsBypassingDnd
                                 || previousExistingImportance != existing.getImportance()) {
-                            updateChannelsBypassingDnd();
+                            needsDndChange = true;
                         }
                     }
                 }
@@ -912,60 +919,64 @@
                     mNotificationChannelLogger.logNotificationChannelModified(existing, uid, pkg,
                             previousLoggingImportance, false);
                 }
-                return needsPolicyFileChange;
-            }
-
-            if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
-                throw new IllegalStateException("Limit exceed; cannot create more channels");
-            }
-
-            needsPolicyFileChange = true;
-
-            if (channel.getImportance() < IMPORTANCE_NONE
-                    || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
-                throw new IllegalArgumentException("Invalid importance level");
-            }
-
-            // Reset fields that apps aren't allowed to set.
-            if (fromTargetApp && !hasDndAccess) {
-                channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
-            }
-            if (fromTargetApp) {
-                channel.setLockscreenVisibility(r.visibility);
-                channel.setAllowBubbles(existing != null
-                        ? existing.getAllowBubbles()
-                        : NotificationChannel.DEFAULT_ALLOW_BUBBLE);
-            }
-            clearLockedFieldsLocked(channel);
-            channel.setImportanceLockedByOEM(r.oemLockedImportance);
-            if (!channel.isImportanceLockedByOEM()) {
-                if (r.oemLockedChannels.contains(channel.getId())) {
-                    channel.setImportanceLockedByOEM(true);
+            } else {
+                if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
+                    throw new IllegalStateException("Limit exceed; cannot create more channels");
                 }
-            }
-            channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
-            if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
-                channel.setLockscreenVisibility(
-                        NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
-            }
-            if (!r.showBadge) {
-                channel.setShowBadge(false);
-            }
-            channel.setOriginalImportance(channel.getImportance());
 
-            // validate parent
-            if (channel.getParentChannelId() != null) {
-                Preconditions.checkArgument(r.channels.containsKey(channel.getParentChannelId()),
-                        "Tried to create a conversation channel without a preexisting parent");
-            }
+                needsPolicyFileChange = true;
 
-            r.channels.put(channel.getId(), channel);
-            if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
-                updateChannelsBypassingDnd();
+                if (channel.getImportance() < IMPORTANCE_NONE
+                        || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
+                    throw new IllegalArgumentException("Invalid importance level");
+                }
+
+                // Reset fields that apps aren't allowed to set.
+                if (fromTargetApp && !hasDndAccess) {
+                    channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
+                }
+                if (fromTargetApp) {
+                    channel.setLockscreenVisibility(r.visibility);
+                    channel.setAllowBubbles(existing != null
+                            ? existing.getAllowBubbles()
+                            : NotificationChannel.DEFAULT_ALLOW_BUBBLE);
+                }
+                clearLockedFieldsLocked(channel);
+                channel.setImportanceLockedByOEM(r.oemLockedImportance);
+                if (!channel.isImportanceLockedByOEM()) {
+                    if (r.oemLockedChannels.contains(channel.getId())) {
+                        channel.setImportanceLockedByOEM(true);
+                    }
+                }
+                channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
+                if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+                    channel.setLockscreenVisibility(
+                            NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
+                }
+                if (!r.showBadge) {
+                    channel.setShowBadge(false);
+                }
+                channel.setOriginalImportance(channel.getImportance());
+
+                // validate parent
+                if (channel.getParentChannelId() != null) {
+                    Preconditions.checkArgument(
+                            r.channels.containsKey(channel.getParentChannelId()),
+                            "Tried to create a conversation channel without a preexisting parent");
+                }
+
+                r.channels.put(channel.getId(), channel);
+                if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
+                    needsDndChange = true;
+                }
+                MetricsLogger.action(getChannelLog(channel, pkg).setType(
+                        com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
+                mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
             }
-            MetricsLogger.action(getChannelLog(channel, pkg).setType(
-                    com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
-            mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg);
+        }
+
+        if (needsDndChange) {
+            updateChannelsBypassingDnd();
         }
 
         return needsPolicyFileChange;
@@ -997,6 +1008,7 @@
             boolean fromUser) {
         Objects.requireNonNull(updatedChannel);
         Objects.requireNonNull(updatedChannel.getId());
+        boolean needsDndChange = false;
         synchronized (mPackagePreferences) {
             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -1031,9 +1043,11 @@
             r.channels.put(updatedChannel.getId(), updatedChannel);
 
             if (onlyHasDefaultChannel(pkg, uid)) {
-                // copy settings to app level so they are inherited by new channels
-                // when the app migrates
-                r.importance = updatedChannel.getImportance();
+                if (!mPermissionHelper.isMigrationEnabled()) {
+                    // copy settings to app level so they are inherited by new channels
+                    // when the app migrates
+                    r.importance = updatedChannel.getImportance();
+                }
                 r.priority = updatedChannel.canBypassDnd()
                         ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
                 r.visibility = updatedChannel.getLockscreenVisibility();
@@ -1050,9 +1064,12 @@
 
             if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
                     || channel.getImportance() != updatedChannel.getImportance()) {
-                updateChannelsBypassingDnd();
+                needsDndChange = true;
             }
         }
+        if (needsDndChange) {
+            updateChannelsBypassingDnd();
+        }
         updateConfig();
     }
 
@@ -1126,6 +1143,8 @@
 
     @Override
     public boolean deleteNotificationChannel(String pkg, int uid, String channelId) {
+        boolean deletedChannel = false;
+        boolean channelBypassedDnd = false;
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -1133,13 +1152,18 @@
             }
             NotificationChannel channel = r.channels.get(channelId);
             if (channel != null) {
-                return deleteNotificationChannelLocked(channel, pkg, uid);
+                channelBypassedDnd = channel.canBypassDnd();
+                deletedChannel = deleteNotificationChannelLocked(channel, pkg, uid);
             }
-            return false;
         }
+        if (channelBypassedDnd) {
+            updateChannelsBypassingDnd();
+        }
+        return deletedChannel;
     }
 
-    private boolean deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
+    private boolean deleteNotificationChannelLocked(NotificationChannel channel, String pkg,
+            int uid) {
         if (!channel.isDeleted()) {
             channel.setDeleted(true);
             channel.setDeletedTimeMs(System.currentTimeMillis());
@@ -1147,10 +1171,6 @@
             lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
             MetricsLogger.action(lm);
             mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg);
-
-            if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
-                updateChannelsBypassingDnd();
-            }
             return true;
         }
         return false;
@@ -1352,6 +1372,7 @@
     public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
             String groupId) {
         List<NotificationChannel> deletedChannels = new ArrayList<>();
+        boolean groupBypassedDnd = false;
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null || TextUtils.isEmpty(groupId)) {
@@ -1368,11 +1389,15 @@
             for (int i = 0; i < N; i++) {
                 final NotificationChannel nc = r.channels.valueAt(i);
                 if (groupId.equals(nc.getGroup())) {
+                    groupBypassedDnd |= nc.canBypassDnd();
                     deleteNotificationChannelLocked(nc, pkg, uid);
                     deletedChannels.add(nc);
                 }
             }
         }
+        if (groupBypassedDnd) {
+            updateChannelsBypassingDnd();
+        }
         return deletedChannels;
     }
 
@@ -1495,8 +1520,8 @@
 
     public @NonNull List<String> deleteConversations(String pkg, int uid,
             Set<String> conversationIds) {
+        List<String> deletedChannelIds = new ArrayList<>();
         synchronized (mPackagePreferences) {
-            List<String> deletedChannelIds = new ArrayList<>();
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null) {
                 return deletedChannelIds;
@@ -1517,11 +1542,11 @@
                     deletedChannelIds.add(nc.getId());
                 }
             }
-            if (!deletedChannelIds.isEmpty() && mAreChannelsBypassingDnd) {
-                updateChannelsBypassingDnd();
-            }
-            return deletedChannelIds;
         }
+        if (!deletedChannelIds.isEmpty() && mAreChannelsBypassingDnd) {
+            updateChannelsBypassingDnd();
+        }
+        return deletedChannelIds;
     }
 
     @Override
@@ -1554,8 +1579,7 @@
         synchronized (mPackagePreferences) {
             final PackagePreferences r = mPackagePreferences.get(
                     packagePreferencesKey(pkg, userId));
-            // notifications from this package aren't blocked
-            if (r != null && r.importance != IMPORTANCE_NONE) {
+            if (r != null) {
                 for (NotificationChannel channel : r.channels.values()) {
                     if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
                         channels.add(channel);
@@ -1622,74 +1646,62 @@
     }
 
     /**
-     * Returns the number of apps that have at least one notification channel that can bypass DND
-     * for given particular user
-     */
-    public int getAppsBypassingDndCount(int userId) {
-        int count = 0;
-        synchronized (mPackagePreferences) {
-            final int numPackagePreferences = mPackagePreferences.size();
-            for (int i = 0; i < numPackagePreferences; i++) {
-                final PackagePreferences r = mPackagePreferences.valueAt(i);
-                // Package isn't associated with this userId or notifications from this package are
-                // blocked
-                if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
-                    continue;
-                }
-
-                for (NotificationChannel channel : r.channels.values()) {
-                    if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
-                        count++;
-                        break;
-                    }
-                }
-            }
-        }
-        return count;
-    }
-
-    /**
      * Syncs {@link #mAreChannelsBypassingDnd} with the current user's notification policy before
      * updating
      */
     private void syncChannelsBypassingDnd() {
         mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
                 & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
+
         updateChannelsBypassingDnd();
     }
 
     /**
      * Updates the user's NotificationPolicy based on whether the current userId
      * has channels bypassing DND
-     * @param userId
      */
     private void updateChannelsBypassingDnd() {
+        ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
+
         synchronized (mPackagePreferences) {
             final int numPackagePreferences = mPackagePreferences.size();
             for (int i = 0; i < numPackagePreferences; i++) {
                 final PackagePreferences r = mPackagePreferences.valueAt(i);
-                // Package isn't associated with the current userId or notifications from this
-                // package are blocked
-                if (mCurrentUserId != UserHandle.getUserId(r.uid)
-                        || r.importance == IMPORTANCE_NONE) {
+                // Package isn't associated with the current userId
+                if (mCurrentUserId != UserHandle.getUserId(r.uid)) {
                     continue;
                 }
 
                 for (NotificationChannel channel : r.channels.values()) {
                     if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
-                        if (!mAreChannelsBypassingDnd) {
-                            mAreChannelsBypassingDnd = true;
-                            updateZenPolicy(true);
-                        }
-                        return;
+                        candidatePkgs.add(new Pair(r.pkg, r.uid));
+                        break;
                     }
                 }
             }
         }
-        // If no channels bypass DND, update the zen policy once to disable DND bypass.
-        if (mAreChannelsBypassingDnd) {
-            mAreChannelsBypassingDnd = false;
-            updateZenPolicy(false);
+        for (int i = candidatePkgs.size() - 1; i >= 0; i--) {
+            Pair<String, Integer> app = candidatePkgs.valueAt(i);
+            if (mPermissionHelper.isMigrationEnabled()) {
+                if (!mPermissionHelper.hasPermission(app.second)) {
+                    candidatePkgs.removeAt(i);
+                }
+            } else {
+                synchronized (mPackagePreferences) {
+                    PackagePreferences r = getPackagePreferencesLocked(app.first, app.second);
+                    if (r == null) {
+                        continue;
+                    }
+                    if (r.importance == IMPORTANCE_NONE) {
+                        candidatePkgs.removeAt(i);
+                    }
+                }
+            }
+        }
+        boolean haveBypassingApps = candidatePkgs.size() > 0;
+        if (mAreChannelsBypassingDnd != haveBypassingApps) {
+            mAreChannelsBypassingDnd = haveBypassingApps;
+            updateZenPolicy(mAreChannelsBypassingDnd);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 8387482..9696d3d 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -62,12 +62,21 @@
     private final PackageManagerService mPm;
     private final Installer mInstaller;
     private final ArtManagerService mArtManagerService;
+    private final PackageManagerServiceInjector mInjector;
 
     // TODO(b/198166813): remove PMS dependency
     AppDataHelper(PackageManagerService pm) {
         mPm = pm;
-        mInstaller = mPm.mInjector.getInstaller();
-        mArtManagerService = mPm.mInjector.getArtManagerService();
+        mInjector = mPm.mInjector;
+        mInstaller = mInjector.getInstaller();
+        mArtManagerService = mInjector.getArtManagerService();
+    }
+
+    AppDataHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
+        mPm = pm;
+        mInjector = injector;
+        mInstaller = injector.getInstaller();
+        mArtManagerService = injector.getArtManagerService();
     }
 
     /**
@@ -90,8 +99,8 @@
         }
 
         Installer.Batch batch = new Installer.Batch();
-        UserManagerInternal umInternal = mPm.mInjector.getUserManagerInternal();
-        StorageManagerInternal smInternal = mPm.mInjector.getLocalService(
+        UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+        StorageManagerInternal smInternal = mInjector.getLocalService(
                 StorageManagerInternal.class);
         for (UserInfo user : umInternal.getUsers(false /*excludeDying*/)) {
             final int flags;
@@ -233,7 +242,8 @@
                     // #performDexOptUpgrade. When we do that we should have a
                     // more granular check here and only update the existing
                     // profiles.
-                    if (mPm.mIsUpgrade || mPm.mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
+                    if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
+                            || (userId != UserHandle.USER_SYSTEM)) {
                         mArtManagerService.prepareAppProfiles(pkg, userId,
                                 /* updateReferenceProfileContent= */ false);
                     }
@@ -313,7 +323,7 @@
      */
     @NonNull
     public void reconcileAppsData(int userId, int flags, boolean migrateAppsData) {
-        final StorageManager storage = mPm.mInjector.getSystemService(StorageManager.class);
+        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
             final String volumeUuid = vol.getFsUuid();
             synchronized (mPm.mInstallLock) {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 1e8e43d..d86a4dc 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -17,6 +17,8 @@
 package com.android.server.pm;
 
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_NULL;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
@@ -24,6 +26,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
@@ -624,7 +627,7 @@
             Set<String> protectedBroadcasts) {
         List<ParsedIntentInfo> intents = component.getIntents();
         for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) {
-            IntentFilter intentFilter = intents.get(i);
+            IntentFilter intentFilter = intents.get(i).getIntentFilter();
             if (matchesIntentFilter(intent, intentFilter, protectedBroadcasts)) {
                 return true;
             }
@@ -701,7 +704,7 @@
                 synchronized (mCacheLock) {
                     if (mShouldFilterCache != null) {
                         updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting,
-                                settings, users, settings.size());
+                                settings, users, USER_ALL, settings.size());
                         if (additionalChangedPackages != null) {
                             for (int index = 0; index < additionalChangedPackages.size(); index++) {
                                 String changedPackage = additionalChangedPackages.valueAt(index);
@@ -714,7 +717,8 @@
                                 }
 
                                 updateShouldFilterCacheForPackage(mShouldFilterCache, null,
-                                        changedPkgSetting, settings, users, settings.size());
+                                        changedPkgSetting, settings, users, USER_ALL,
+                                        settings.size());
                             }
                         }
                     } // else, rebuild entire cache when system is ready
@@ -851,9 +855,25 @@
     }
 
     private void updateEntireShouldFilterCache() {
+        updateEntireShouldFilterCache(USER_ALL);
+    }
+
+    private void updateEntireShouldFilterCache(int subjectUserId) {
         mStateProvider.runWithState((settings, users) -> {
+            int userId = USER_NULL;
+            for (int u = 0; u < users.length; u++) {
+                if (subjectUserId == users[u].id) {
+                    userId = subjectUserId;
+                    break;
+                }
+            }
+            if (userId == USER_NULL) {
+                Slog.e(TAG, "We encountered a new user that isn't a member of known users, "
+                        + "updating the whole cache");
+                userId = USER_ALL;
+            }
             WatchedSparseBooleanMatrix cache =
-                    updateEntireShouldFilterCacheInner(settings, users);
+                    updateEntireShouldFilterCacheInner(settings, users, userId);
             synchronized (mCacheLock) {
                 mShouldFilterCache = cache;
             }
@@ -861,12 +881,19 @@
     }
 
     private WatchedSparseBooleanMatrix updateEntireShouldFilterCacheInner(
-            ArrayMap<String, PackageSetting> settings, UserInfo[] users) {
-        WatchedSparseBooleanMatrix cache =
-                new WatchedSparseBooleanMatrix(users.length * settings.size());
+            ArrayMap<String, PackageSetting> settings, UserInfo[] users, int subjectUserId) {
+        final WatchedSparseBooleanMatrix cache;
+        if (subjectUserId == USER_ALL) {
+            cache = new WatchedSparseBooleanMatrix(users.length * settings.size());
+        } else {
+            synchronized (mCacheLock) {
+                cache = mShouldFilterCache.snapshot();
+            }
+            cache.setCapacity(users.length * settings.size());
+        }
         for (int i = settings.size() - 1; i >= 0; i--) {
             updateShouldFilterCacheForPackage(cache,
-                    null /*skipPackage*/, settings.valueAt(i), settings, users, i);
+                    null /*skipPackage*/, settings.valueAt(i), settings, users, subjectUserId, i);
         }
         return cache;
     }
@@ -887,8 +914,8 @@
                     packagesCache.put(settings.keyAt(i), pkg);
                 }
             });
-            WatchedSparseBooleanMatrix cache =
-                    updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0]);
+            WatchedSparseBooleanMatrix cache = updateEntireShouldFilterCacheInner(
+                    settingsCopy, usersRef[0], USER_ALL);
             boolean[] changed = new boolean[1];
             // We have a cache, let's make sure the world hasn't changed out from under us.
             mStateProvider.runWithState((settings, users) -> {
@@ -918,10 +945,19 @@
         });
     }
 
-    public void onUsersChanged() {
+    public void onUserCreated(int newUserId) {
         synchronized (mCacheLock) {
             if (mShouldFilterCache != null) {
-                updateEntireShouldFilterCache();
+                updateEntireShouldFilterCache(newUserId);
+                onChanged();
+            }
+        }
+    }
+
+    public void onUserDeleted(@UserIdInt int userId) {
+        synchronized (mCacheLock) {
+            if (mShouldFilterCache != null) {
+                removeShouldFilterCacheForUser(userId);
                 onChanged();
             }
         }
@@ -934,7 +970,7 @@
                     return;
                 }
                 updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */,
-                        settings.get(packageName), settings, users,
+                        settings.get(packageName), settings, users, USER_ALL,
                         settings.size() /*maxIndex*/);
             }
         });
@@ -942,7 +978,7 @@
 
     private void updateShouldFilterCacheForPackage(WatchedSparseBooleanMatrix cache,
             @Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String,
-            PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) {
+            PackageSetting> allSettings, UserInfo[] allUsers, int subjectUserId, int maxIndex) {
         for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
             PackageSetting otherSetting = allSettings.valueAt(i);
             if (subjectSetting.getAppId() == otherSetting.getAppId()) {
@@ -953,25 +989,57 @@
                     == skipPackageName) {
                 continue;
             }
-            final int userCount = allUsers.length;
-            final int appxUidCount = userCount * allSettings.size();
-            for (int su = 0; su < userCount; su++) {
-                int subjectUser = allUsers[su].id;
-                for (int ou = 0; ou < userCount; ou++) {
-                    int otherUser = allUsers[ou].id;
-                    int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.getAppId());
-                    int otherUid = UserHandle.getUid(otherUser, otherSetting.getAppId());
-                    cache.put(subjectUid, otherUid,
-                            shouldFilterApplicationInternal(
-                                    subjectUid, subjectSetting, otherSetting, otherUser));
-                    cache.put(otherUid, subjectUid,
-                            shouldFilterApplicationInternal(
-                                    otherUid, otherSetting, subjectSetting, subjectUser));
+            if (subjectUserId == USER_ALL) {
+                for (int su = 0; su < allUsers.length; su++) {
+                    updateShouldFilterCacheForUser(cache, subjectSetting, allUsers, otherSetting,
+                            allUsers[su].id);
                 }
+            } else {
+                updateShouldFilterCacheForUser(cache, subjectSetting, allUsers, otherSetting,
+                        subjectUserId);
             }
         }
     }
 
+    private void updateShouldFilterCacheForUser(WatchedSparseBooleanMatrix cache,
+            PackageSetting subjectSetting, UserInfo[] allUsers, PackageSetting otherSetting,
+            int subjectUserId) {
+        for (int ou = 0; ou < allUsers.length; ou++) {
+            int otherUser = allUsers[ou].id;
+            int subjectUid = UserHandle.getUid(subjectUserId, subjectSetting.getAppId());
+            int otherUid = UserHandle.getUid(otherUser, otherSetting.getAppId());
+            cache.put(subjectUid, otherUid,
+                    shouldFilterApplicationInternal(
+                            subjectUid, subjectSetting, otherSetting, otherUser));
+            cache.put(otherUid, subjectUid,
+                    shouldFilterApplicationInternal(
+                            otherUid, otherSetting, subjectSetting, subjectUserId));
+        }
+    }
+
+    @GuardedBy("mCacheLock")
+    private void removeShouldFilterCacheForUser(int userId) {
+        // Sorted uids with the ascending order
+        final int[] cacheUids = mShouldFilterCache.keys();
+        final int size = cacheUids.length;
+        int pos = Arrays.binarySearch(cacheUids, UserHandle.getUid(userId, 0));
+        final int fromIndex = (pos >= 0 ? pos : ~pos);
+        if (fromIndex >= size || UserHandle.getUserId(cacheUids[fromIndex]) != userId) {
+            Slog.w(TAG, "Failed to remove should filter cache for user " + userId
+                    + ", fromIndex=" + fromIndex);
+            return;
+        }
+        pos = Arrays.binarySearch(cacheUids, UserHandle.getUid(userId + 1, 0) - 1);
+        final int toIndex = (pos >= 0 ? pos + 1 : ~pos);
+        if (fromIndex >= toIndex || UserHandle.getUserId(cacheUids[toIndex - 1]) != userId) {
+            Slog.w(TAG, "Failed to remove should filter cache for user " + userId
+                    + ", fromIndex=" + fromIndex + ", toIndex=" + toIndex);
+            return;
+        }
+        mShouldFilterCache.removeRange(fromIndex, toIndex);
+        mShouldFilterCache.compact();
+    }
+
     private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
             PackageSetting pkgSetting) {
         return pkgSetting.isSystem()
@@ -1181,8 +1249,8 @@
                             continue;
                         }
                         updateShouldFilterCacheForPackage(mShouldFilterCache,
-                                setting.getPackageName(),
-                                siblingSetting, settings, users, settings.size());
+                                setting.getPackageName(), siblingSetting, settings, users,
+                                USER_ALL, settings.size());
                     }
                 }
 
@@ -1199,7 +1267,7 @@
                             }
 
                             updateShouldFilterCacheForPackage(mShouldFilterCache, null,
-                                    changedPkgSetting, settings, users, settings.size());
+                                    changedPkgSetting, settings, users, USER_ALL, settings.size());
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 44e8e66..af9c401 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -251,13 +251,15 @@
                 resetStatesForNewDexOptRunLocked(Thread.currentThread());
             }
             PackageManagerService pm = mInjector.getPackageManagerService();
+            DexOptHelper dexOptHelper = new DexOptHelper(pm);
             ArraySet<String> packagesToOptimize;
             if (packageNames == null) {
-                packagesToOptimize = pm.getOptimizablePackages();
+                packagesToOptimize = dexOptHelper.getOptimizablePackages();
             } else {
                 packagesToOptimize = new ArraySet<>(packageNames);
             }
-            return runIdleOptimization(pm, packagesToOptimize, /* isPostBootUpdate= */ false);
+            return runIdleOptimization(pm, dexOptHelper, packagesToOptimize,
+                    /* isPostBootUpdate= */ false);
         } finally {
             Binder.restoreCallingIdentity(identity);
             markDexOptCompleted();
@@ -318,7 +320,8 @@
             return false;
         }
 
-        ArraySet<String> pkgs = pm.getOptimizablePackages();
+        DexOptHelper dexOptHelper = new DexOptHelper(pm);
+        ArraySet<String> pkgs = dexOptHelper.getOptimizablePackages();
         if (pkgs.isEmpty()) {
             Slog.i(TAG, "No packages to optimize");
             markPostBootUpdateCompleted(params);
@@ -344,7 +347,7 @@
                         tr.traceBegin("jobExecution");
                         boolean completed = false;
                         try {
-                            completed = runIdleOptimization(pm, pkgs,
+                            completed = runIdleOptimization(pm, dexOptHelper, pkgs,
                                     params.getJobId() == JOB_POST_BOOT_UPDATE);
                         } finally { // Those cleanup should be done always.
                             tr.traceEnd();
@@ -457,7 +460,8 @@
 
     @GuardedBy("mLock")
     private void controlDexOptBlockingLocked(boolean block) {
-        mInjector.getPackageManagerService().controlDexOptBlocking(block);
+        PackageManagerService pm = mInjector.getPackageManagerService();
+        new DexOptHelper(pm).controlDexOptBlocking(block);
     }
 
     private void scheduleAJob(int jobId) {
@@ -507,10 +511,11 @@
     }
 
     /** Returns true if completed */
-    private boolean runIdleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
-            boolean isPostBootUpdate) {
+    private boolean runIdleOptimization(PackageManagerService pm, DexOptHelper dexOptHelper,
+            ArraySet<String> pkgs, boolean isPostBootUpdate) {
         long lowStorageThreshold = getLowStorageThreshold();
-        int status = idleOptimizePackages(pm, pkgs, lowStorageThreshold, isPostBootUpdate);
+        int status = idleOptimizePackages(pm, dexOptHelper, pkgs, lowStorageThreshold,
+                isPostBootUpdate);
         logStatus(status);
         synchronized (mLock) {
             mLastExecutionStatus = status;
@@ -557,8 +562,8 @@
     }
 
     @Status
-    private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
-            long lowStorageThreshold, boolean isPostBootUpdate) {
+    private int idleOptimizePackages(PackageManagerService pm, DexOptHelper dexOptHelper,
+            ArraySet<String> pkgs, long lowStorageThreshold, boolean isPostBootUpdate) {
         ArraySet<String> updatedPackages = new ArraySet<>();
         ArraySet<String> updatedPackagesDueToSecondaryDex = new ArraySet<>();
 
@@ -595,7 +600,7 @@
                             // Should be aborted by the scheduler.
                             return abortCode;
                         }
-                        @DexOptResult int downgradeResult = downgradePackage(pm, pkg,
+                        @DexOptResult int downgradeResult = downgradePackage(pm, dexOptHelper, pkg,
                                 /* isForPrimaryDex= */ true, isPostBootUpdate);
                         if (downgradeResult == PackageDexOptimizer.DEX_OPT_PERFORMED) {
                             updatedPackages.add(pkg);
@@ -606,7 +611,7 @@
                             return status;
                         }
                         if (supportSecondaryDex) {
-                            downgradeResult = downgradePackage(pm, pkg,
+                            downgradeResult = downgradePackage(pm, dexOptHelper, pkg,
                                     /* isForPrimaryDex= */false, isPostBootUpdate);
                             status = convertPackageDexOptimizerStatusToInternal(downgradeResult);
                             if (status != STATUS_OK) {
@@ -620,8 +625,9 @@
                 }
             }
 
-            @Status int primaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
-                    /*isForPrimaryDex=*/ true, updatedPackages, isPostBootUpdate);
+            @Status int primaryResult = optimizePackages(dexOptHelper, pkgs,
+                    lowStorageThreshold, /*isForPrimaryDex=*/ true, updatedPackages,
+                    isPostBootUpdate);
             if (primaryResult != STATUS_OK) {
                 return primaryResult;
             }
@@ -630,9 +636,9 @@
                 return STATUS_OK;
             }
 
-            @Status int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
-                    /*isForPrimaryDex*/ false, updatedPackagesDueToSecondaryDex,
-                    isPostBootUpdate);
+            @Status int secondaryResult = optimizePackages(dexOptHelper, pkgs,
+                    lowStorageThreshold, /*isForPrimaryDex*/ false,
+                    updatedPackagesDueToSecondaryDex, isPostBootUpdate);
             return secondaryResult;
         } finally {
             // Always let the pinner service know about changes.
@@ -645,9 +651,9 @@
     }
 
     @Status
-    private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
-            long lowStorageThreshold, boolean isForPrimaryDex, ArraySet<String> updatedPackages,
-            boolean isPostBootUpdate) {
+    private int optimizePackages(DexOptHelper dexOptHelper,
+            ArraySet<String> pkgs, long lowStorageThreshold, boolean isForPrimaryDex,
+            ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
         for (String pkg : pkgs) {
             int abortCode = abortIdleOptimizations(lowStorageThreshold);
             if (abortCode != STATUS_OK) {
@@ -655,7 +661,8 @@
                 return abortCode;
             }
 
-            @DexOptResult int result = optimizePackage(pm, pkg, isForPrimaryDex, isPostBootUpdate);
+            @DexOptResult int result = optimizePackage(dexOptHelper, pkg, isForPrimaryDex,
+                    isPostBootUpdate);
             if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
                 updatedPackages.add(pkg);
             } else if (result != PackageDexOptimizer.DEX_OPT_SKIPPED) {
@@ -674,7 +681,7 @@
      * @return PackageDexOptimizer.DEX_*
      */
     @DexOptResult
-    private int downgradePackage(PackageManagerService pm, String pkg,
+    private int downgradePackage(PackageManagerService pm, DexOptHelper dexOptHelper, String pkg,
             boolean isForPrimaryDex, boolean isPostBootUpdate) {
         if (DEBUG) {
             Slog.d(TAG, "Downgrading " + pkg);
@@ -697,10 +704,10 @@
                 // remove their compiler artifacts from dalvik cache.
                 pm.deleteOatArtifactsOfPackage(pkg);
             } else {
-                result = performDexOptPrimary(pm, pkg, reason, dexoptFlags);
+                result = performDexOptPrimary(dexOptHelper, pkg, reason, dexoptFlags);
             }
         } else {
-            result = performDexOptSecondary(pm, pkg, reason, dexoptFlags);
+            result = performDexOptSecondary(dexOptHelper, pkg, reason, dexoptFlags);
         }
 
         if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -726,14 +733,14 @@
      *
      * Optimize package if needed. Note that there can be no race between
      * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
-     * @param pm An instance of PackageManagerService
+     * @param dexOptHelper An instance of DexOptHelper
      * @param pkg The package to be downgraded.
      * @param isForPrimaryDex Apps can have several dex file, primary and secondary.
      * @param isPostBootUpdate is post boot update or not.
      * @return PackageDexOptimizer#DEX_OPT_*
      */
     @DexOptResult
-    private int optimizePackage(PackageManagerService pm, String pkg,
+    private int optimizePackage(DexOptHelper dexOptHelper, String pkg,
             boolean isForPrimaryDex, boolean isPostBootUpdate) {
         int reason = isPostBootUpdate ? PackageManagerService.REASON_POST_BOOT
                 : PackageManagerService.REASON_BACKGROUND_DEXOPT;
@@ -746,26 +753,27 @@
         // System server share the same code path as primary dex files.
         // PackageManagerService will select the right optimization path for it.
         if (isForPrimaryDex || PLATFORM_PACKAGE_NAME.equals(pkg)) {
-            return performDexOptPrimary(pm, pkg, reason, dexoptFlags);
+            return performDexOptPrimary(dexOptHelper, pkg, reason, dexoptFlags);
         } else {
-            return performDexOptSecondary(pm, pkg, reason, dexoptFlags);
+            return performDexOptSecondary(dexOptHelper, pkg, reason, dexoptFlags);
         }
     }
 
     @DexOptResult
-    private int performDexOptPrimary(PackageManagerService pm, String pkg, int reason,
+    private int performDexOptPrimary(DexOptHelper dexOptHelper, String pkg, int reason,
             int dexoptFlags) {
         return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ true,
-                () -> pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags)));
+                () -> dexOptHelper.performDexOptWithStatus(
+                        new DexoptOptions(pkg, reason, dexoptFlags)));
     }
 
     @DexOptResult
-    private int performDexOptSecondary(PackageManagerService pm, String pkg, int reason,
+    private int performDexOptSecondary(DexOptHelper dexOptHelper, String pkg, int reason,
             int dexoptFlags) {
         DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason,
                 dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX);
         return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ false,
-                () -> pm.performDexOpt(dexoptOptions)
+                () -> dexOptHelper.performDexOpt(dexoptOptions)
                     ? PackageDexOptimizer.DEX_OPT_PERFORMED : PackageDexOptimizer.DEX_OPT_FAILED
         );
     }
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index d6b9c34..9251f65 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -36,11 +36,13 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.parsing.component.ComponentMutateUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedProviderImpl;
 import android.content.pm.parsing.component.ParsedService;
 import android.content.pm.pkg.PackageUserState;
 import android.os.UserHandle;
@@ -588,7 +590,8 @@
             for (int i = protectedFilters.size() - 1; i >= 0; --i) {
                 final Pair<ParsedMainComponent, ParsedIntentInfo> pair = protectedFilters.get(i);
                 ParsedMainComponent component = pair.first;
-                ParsedIntentInfo filter = pair.second;
+                ParsedIntentInfo intentInfo = pair.second;
+                IntentFilter filter = intentInfo.getIntentFilter();
                 String packageName = component.getPackageName();
                 String className = component.getClassName();
                 if (packageName.equals(setupWizardPackage)) {
@@ -744,7 +747,7 @@
                 String[] names = p.getAuthority().split(";");
 
                 // TODO(b/135203078): Remove this mutation
-                p.setAuthority(null);
+                ComponentMutateUtils.setAuthority(p, null);
                 for (int j = 0; j < names.length; j++) {
                     if (j == 1 && p.isSyncable()) {
                         // We only want the first authority for a provider to possibly be
@@ -754,15 +757,15 @@
                         // to a provider that we don't want to change.
                         // Only do this for the second authority since the resulting provider
                         // object can be the same for all future authorities for this provider.
-                        p = new ParsedProvider(p);
-                        p.setSyncable(false);
+                        p = new ParsedProviderImpl(p);
+                        ComponentMutateUtils.setSyncable(p, false);
                     }
                     if (!mProvidersByAuthority.containsKey(names[j])) {
                         mProvidersByAuthority.put(names[j], p);
                         if (p.getAuthority() == null) {
-                            p.setAuthority(names[j]);
+                            ComponentMutateUtils.setAuthority(p, names[j]);
                         } else {
-                            p.setAuthority(p.getAuthority() + ";" + names[j]);
+                            ComponentMutateUtils.setAuthority(p, p.getAuthority() + ";" + names[j]);
                         }
                         if (DEBUG_PACKAGE_SCANNING && chatty) {
                             Log.d(TAG, "Registered content provider: " + names[j]
@@ -844,7 +847,7 @@
      * MODIFIED. Do not pass in a list that should not be changed.
      */
     private static <T> void getIntentListSubset(List<ParsedIntentInfo> intentList,
-            Function<ParsedIntentInfo, Iterator<T>> generator, Iterator<T> searchIterator) {
+            Function<IntentFilter, Iterator<T>> generator, Iterator<T> searchIterator) {
         // loop through the set of actions; every one must be found in the intent filter
         while (searchIterator.hasNext()) {
             // we must have at least one filter in the list to consider a match
@@ -862,7 +865,8 @@
 
                 // loop through the intent filter's selection criteria; at least one
                 // of them must match the searched criteria
-                final Iterator<T> intentSelectionIter = generator.apply(intentInfo);
+                final Iterator<T> intentSelectionIter =
+                        generator.apply(intentInfo.getIntentFilter());
                 while (intentSelectionIter != null && intentSelectionIter.hasNext()) {
                     final T intentSelection = intentSelectionIter.next();
                     if (intentSelection != null && intentSelection.equals(searchAction)) {
@@ -880,7 +884,7 @@
         }
     }
 
-    private static boolean isProtectedAction(ParsedIntentInfo filter) {
+    private static boolean isProtectedAction(IntentFilter filter) {
         final Iterator<String> actionsIter = filter.actionsIterator();
         while (actionsIter != null && actionsIter.hasNext()) {
             final String filterAction = actionsIter.next();
@@ -929,9 +933,10 @@
      * allowed to obtain any priority on any action.
      */
     private void adjustPriority(List<ParsedActivity> systemActivities, ParsedActivity activity,
-            ParsedIntentInfo intent, String setupWizardPackage) {
+            ParsedIntentInfo intentInfo, String setupWizardPackage) {
         // nothing to do; priority is fine as-is
-        if (intent.getPriority() <= 0) {
+        IntentFilter intentFilter = intentInfo.getIntentFilter();
+        if (intentFilter.getPriority() <= 0) {
             return;
         }
 
@@ -946,13 +951,13 @@
                 Slog.i(TAG, "Non-privileged app; cap priority to 0;"
                         + " package: " + packageName
                         + " activity: " + className
-                        + " origPrio: " + intent.getPriority());
+                        + " origPrio: " + intentFilter.getPriority());
             }
-            intent.setPriority(0);
+            intentFilter.setPriority(0);
             return;
         }
 
-        if (isProtectedAction(intent)) {
+        if (isProtectedAction(intentFilter)) {
             if (mDeferProtectedFilters) {
                 // We can't deal with these just yet. No component should ever obtain a
                 // >0 priority for a protected actions, with ONE exception -- the setup
@@ -964,12 +969,12 @@
                 if (mProtectedFilters == null) {
                     mProtectedFilters = new ArrayList<>();
                 }
-                mProtectedFilters.add(Pair.create(activity, intent));
+                mProtectedFilters.add(Pair.create(activity, intentInfo));
                 if (DEBUG_FILTERS) {
                     Slog.i(TAG, "Protected action; save for later;"
                             + " package: " + packageName
                             + " activity: " + className
-                            + " origPrio: " + intent.getPriority());
+                            + " origPrio: " + intentFilter.getPriority());
                 }
             } else {
                 if (DEBUG_FILTERS && setupWizardPackage == null) {
@@ -979,10 +984,10 @@
                 if (packageName.equals(setupWizardPackage)) {
                     if (DEBUG_FILTERS) {
                         Slog.i(TAG, "Found setup wizard;"
-                                + " allow priority " + intent.getPriority() + ";"
+                                + " allow priority " + intentFilter.getPriority() + ";"
                                 + " package: " + packageName
                                 + " activity: " + className
-                                + " priority: " + intent.getPriority());
+                                + " priority: " + intentFilter.getPriority());
                     }
                     // setup wizard gets whatever it wants
                     return;
@@ -991,9 +996,9 @@
                     Slog.i(TAG, "Protected action; cap priority to 0;"
                             + " package: " + packageName
                             + " activity: " + className
-                            + " origPrio: " + intent.getPriority());
+                            + " origPrio: " + intentFilter.getPriority());
                 }
-                intent.setPriority(0);
+                intentFilter.setPriority(0);
             }
             return;
         }
@@ -1014,9 +1019,9 @@
                 Slog.i(TAG, "New activity; cap priority to 0;"
                         + " package: " + packageName
                         + " activity: " + className
-                        + " origPrio: " + intent.getPriority());
+                        + " origPrio: " + intentFilter.getPriority());
             }
-            intent.setPriority(0);
+            intentFilter.setPriority(0);
             return;
         }
 
@@ -1027,7 +1032,7 @@
                 new ArrayList<>(foundActivity.getIntents());
 
         // find matching action subsets
-        final Iterator<String> actionsIterator = intent.actionsIterator();
+        final Iterator<String> actionsIterator = intentFilter.actionsIterator();
         if (actionsIterator != null) {
             getIntentListSubset(intentListCopy, IntentFilter::actionsIterator, actionsIterator);
             if (intentListCopy.size() == 0) {
@@ -1036,15 +1041,15 @@
                     Slog.i(TAG, "Mismatched action; cap priority to 0;"
                             + " package: " + packageName
                             + " activity: " + className
-                            + " origPrio: " + intent.getPriority());
+                            + " origPrio: " + intentFilter.getPriority());
                 }
-                intent.setPriority(0);
+                intentFilter.setPriority(0);
                 return;
             }
         }
 
         // find matching category subsets
-        final Iterator<String> categoriesIterator = intent.categoriesIterator();
+        final Iterator<String> categoriesIterator = intentFilter.categoriesIterator();
         if (categoriesIterator != null) {
             getIntentListSubset(intentListCopy, IntentFilter::categoriesIterator,
                     categoriesIterator);
@@ -1054,15 +1059,15 @@
                     Slog.i(TAG, "Mismatched category; cap priority to 0;"
                             + " package: " + packageName
                             + " activity: " + className
-                            + " origPrio: " + intent.getPriority());
+                            + " origPrio: " + intentFilter.getPriority());
                 }
-                intent.setPriority(0);
+                intentFilter.setPriority(0);
                 return;
             }
         }
 
         // find matching schemes subsets
-        final Iterator<String> schemesIterator = intent.schemesIterator();
+        final Iterator<String> schemesIterator = intentFilter.schemesIterator();
         if (schemesIterator != null) {
             getIntentListSubset(intentListCopy, IntentFilter::schemesIterator, schemesIterator);
             if (intentListCopy.size() == 0) {
@@ -1071,16 +1076,16 @@
                     Slog.i(TAG, "Mismatched scheme; cap priority to 0;"
                             + " package: " + packageName
                             + " activity: " + className
-                            + " origPrio: " + intent.getPriority());
+                            + " origPrio: " + intentFilter.getPriority());
                 }
-                intent.setPriority(0);
+                intentFilter.setPriority(0);
                 return;
             }
         }
 
         // find matching authorities subsets
         final Iterator<IntentFilter.AuthorityEntry> authoritiesIterator =
-                intent.authoritiesIterator();
+                intentFilter.authoritiesIterator();
         if (authoritiesIterator != null) {
             getIntentListSubset(intentListCopy, IntentFilter::authoritiesIterator,
                     authoritiesIterator);
@@ -1090,9 +1095,9 @@
                     Slog.i(TAG, "Mismatched authority; cap priority to 0;"
                             + " package: " + packageName
                             + " activity: " + className
-                            + " origPrio: " + intent.getPriority());
+                            + " origPrio: " + intentFilter.getPriority());
                 }
-                intent.setPriority(0);
+                intentFilter.setPriority(0);
                 return;
             }
         }
@@ -1100,17 +1105,18 @@
         // we found matching filter(s); app gets the max priority of all intents
         int cappedPriority = 0;
         for (int i = intentListCopy.size() - 1; i >= 0; --i) {
-            cappedPriority = Math.max(cappedPriority, intentListCopy.get(i).getPriority());
+            cappedPriority = Math.max(cappedPriority,
+                    intentListCopy.get(i).getIntentFilter().getPriority());
         }
-        if (intent.getPriority() > cappedPriority) {
+        if (intentFilter.getPriority() > cappedPriority) {
             if (DEBUG_FILTERS) {
                 Slog.i(TAG, "Found matching filter(s);"
                         + " cap priority to " + cappedPriority + ";"
                         + " package: " + packageName
                         + " activity: " + className
-                        + " origPrio: " + intent.getPriority());
+                        + " origPrio: " + intentFilter.getPriority());
             }
-            intent.setPriority(cappedPriority);
+            intentFilter.setPriority(cappedPriority);
             return;
         }
         // all this for nothing; the requested priority was <= what was on the system
@@ -1425,14 +1431,15 @@
             final int intentsSize = a.getIntents().size();
             for (int j = 0; j < intentsSize; j++) {
                 ParsedIntentInfo intent = a.getIntents().get(j);
+                IntentFilter intentFilter = intent.getIntentFilter();
                 if (newIntents != null && "activity".equals(type)) {
                     newIntents.add(Pair.create(a, intent));
                 }
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
-                    intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
+                    intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
-                if (!intent.debugCheck()) {
+                if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Activity " + a.getName());
                 }
                 addFilter(Pair.create(a, intent));
@@ -1448,9 +1455,10 @@
             final int intentsSize = a.getIntents().size();
             for (int j = 0; j < intentsSize; j++) {
                 ParsedIntentInfo intent = a.getIntents().get(j);
+                IntentFilter intentFilter = intent.getIntentFilter();
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
-                    intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
+                    intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
                 removeFilter(Pair.create(a, intent));
             }
@@ -1500,6 +1508,7 @@
                 int match, int userId) {
             ParsedActivity activity = pair.first;
             ParsedIntentInfo info = pair.second;
+            IntentFilter intentFilter = info.getIntentFilter();
 
             if (!sUserManager.exists(userId)) {
                 if (DEBUG) {
@@ -1545,8 +1554,9 @@
                     (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
             final boolean componentVisible =
                     matchVisibleToInstantApp
-                    && info.isVisibleToInstantApp()
-                    && (!matchExplicitlyVisibleOnly || info.isExplicitlyVisibleToInstantApp());
+                    && intentFilter.isVisibleToInstantApp()
+                    && (!matchExplicitlyVisibleOnly
+                            || intentFilter.isExplicitlyVisibleToInstantApp());
             final boolean matchInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to ephemeral apps
             if (matchVisibleToInstantApp && !(componentVisible || userState.isInstantApp())) {
@@ -1554,10 +1564,11 @@
                     log("Filter(s) not visible to ephemeral apps"
                             + "; matchVisibleToInstantApp=" + matchVisibleToInstantApp
                             + "; matchInstantApp=" + matchInstantApp
-                            + "; info.isVisibleToInstantApp()=" + info.isVisibleToInstantApp()
+                            + "; info.isVisibleToInstantApp()="
+                                    + intentFilter.isVisibleToInstantApp()
                             + "; matchExplicitlyVisibleOnly=" + matchExplicitlyVisibleOnly
                             + "; info.isExplicitlyVisibleToInstantApp()="
-                                    + info.isExplicitlyVisibleToInstantApp(),
+                                    + intentFilter.isExplicitlyVisibleToInstantApp(),
                             info, match, userId);
                 }
                 return null;
@@ -1577,13 +1588,14 @@
                 }
                 return null;
             }
-            final ResolveInfo res = new ResolveInfo(info.hasCategory(Intent.CATEGORY_BROWSABLE));
+            final ResolveInfo res =
+                    new ResolveInfo(intentFilter.hasCategory(Intent.CATEGORY_BROWSABLE));
             res.activityInfo = ai;
             if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
-                res.filter = info;
+                res.filter = intentFilter;
             }
-            res.handleAllWebDataURI = info.handleAllWebDataURI();
-            res.priority = info.getPriority();
+            res.handleAllWebDataURI = intentFilter.handleAllWebDataURI();
+            res.priority = intentFilter.getPriority();
             // TODO(b/135203078): This field was unwritten and does nothing
 //            res.preferredOrder = pkg.getPreferredOrder();
             //System.out.println("Result: " + res.activityInfo.className +
@@ -1645,7 +1657,7 @@
         @Override
         protected IntentFilter getIntentFilter(
                 @NonNull Pair<ParsedActivity, ParsedIntentInfo> input) {
-            return input.second;
+            return input.second.getIntentFilter();
         }
 
         protected List<ParsedActivity> getResolveList(AndroidPackage pkg) {
@@ -1756,11 +1768,12 @@
             int j;
             for (j = 0; j < intentsSize; j++) {
                 ParsedIntentInfo intent = p.getIntents().get(j);
+                IntentFilter intentFilter = intent.getIntentFilter();
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
-                    intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
+                    intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
-                if (!intent.debugCheck()) {
+                if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Provider " + p.getName());
                 }
                 addFilter(Pair.create(p, intent));
@@ -1777,9 +1790,10 @@
             int j;
             for (j = 0; j < intentsSize; j++) {
                 ParsedIntentInfo intent = p.getIntents().get(j);
+                IntentFilter intentFilter = intent.getIntentFilter();
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
-                    intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
+                    intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
                 removeFilter(Pair.create(p, intent));
             }
@@ -1824,7 +1838,8 @@
             }
 
             ParsedProvider provider = pair.first;
-            ParsedIntentInfo filter = pair.second;
+            ParsedIntentInfo intentInfo = pair.second;
+            IntentFilter filter = intentInfo.getIntentFilter();
 
             AndroidPackage pkg = sPackageManagerInternal.getPackage(provider.getPackageName());
             if (pkg == null) {
@@ -1877,10 +1892,10 @@
             // TODO(b/135203078): This field was unwritten and does nothing
 //            res.preferredOrder = pkg.getPreferredOrder();
             res.match = match;
-            res.isDefault = filter.isHasDefault();
-            res.labelRes = filter.getLabelRes();
-            res.nonLocalizedLabel = filter.getNonLocalizedLabel();
-            res.icon = filter.getIcon();
+            res.isDefault = intentInfo.isHasDefault();
+            res.labelRes = intentInfo.getLabelRes();
+            res.nonLocalizedLabel = intentInfo.getNonLocalizedLabel();
+            res.icon = intentInfo.getIcon();
             res.system = res.providerInfo.applicationInfo.isSystemApp();
             return res;
         }
@@ -1928,7 +1943,7 @@
         @Override
         protected IntentFilter getIntentFilter(
                 @NonNull Pair<ParsedProvider, ParsedIntentInfo> input) {
-            return input.second;
+            return input.second.getIntentFilter();
         }
 
         private final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
@@ -2001,11 +2016,12 @@
             int j;
             for (j = 0; j < intentsSize; j++) {
                 ParsedIntentInfo intent = s.getIntents().get(j);
+                IntentFilter intentFilter = intent.getIntentFilter();
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
-                    intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
+                    intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
-                if (!intent.debugCheck()) {
+                if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Service " + s.getName());
                 }
                 addFilter(Pair.create(s, intent));
@@ -2022,9 +2038,10 @@
             int j;
             for (j = 0; j < intentsSize; j++) {
                 ParsedIntentInfo intent = s.getIntents().get(j);
+                IntentFilter intentFilter = intent.getIntentFilter();
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
-                    intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
+                    intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
                 removeFilter(Pair.create(s, intent));
             }
@@ -2066,7 +2083,8 @@
             if (!sUserManager.exists(userId)) return null;
 
             ParsedService service = pair.first;
-            ParsedIntentInfo filter = pair.second;
+            ParsedIntentInfo intentInfo = pair.second;
+            IntentFilter filter = intentInfo.getIntentFilter();
 
             AndroidPackage pkg = sPackageManagerInternal.getPackage(service.getPackageName());
             if (pkg == null) {
@@ -2114,10 +2132,10 @@
             // TODO(b/135203078): This field was unwritten and does nothing
 //            res.preferredOrder = pkg.getPreferredOrder();
             res.match = match;
-            res.isDefault = filter.isHasDefault();
-            res.labelRes = filter.getLabelRes();
-            res.nonLocalizedLabel = filter.getNonLocalizedLabel();
-            res.icon = filter.getIcon();
+            res.isDefault = intentInfo.isHasDefault();
+            res.labelRes = intentInfo.getLabelRes();
+            res.nonLocalizedLabel = intentInfo.getNonLocalizedLabel();
+            res.icon = intentInfo.getIcon();
             res.system = res.serviceInfo.applicationInfo.isSystemApp();
             return res;
         }
@@ -2168,7 +2186,7 @@
         @Override
         protected IntentFilter getIntentFilter(
                 @NonNull Pair<ParsedService, ParsedIntentInfo> input) {
-            return input.second;
+            return input.second.getIntentFilter();
         }
 
         // Keys are String (activity class name), values are Activity.
@@ -2288,45 +2306,6 @@
         return !ps.isSystem() && ps.getStopped(userId);
     }
 
-    /** Generic to create an {@link Iterator} for a data type */
-    static class IterGenerator<E> {
-        public Iterator<E> generate(ParsedIntentInfo info) {
-            return null;
-        }
-    }
-
-    /** Create an {@link Iterator} for intent actions */
-    static class ActionIterGenerator extends IterGenerator<String> {
-        @Override
-        public Iterator<String> generate(ParsedIntentInfo info) {
-            return info.actionsIterator();
-        }
-    }
-
-    /** Create an {@link Iterator} for intent categories */
-    static class CategoriesIterGenerator extends IterGenerator<String> {
-        @Override
-        public Iterator<String> generate(ParsedIntentInfo info) {
-            return info.categoriesIterator();
-        }
-    }
-
-    /** Create an {@link Iterator} for intent schemes */
-    static class SchemesIterGenerator extends IterGenerator<String> {
-        @Override
-        public Iterator<String> generate(ParsedIntentInfo info) {
-            return info.schemesIterator();
-        }
-    }
-
-    /** Create an {@link Iterator} for intent authorities */
-    static class AuthoritiesIterGenerator extends IterGenerator<IntentFilter.AuthorityEntry> {
-        @Override
-        public Iterator<IntentFilter.AuthorityEntry> generate(ParsedIntentInfo info) {
-            return info.authoritiesIterator();
-        }
-    }
-
     /**
      * Removes MIME type from the group, by delegating to IntentResolvers
      * @return true if any intent filters were changed due to this update
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 9c6f3eb..aa4d6bb 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1811,7 +1811,7 @@
 
     @Nullable
     public final SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
-        return PackageManagerService.getSharedLibraryInfo(
+        return SharedLibraryHelper.getSharedLibraryInfo(
                 name, version, mSharedLibraries, null);
     }
 
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index f63cc4e..b83f987 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -61,7 +61,7 @@
         return mService.mInstantAppInstallerActivity;
     }
     protected ApplicationInfo androidApplication() {
-        return mService.mAndroidApplication;
+        return mService.getCoreAndroidApplication();
     }
 
     public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 7dd02cb..0feb9c5 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -37,6 +37,7 @@
 import android.annotation.Nullable;
 import android.app.ApplicationPackageManager;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDeleteObserver2;
 import android.content.pm.PackageChangeEvent;
 import android.content.pm.PackageInstaller;
@@ -50,6 +51,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -73,6 +75,7 @@
  * Relies on RemovePackageHelper to clear internal data structures.
  */
 final class DeletePackageHelper {
+    private static final boolean DEBUG_CLEAN_APKS = false;
     // ------- apps on sdcard specific code -------
     private static final boolean DEBUG_SD_INSTALL = false;
 
@@ -80,6 +83,7 @@
     private final UserManagerInternal mUserManagerInternal;
     private final PermissionManagerServiceInternal mPermissionManager;
     private final RemovePackageHelper mRemovePackageHelper;
+    // TODO(b/201815903): remove dependency to InitAndSystemPackageHelper
     private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
     private final AppDataHelper mAppDataHelper;
 
@@ -101,8 +105,7 @@
         mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
         mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
         mRemovePackageHelper = new RemovePackageHelper(mPm, mAppDataHelper);
-        mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(mPm, mRemovePackageHelper,
-                mAppDataHelper);
+        mInitAndSystemPackageHelper = mPm.getInitAndSystemPackageHelper();
     }
 
     /**
@@ -279,8 +282,7 @@
                             Slog.i(TAG, "Enabling system stub after removal; pkg: "
                                     + stubPkg.getPackageName());
                         }
-                        mInitAndSystemPackageHelper.enableCompressedPackage(stubPkg, stubPs,
-                                mPm.mDefParseFlags, mPm.getDirsToScanAsSystem());
+                        mInitAndSystemPackageHelper.enableCompressedPackage(stubPkg, stubPs);
                     } else if (DEBUG_COMPRESSION) {
                         Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
                                 + "after removal; pkg: " + stubPkg.getPackageName());
@@ -421,8 +423,7 @@
             PackageSetting disabledPs = deleteInstalledSystemPackage(action, ps, allUserHandles,
                     flags, outInfo, writeSettings);
             mInitAndSystemPackageHelper.restoreDisabledSystemPackageLIF(
-                    action, ps, allUserHandles, outInfo, writeSettings, mPm.mDefParseFlags,
-                    mPm.getDirsToScanAsSystem(), disabledPs);
+                    action, ps, allUserHandles, outInfo, writeSettings, disabledPs);
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName());
             deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
@@ -460,6 +461,7 @@
         final SharedUserSetting sus = ps.getSharedUser();
         final List<AndroidPackage> sharedUserPkgs =
                 sus != null ? sus.getPackages() : Collections.emptyList();
+        final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
         final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds()
                 : new int[] {userId};
         for (int nextUserId : userIds) {
@@ -473,7 +475,8 @@
             }
             PackageManagerService.removeKeystoreDataIfNeeded(mUserManagerInternal, nextUserId,
                     ps.getAppId());
-            mPm.clearPackagePreferredActivities(ps.getPackageName(), nextUserId);
+            preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
+                    nextUserId);
             mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
         }
         mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), pkg,
@@ -509,9 +512,9 @@
 
         // Delete application code and resources only for parent packages
         if (deleteCodeAndResources && (outInfo != null)) {
-            outInfo.mArgs = mPm.createInstallArgsForExisting(
+            outInfo.mArgs = new FileInstallArgs(
                     ps.getPathString(), getAppDexInstructionSets(
-                            ps.getPrimaryCpuAbi(), ps.getSecondaryCpuAbi()));
+                            ps.getPrimaryCpuAbi(), ps.getSecondaryCpuAbi()), mPm);
             if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.mArgs);
         }
     }
@@ -811,4 +814,89 @@
             this.installed = installed;
         }
     }
+
+    /**
+     * We're removing userId and would like to remove any downloaded packages
+     * that are no longer in use by any other user.
+     * @param userId the user being removed
+     */
+    @GuardedBy("mPm.mLock")
+    public void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) {
+        int [] users = userManager.getUserIds();
+        final int numPackages = mPm.mSettings.getPackagesLocked().size();
+        for (int index = 0; index < numPackages; index++) {
+            final PackageSetting ps = mPm.mSettings.getPackagesLocked().valueAt(index);
+            if (ps.getPkg() == null) {
+                continue;
+            }
+            final String packageName = ps.getPkg().getPackageName();
+            // Skip over if system app or static shared library
+            if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
+                    || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())) {
+                continue;
+            }
+            if (DEBUG_CLEAN_APKS) {
+                Slog.i(TAG, "Checking package " + packageName);
+            }
+            boolean keep = mPm.shouldKeepUninstalledPackageLPr(packageName);
+            if (keep) {
+                if (DEBUG_CLEAN_APKS) {
+                    Slog.i(TAG, "  Keeping package " + packageName + " - requested by DO");
+                }
+            } else {
+                for (int i = 0; i < users.length; i++) {
+                    if (users[i] != userId && ps.getInstalled(users[i])) {
+                        keep = true;
+                        if (DEBUG_CLEAN_APKS) {
+                            Slog.i(TAG, "  Keeping package " + packageName + " for user "
+                                    + users[i]);
+                        }
+                        break;
+                    }
+                }
+            }
+            if (!keep) {
+                if (DEBUG_CLEAN_APKS) {
+                    Slog.i(TAG, "  Removing package " + packageName);
+                }
+                //end run
+                mPm.mHandler.post(() -> deletePackageX(
+                        packageName, PackageManager.VERSION_CODE_HIGHEST,
+                        userId, 0, true /*removedBySystem*/));
+            }
+        }
+    }
+
+    public void deleteExistingPackageAsUser(VersionedPackage versionedPackage,
+            final IPackageDeleteObserver2 observer, final int userId) {
+        mPm.mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.DELETE_PACKAGES, null);
+        Preconditions.checkNotNull(versionedPackage);
+        Preconditions.checkNotNull(observer);
+        final String packageName = versionedPackage.getPackageName();
+        final long versionCode = versionedPackage.getLongVersionCode();
+
+        int installedForUsersCount = 0;
+        synchronized (mPm.mLock) {
+            // Normalize package name to handle renamed packages and static libs
+            final String internalPkgName = mPm.resolveInternalPackageNameLPr(packageName,
+                    versionCode);
+            final PackageSetting ps = mPm.mSettings.getPackageLPr(internalPkgName);
+            if (ps != null) {
+                int[] installedUsers = ps.queryInstalledUsers(mUserManagerInternal.getUserIds(),
+                        true);
+                installedForUsersCount = installedUsers.length;
+            }
+        }
+
+        if (installedForUsersCount > 1) {
+            deletePackageVersionedInternal(versionedPackage, observer, userId, 0, true);
+        } else {
+            try {
+                observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_INTERNAL_ERROR,
+                        null);
+            } catch (RemoteException re) {
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
new file mode 100644
index 0000000..9390284
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -0,0 +1,667 @@
+/*
+ * 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.server.pm;
+
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_OTA;
+import static com.android.server.pm.PackageManagerService.REASON_CMDLINE;
+import static com.android.server.pm.PackageManagerService.REASON_FIRST_BOOT;
+import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PKG;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.dex.ArtManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.server.apphibernation.AppHibernationManagerInternal;
+import com.android.server.apphibernation.AppHibernationService;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+final class DexOptHelper {
+    private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+
+    private final PackageManagerService mPm;
+
+    public boolean isDexOptDialogShown() {
+        return mDexOptDialogShown;
+    }
+
+    @GuardedBy("mPm.mLock")
+    private boolean mDexOptDialogShown;
+
+    DexOptHelper(PackageManagerService pm) {
+        mPm = pm;
+    }
+
+    /*
+     * Return the prebuilt profile path given a package base code path.
+     */
+    private static String getPrebuildProfilePath(AndroidPackage pkg) {
+        return pkg.getBaseApkPath() + ".prof";
+    }
+
+    /**
+     * Performs dexopt on the set of packages in {@code packages} and returns an int array
+     * containing statistics about the invocation. The array consists of three elements,
+     * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
+     * and {@code numberOfPackagesFailed}.
+     */
+    public int[] performDexOptUpgrade(List<AndroidPackage> pkgs, boolean showDialog,
+            final int compilationReason, boolean bootComplete) {
+
+        int numberOfPackagesVisited = 0;
+        int numberOfPackagesOptimized = 0;
+        int numberOfPackagesSkipped = 0;
+        int numberOfPackagesFailed = 0;
+        final int numberOfPackagesToDexopt = pkgs.size();
+
+        for (AndroidPackage pkg : pkgs) {
+            numberOfPackagesVisited++;
+
+            boolean useProfileForDexopt = false;
+
+            if ((mPm.isFirstBoot() || mPm.isDeviceUpgrading()) && pkg.isSystem()) {
+                // Copy over initial preopt profiles since we won't get any JIT samples for methods
+                // that are already compiled.
+                File profileFile = new File(getPrebuildProfilePath(pkg));
+                // Copy profile if it exists.
+                if (profileFile.exists()) {
+                    try {
+                        // We could also do this lazily before calling dexopt in
+                        // PackageDexOptimizer to prevent this happening on first boot. The issue
+                        // is that we don't have a good way to say "do this only once".
+                        if (!mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
+                                pkg.getUid(), pkg.getPackageName(),
+                                ArtManager.getProfileName(null))) {
+                            Log.e(TAG, "Installer failed to copy system profile!");
+                        } else {
+                            // Disabled as this causes speed-profile compilation during first boot
+                            // even if things are already compiled.
+                            // useProfileForDexopt = true;
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
+                                e);
+                    }
+                } else {
+                    PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(
+                            pkg.getPackageName());
+                    // Handle compressed APKs in this path. Only do this for stubs with profiles to
+                    // minimize the number off apps being speed-profile compiled during first boot.
+                    // The other paths will not change the filter.
+                    if (disabledPs != null && disabledPs.getPkg().isStub()) {
+                        // The package is the stub one, remove the stub suffix to get the normal
+                        // package and APK names.
+                        String systemProfilePath = getPrebuildProfilePath(disabledPs.getPkg())
+                                .replace(STUB_SUFFIX, "");
+                        profileFile = new File(systemProfilePath);
+                        // If we have a profile for a compressed APK, copy it to the reference
+                        // location.
+                        // Note that copying the profile here will cause it to override the
+                        // reference profile every OTA even though the existing reference profile
+                        // may have more data. We can't copy during decompression since the
+                        // directories are not set up at that point.
+                        if (profileFile.exists()) {
+                            try {
+                                // We could also do this lazily before calling dexopt in
+                                // PackageDexOptimizer to prevent this happening on first boot. The
+                                // issue is that we don't have a good way to say "do this only
+                                // once".
+                                if (!mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
+                                        pkg.getUid(), pkg.getPackageName(),
+                                        ArtManager.getProfileName(null))) {
+                                    Log.e(TAG, "Failed to copy system profile for stub package!");
+                                } else {
+                                    useProfileForDexopt = true;
+                                }
+                            } catch (Exception e) {
+                                Log.e(TAG, "Failed to copy profile "
+                                        + profileFile.getAbsolutePath() + " ", e);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName());
+                }
+                numberOfPackagesSkipped++;
+                continue;
+            }
+
+            if (DEBUG_DEXOPT) {
+                Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of "
+                        + numberOfPackagesToDexopt + ": " + pkg.getPackageName());
+            }
+
+            if (showDialog) {
+                try {
+                    ActivityManager.getService().showBootMessage(
+                            mPm.mContext.getResources().getString(R.string.android_upgrading_apk,
+                                    numberOfPackagesVisited, numberOfPackagesToDexopt), true);
+                } catch (RemoteException e) {
+                }
+                synchronized (mPm.mLock) {
+                    mDexOptDialogShown = true;
+                }
+            }
+
+            int pkgCompilationReason = compilationReason;
+            if (useProfileForDexopt) {
+                // Use background dexopt mode to try and use the profile. Note that this does not
+                // guarantee usage of the profile.
+                pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
+            }
+
+            if (SystemProperties.getBoolean(mPm.PRECOMPILE_LAYOUTS, false)) {
+                mPm.mArtManagerService.compileLayouts(pkg);
+            }
+
+            // checkProfiles is false to avoid merging profiles during boot which
+            // might interfere with background compilation (b/28612421).
+            // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
+            // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
+            // trade-off worth doing to save boot time work.
+            int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
+            if (compilationReason == REASON_FIRST_BOOT) {
+                // TODO: This doesn't cover the upgrade case, we should check for this too.
+                dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+            }
+            int primaryDexOptStatus = performDexOptTraced(new DexoptOptions(
+                    pkg.getPackageName(),
+                    pkgCompilationReason,
+                    dexoptFlags));
+
+            switch (primaryDexOptStatus) {
+                case PackageDexOptimizer.DEX_OPT_PERFORMED:
+                    numberOfPackagesOptimized++;
+                    break;
+                case PackageDexOptimizer.DEX_OPT_SKIPPED:
+                    numberOfPackagesSkipped++;
+                    break;
+                case PackageDexOptimizer.DEX_OPT_CANCELLED:
+                    // ignore this case
+                    break;
+                case PackageDexOptimizer.DEX_OPT_FAILED:
+                    numberOfPackagesFailed++;
+                    break;
+                default:
+                    Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStatus);
+                    break;
+            }
+        }
+
+        return new int[]{numberOfPackagesOptimized, numberOfPackagesSkipped,
+                numberOfPackagesFailed};
+    }
+
+    public void performPackageDexOptUpgradeIfNeeded() {
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system can request package update");
+
+        // We need to re-extract after an OTA.
+        boolean causeUpgrade = mPm.isDeviceUpgrading();
+
+        // First boot or factory reset.
+        // Note: we also handle devices that are upgrading to N right now as if it is their
+        //       first boot, as they do not have profile data.
+        boolean causeFirstBoot = mPm.isFirstBoot() || mPm.isPreNUpgrade();
+
+        if (!causeUpgrade && !causeFirstBoot) {
+            return;
+        }
+
+        List<PackageSetting> pkgSettings;
+        synchronized (mPm.mLock) {
+            pkgSettings = getPackagesForDexopt(mPm.mSettings.getPackagesLocked().values(), mPm);
+        }
+
+        List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
+        for (int index = 0; index < pkgSettings.size(); index++) {
+            pkgs.add(pkgSettings.get(index).getPkg());
+        }
+
+        final long startTime = System.nanoTime();
+        final int[] stats = performDexOptUpgrade(pkgs, mPm.isPreNUpgrade() /* showDialog */,
+                causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA,
+                false /* bootComplete */);
+
+        final int elapsedTimeSeconds =
+                (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
+
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_dexopted", stats[0]);
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_skipped", stats[1]);
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_num_failed", stats[2]);
+        MetricsLogger.histogram(
+                mPm.mContext, "opt_dialog_num_total", getOptimizablePackages().size());
+        MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds);
+    }
+
+    public ArraySet<String> getOptimizablePackages() {
+        ArraySet<String> pkgs = new ArraySet<>();
+        synchronized (mPm.mLock) {
+            for (AndroidPackage p : mPm.mPackages.values()) {
+                if (PackageDexOptimizer.canOptimizePackage(p)) {
+                    pkgs.add(p.getPackageName());
+                }
+            }
+        }
+        if (AppHibernationService.isAppHibernationEnabled()) {
+            AppHibernationManagerInternal appHibernationManager =
+                    mPm.mInjector.getLocalService(AppHibernationManagerInternal.class);
+            pkgs.removeIf(pkgName -> appHibernationManager.isHibernatingGlobally(pkgName));
+        }
+        return pkgs;
+    }
+
+    /*package*/ boolean performDexOpt(DexoptOptions options) {
+        if (mPm.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+            return false;
+        } else if (mPm.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
+            return false;
+        }
+
+        if (options.isDexoptOnlySecondaryDex()) {
+            return mPm.getDexManager().dexoptSecondaryDex(options);
+        } else {
+            int dexoptStatus = performDexOptWithStatus(options);
+            return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
+        }
+    }
+
+    /**
+     * Perform dexopt on the given package and return one of following result:
+     * {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
+     * {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
+     * {@link PackageDexOptimizer#DEX_OPT_CANCELLED}
+     * {@link PackageDexOptimizer#DEX_OPT_FAILED}
+     */
+    @PackageDexOptimizer.DexOptResult
+    /* package */ int performDexOptWithStatus(DexoptOptions options) {
+        return performDexOptTraced(options);
+    }
+
+    private int performDexOptTraced(DexoptOptions options) {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+        try {
+            return performDexOptInternal(options);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
+    // if the package can now be considered up to date for the given filter.
+    private int performDexOptInternal(DexoptOptions options) {
+        AndroidPackage p;
+        PackageSetting pkgSetting;
+        synchronized (mPm.mLock) {
+            p = mPm.mPackages.get(options.getPackageName());
+            pkgSetting = mPm.mSettings.getPackageLPr(options.getPackageName());
+            if (p == null || pkgSetting == null) {
+                // Package could not be found. Report failure.
+                return PackageDexOptimizer.DEX_OPT_FAILED;
+            }
+            mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
+            mPm.mCompilerStats.maybeWriteAsync();
+        }
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mPm.mInstallLock) {
+                return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
+            @NonNull PackageSetting pkgSetting, DexoptOptions options) {
+        // System server gets a special path.
+        if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
+            return mPm.getDexManager().dexoptSystemServer(options);
+        }
+
+        // Select the dex optimizer based on the force parameter.
+        // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
+        //       allocate an object here.
+        PackageDexOptimizer pdo = options.isForce()
+                ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPm.mPackageDexOptimizer)
+                : mPm.mPackageDexOptimizer;
+
+        // Dexopt all dependencies first. Note: we ignore the return value and march on
+        // on errors.
+        // Note that we are going to call performDexOpt on those libraries as many times as
+        // they are referenced in packages. When we do a batch of performDexOpt (for example
+        // at boot, or background job), the passed 'targetCompilerFilter' stays the same,
+        // and the first package that uses the library will dexopt it. The
+        // others will see that the compiled code for the library is up to date.
+        Collection<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
+        final String[] instructionSets = getAppDexInstructionSets(
+                AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
+                AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
+        if (!deps.isEmpty()) {
+            DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
+                    options.getCompilationReason(), options.getCompilerFilter(),
+                    options.getSplitName(),
+                    options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
+            for (SharedLibraryInfo info : deps) {
+                AndroidPackage depPackage = null;
+                PackageSetting depPackageSetting = null;
+                synchronized (mPm.mLock) {
+                    depPackage = mPm.mPackages.get(info.getPackageName());
+                    depPackageSetting = mPm.mSettings.getPackageLPr(info.getPackageName());
+                }
+                if (depPackage != null && depPackageSetting != null) {
+                    // TODO: Analyze and investigate if we (should) profile libraries.
+                    pdo.performDexOpt(depPackage, depPackageSetting, instructionSets,
+                            mPm.getOrCreateCompilerPackageStats(depPackage),
+                            mPm.getDexManager().getPackageUseInfoOrDefault(
+                                    depPackage.getPackageName()), libraryOptions);
+                } else {
+                    // TODO(ngeoffray): Support dexopting system shared libraries.
+                }
+            }
+        }
+
+        return pdo.performDexOpt(p, pkgSetting, instructionSets,
+                mPm.getOrCreateCompilerPackageStats(p),
+                mPm.getDexManager().getPackageUseInfoOrDefault(p.getPackageName()), options);
+    }
+
+    public void forceDexOpt(String packageName) {
+        PackageManagerServiceUtils.enforceSystemOrRoot("forceDexOpt");
+
+        AndroidPackage pkg;
+        PackageSetting pkgSetting;
+        synchronized (mPm.mLock) {
+            pkg = mPm.mPackages.get(packageName);
+            pkgSetting = mPm.mSettings.getPackageLPr(packageName);
+            if (pkg == null || pkgSetting == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+        }
+
+        synchronized (mPm.mInstallLock) {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+
+            // Whoever is calling forceDexOpt wants a compiled package.
+            // Don't use profiles since that may cause compilation to be skipped.
+            final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
+                    new DexoptOptions(packageName,
+                            getDefaultCompilerFilter(),
+                            DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
+
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
+                throw new IllegalStateException("Failed to dexopt: " + res);
+            }
+        }
+    }
+
+    public boolean performDexOptMode(String packageName,
+            boolean checkProfiles, String targetCompilerFilter, boolean force,
+            boolean bootComplete, String splitName) {
+        PackageManagerServiceUtils.enforceSystemOrRootOrShell("performDexOptMode");
+
+        int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0)
+                | (force ? DexoptOptions.DEXOPT_FORCE : 0)
+                | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
+        return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
+                targetCompilerFilter, splitName, flags));
+    }
+
+    public boolean performDexOptSecondary(String packageName, String compilerFilter,
+            boolean force) {
+        int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX
+                | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
+                | DexoptOptions.DEXOPT_BOOT_COMPLETE
+                | (force ? DexoptOptions.DEXOPT_FORCE : 0);
+        return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
+    }
+
+    // Sort apps by importance for dexopt ordering. Important apps are given
+    // more priority in case the device runs out of space.
+    public static List<PackageSetting> getPackagesForDexopt(
+            Collection<PackageSetting> packages,
+            PackageManagerService packageManagerService) {
+        return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
+    }
+
+    public static List<PackageSetting> getPackagesForDexopt(
+            Collection<PackageSetting> pkgSettings,
+            PackageManagerService packageManagerService,
+            boolean debug) {
+        List<PackageSetting> result = new LinkedList<>();
+        ArrayList<PackageSetting> remainingPkgSettings = new ArrayList<>(pkgSettings);
+
+        // First, remove all settings without available packages
+        remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG);
+
+        ArrayList<PackageSetting> sortTemp = new ArrayList<>(remainingPkgSettings.size());
+
+        // Give priority to core apps.
+        applyPackageFilter(pkgSetting -> pkgSetting.getPkg().isCoreApp(), result,
+                remainingPkgSettings, sortTemp, packageManagerService);
+
+        // Give priority to system apps that listen for pre boot complete.
+        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+        final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
+        applyPackageFilter(pkgSetting -> pkgNames.contains(pkgSetting.getPackageName()), result,
+                remainingPkgSettings, sortTemp, packageManagerService);
+
+        // Give priority to apps used by other apps.
+        DexManager dexManager = packageManagerService.getDexManager();
+        applyPackageFilter(pkgSetting ->
+                        dexManager.getPackageUseInfoOrDefault(pkgSetting.getPackageName())
+                                .isAnyCodePathUsedByOtherApps(),
+                result, remainingPkgSettings, sortTemp, packageManagerService);
+
+        // Filter out packages that aren't recently used, add all remaining apps.
+        // TODO: add a property to control this?
+        Predicate<PackageSetting> remainingPredicate;
+        if (!remainingPkgSettings.isEmpty()
+                && packageManagerService.isHistoricalPackageUsageAvailable()) {
+            if (debug) {
+                Log.i(TAG, "Looking at historical package use");
+            }
+            // Get the package that was used last.
+            PackageSetting lastUsed = Collections.max(remainingPkgSettings,
+                    (pkgSetting1, pkgSetting2) -> Long.compare(
+                            pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills(),
+                            pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills()));
+            if (debug) {
+                Log.i(TAG, "Taking package " + lastUsed.getPackageName()
+                        + " as reference in time use");
+            }
+            long estimatedPreviousSystemUseTime = lastUsed.getPkgState()
+                    .getLatestForegroundPackageUseTimeInMills();
+            // Be defensive if for some reason package usage has bogus data.
+            if (estimatedPreviousSystemUseTime != 0) {
+                final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
+                remainingPredicate = pkgSetting -> pkgSetting.getPkgState()
+                        .getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
+            } else {
+                // No meaningful historical info. Take all.
+                remainingPredicate = pkgSetting -> true;
+            }
+            sortPackagesByUsageDate(remainingPkgSettings, packageManagerService);
+        } else {
+            // No historical info. Take all.
+            remainingPredicate = pkgSetting -> true;
+        }
+        applyPackageFilter(remainingPredicate, result, remainingPkgSettings, sortTemp,
+                packageManagerService);
+
+        if (debug) {
+            Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
+            Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
+        }
+
+        return result;
+    }
+
+    // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
+    // package will be removed from {@code packages} and added to {@code result} with its
+    // dependencies. If usage data is available, the positive packages will be sorted by usage
+    // data (with {@code sortTemp} as temporary storage).
+    private static void applyPackageFilter(
+            Predicate<PackageSetting> filter,
+            Collection<PackageSetting> result,
+            Collection<PackageSetting> packages,
+            @NonNull List<PackageSetting> sortTemp,
+            PackageManagerService packageManagerService) {
+        for (PackageSetting pkgSetting : packages) {
+            if (filter.test(pkgSetting)) {
+                sortTemp.add(pkgSetting);
+            }
+        }
+
+        sortPackagesByUsageDate(sortTemp, packageManagerService);
+        packages.removeAll(sortTemp);
+
+        for (PackageSetting pkgSetting : sortTemp) {
+            result.add(pkgSetting);
+
+            List<PackageSetting> deps =
+                    packageManagerService.findSharedNonSystemLibraries(pkgSetting);
+            if (!deps.isEmpty()) {
+                deps.removeAll(result);
+                result.addAll(deps);
+                packages.removeAll(deps);
+            }
+        }
+
+        sortTemp.clear();
+    }
+
+    // Sort a list of apps by their last usage, most recently used apps first. The order of
+    // packages without usage data is undefined (but they will be sorted after the packages
+    // that do have usage data).
+    private static void sortPackagesByUsageDate(List<PackageSetting> pkgSettings,
+            PackageManagerService packageManagerService) {
+        if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
+            return;
+        }
+
+        Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) ->
+                Long.compare(
+                        pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills(),
+                        pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills())
+        );
+    }
+
+    private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
+        List<ResolveInfo> ris = null;
+        try {
+            ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
+                    .getList();
+        } catch (RemoteException e) {
+        }
+        ArraySet<String> pkgNames = new ArraySet<String>();
+        if (ris != null) {
+            for (ResolveInfo ri : ris) {
+                pkgNames.add(ri.activityInfo.packageName);
+            }
+        }
+        return pkgNames;
+    }
+
+    public static String packagesToString(List<PackageSetting> pkgSettings) {
+        StringBuilder sb = new StringBuilder();
+        for (int index = 0; index < pkgSettings.size(); index++) {
+            if (sb.length() > 0) {
+                sb.append(", ");
+            }
+            sb.append(pkgSettings.get(index).getPackageName());
+        }
+        return sb.toString();
+    }
+
+     /**
+     * Requests that files preopted on a secondary system partition be copied to the data partition
+     * if possible.  Note that the actual copying of the files is accomplished by init for security
+     * reasons. This simply requests that the copy takes place and awaits confirmation of its
+     * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
+     */
+    public static void requestCopyPreoptedFiles() {
+        final int WAIT_TIME_MS = 100;
+        final String CP_PREOPT_PROPERTY = "sys.cppreopt";
+        if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
+            SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
+            // We will wait for up to 100 seconds.
+            final long timeStart = SystemClock.uptimeMillis();
+            final long timeEnd = timeStart + 100 * 1000;
+            long timeNow = timeStart;
+            while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
+                try {
+                    Thread.sleep(WAIT_TIME_MS);
+                } catch (InterruptedException e) {
+                    // Do nothing
+                }
+                timeNow = SystemClock.uptimeMillis();
+                if (timeNow > timeEnd) {
+                    SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
+                    Slog.wtf(TAG, "cppreopt did not finish!");
+                    break;
+                }
+            }
+
+            Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
+        }
+    }
+
+    /*package*/ void controlDexOptBlocking(boolean block) {
+        mPm.mPackageDexOptimizer.controlDexOptBlocking(block);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
new file mode 100644
index 0000000..d43b681
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -0,0 +1,757 @@
+/*
+ * 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.server.pm;
+
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
+
+import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo;
+
+import android.content.ComponentName;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.SharedLibraryInfo;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.os.incremental.PerUidReadTimeouts;
+import android.service.pm.PackageServiceDumpProto;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Dumps PackageManagerService internal states.
+ */
+final class DumpHelper {
+    final PackageManagerService mPm;
+
+    DumpHelper(PackageManagerService pm) {
+        mPm = pm;
+    }
+
+    public void doDump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        DumpState dumpState = new DumpState();
+        ArraySet<String> permissionNames = null;
+
+        int opti = 0;
+        while (opti < args.length) {
+            String opt = args[opti];
+            if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
+                break;
+            }
+            opti++;
+
+            if ("-a".equals(opt)) {
+                // Right now we only know how to print all.
+            } else if ("-h".equals(opt)) {
+                printHelp(pw);
+                return;
+            } else if ("--checkin".equals(opt)) {
+                dumpState.setCheckIn(true);
+            } else if ("--all-components".equals(opt)) {
+                dumpState.setOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS);
+            } else if ("-f".equals(opt)) {
+                dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
+            } else if ("--proto".equals(opt)) {
+                dumpProto(fd);
+                return;
+            } else {
+                pw.println("Unknown argument: " + opt + "; use -h for help");
+            }
+        }
+
+        // Is the caller requesting to dump a particular piece of data?
+        if (opti < args.length) {
+            String cmd = args[opti];
+            opti++;
+            // Is this a package name?
+            if ("android".equals(cmd) || cmd.contains(".")) {
+                dumpState.setTargetPackageName(cmd);
+                // When dumping a single package, we always dump all of its
+                // filter information since the amount of data will be reasonable.
+                dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
+            } else if ("check-permission".equals(cmd)) {
+                if (opti >= args.length) {
+                    pw.println("Error: check-permission missing permission argument");
+                    return;
+                }
+                String perm = args[opti];
+                opti++;
+                if (opti >= args.length) {
+                    pw.println("Error: check-permission missing package argument");
+                    return;
+                }
+
+                String pkg = args[opti];
+                opti++;
+                int user = UserHandle.getUserId(Binder.getCallingUid());
+                if (opti < args.length) {
+                    try {
+                        user = Integer.parseInt(args[opti]);
+                    } catch (NumberFormatException e) {
+                        pw.println("Error: check-permission user argument is not a number: "
+                                + args[opti]);
+                        return;
+                    }
+                }
+
+                // Normalize package name to handle renamed packages and static libs
+                pkg = mPm.resolveInternalPackageNameLPr(pkg, PackageManager.VERSION_CODE_HIGHEST);
+
+                pw.println(mPm.checkPermission(perm, pkg, user));
+                return;
+            } else if ("l".equals(cmd) || "libraries".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_LIBS);
+            } else if ("f".equals(cmd) || "features".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_FEATURES);
+            } else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
+                if (opti >= args.length) {
+                    dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS
+                            | DumpState.DUMP_SERVICE_RESOLVERS
+                            | DumpState.DUMP_RECEIVER_RESOLVERS
+                            | DumpState.DUMP_CONTENT_RESOLVERS);
+                } else {
+                    while (opti < args.length) {
+                        String name = args[opti];
+                        if ("a".equals(name) || "activity".equals(name)) {
+                            dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS);
+                        } else if ("s".equals(name) || "service".equals(name)) {
+                            dumpState.setDump(DumpState.DUMP_SERVICE_RESOLVERS);
+                        } else if ("r".equals(name) || "receiver".equals(name)) {
+                            dumpState.setDump(DumpState.DUMP_RECEIVER_RESOLVERS);
+                        } else if ("c".equals(name) || "content".equals(name)) {
+                            dumpState.setDump(DumpState.DUMP_CONTENT_RESOLVERS);
+                        } else {
+                            pw.println("Error: unknown resolver table type: " + name);
+                            return;
+                        }
+                        opti++;
+                    }
+                }
+            } else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_PERMISSIONS);
+            } else if ("permission".equals(cmd)) {
+                if (opti >= args.length) {
+                    pw.println("Error: permission requires permission name");
+                    return;
+                }
+                permissionNames = new ArraySet<>();
+                while (opti < args.length) {
+                    permissionNames.add(args[opti]);
+                    opti++;
+                }
+                dumpState.setDump(DumpState.DUMP_PERMISSIONS
+                        | DumpState.DUMP_PACKAGES | DumpState.DUMP_SHARED_USERS);
+            } else if ("pref".equals(cmd) || "preferred".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_PREFERRED);
+            } else if ("preferred-xml".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_PREFERRED_XML);
+                if (opti < args.length && "--full".equals(args[opti])) {
+                    dumpState.setFullPreferred(true);
+                    opti++;
+                }
+            } else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_DOMAIN_PREFERRED);
+            } else if ("p".equals(cmd) || "packages".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_PACKAGES);
+            } else if ("q".equals(cmd) || "queries".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_QUERIES);
+            } else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_SHARED_USERS);
+                if (opti < args.length && "noperm".equals(args[opti])) {
+                    dumpState.setOptionEnabled(DumpState.OPTION_SKIP_PERMISSIONS);
+                }
+            } else if ("prov".equals(cmd) || "providers".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_PROVIDERS);
+            } else if ("m".equals(cmd) || "messages".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_MESSAGES);
+            } else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_VERIFIERS);
+            } else if ("dv".equals(cmd) || "domain-verifier".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_DOMAIN_VERIFIER);
+            } else if ("version".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_VERSION);
+            } else if ("k".equals(cmd) || "keysets".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_KEYSETS);
+            } else if ("installs".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_INSTALLS);
+            } else if ("frozen".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_FROZEN);
+            } else if ("volumes".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_VOLUMES);
+            } else if ("dexopt".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_DEXOPT);
+            } else if ("compiler-stats".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
+            } else if ("changes".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_CHANGES);
+            } else if ("service-permissions".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
+            } else if ("known-packages".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_KNOWN_PACKAGES);
+            } else if ("t".equals(cmd) || "timeouts".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS);
+            } else if ("snapshot".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_SNAPSHOT_STATISTICS);
+                if (opti < args.length) {
+                    if ("--full".equals(args[opti])) {
+                        dumpState.setBrief(false);
+                        opti++;
+                    } else if ("--brief".equals(args[opti])) {
+                        dumpState.setBrief(true);
+                        opti++;
+                    }
+                }
+            } else if ("protected-broadcasts".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_PROTECTED_BROADCASTS);
+            } else if ("write".equals(cmd)) {
+                synchronized (mPm.mLock) {
+                    mPm.writeSettingsLPrTEMP();
+                    pw.println("Settings written.");
+                    return;
+                }
+            }
+        }
+
+        final String packageName = dumpState.getTargetPackageName();
+        final boolean checkin = dumpState.isCheckIn();
+
+        // Return if the package doesn't exist.
+        if (packageName != null
+                && mPm.getPackageSetting(packageName) == null
+                && !mPm.mApexManager.isApexPackage(packageName)) {
+            pw.println("Unable to find package: " + packageName);
+            return;
+        }
+
+        if (checkin) {
+            pw.println("vers,1");
+        }
+
+        // reader
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_VERSION)
+                && packageName == null) {
+            mPm.dumpComputer(DumpState.DUMP_VERSION, fd, pw, dumpState);
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+            ipw.println("Known Packages:");
+            ipw.increaseIndent();
+            for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
+                final String knownPackage = PackageManagerInternal.knownPackageToString(i);
+                ipw.print(knownPackage);
+                ipw.println(":");
+                final String[] pkgNames = mPm.getKnownPackageNamesInternal(i,
+                        UserHandle.USER_SYSTEM);
+                ipw.increaseIndent();
+                if (ArrayUtils.isEmpty(pkgNames)) {
+                    ipw.println("none");
+                } else {
+                    for (String name : pkgNames) {
+                        ipw.println(name);
+                    }
+                }
+                ipw.decreaseIndent();
+            }
+            ipw.decreaseIndent();
+        }
+
+        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
+                && packageName == null) {
+            final String requiredVerifierPackage = mPm.mRequiredVerifierPackage;
+            if (!checkin) {
+                if (dumpState.onTitlePrinted()) {
+                    pw.println();
+                }
+                pw.println("Verifiers:");
+                pw.print("  Required: ");
+                pw.print(requiredVerifierPackage);
+                pw.print(" (uid=");
+                pw.print(mPm.getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+                        UserHandle.USER_SYSTEM));
+                pw.println(")");
+            } else if (requiredVerifierPackage != null) {
+                pw.print("vrfy,"); pw.print(requiredVerifierPackage);
+                pw.print(",");
+                pw.println(mPm.getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+                        UserHandle.USER_SYSTEM));
+            }
+        }
+
+        if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER)
+                && packageName == null) {
+            final DomainVerificationProxy proxy = mPm.mDomainVerificationManager.getProxy();
+            final ComponentName verifierComponent = proxy.getComponentName();
+            if (verifierComponent != null) {
+                String verifierPackageName = verifierComponent.getPackageName();
+                if (!checkin) {
+                    if (dumpState.onTitlePrinted()) {
+                        pw.println();
+                    }
+                    pw.println("Domain Verifier:");
+                    pw.print("  Using: ");
+                    pw.print(verifierPackageName);
+                    pw.print(" (uid=");
+                    pw.print(mPm.getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
+                            UserHandle.USER_SYSTEM));
+                    pw.println(")");
+                } else if (verifierPackageName != null) {
+                    pw.print("dv,"); pw.print(verifierPackageName);
+                    pw.print(",");
+                    pw.println(mPm.getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
+                            UserHandle.USER_SYSTEM));
+                }
+            } else {
+                pw.println();
+                pw.println("No Domain Verifier available!");
+            }
+        }
+
+        if (dumpState.isDumping(DumpState.DUMP_LIBS)
+                && packageName == null) {
+            mPm.dumpComputer(DumpState.DUMP_LIBS, fd, pw, dumpState);
+        }
+
+        if (dumpState.isDumping(DumpState.DUMP_FEATURES)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            if (!checkin) {
+                pw.println("Features:");
+            }
+
+            synchronized (mPm.mAvailableFeatures) {
+                for (FeatureInfo feat : mPm.mAvailableFeatures.values()) {
+                    if (!checkin) {
+                        pw.print("  ");
+                        pw.print(feat.name);
+                        if (feat.version > 0) {
+                            pw.print(" version=");
+                            pw.print(feat.version);
+                        }
+                        pw.println();
+                    } else {
+                        pw.print("feat,");
+                        pw.print(feat.name);
+                        pw.print(",");
+                        pw.println(feat.version);
+                    }
+                }
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+            synchronized (mPm.mLock) {
+                mPm.mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
+            }
+        }
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+            synchronized (mPm.mLock) {
+                mPm.mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
+            }
+        }
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+            synchronized (mPm.mLock) {
+                mPm.mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
+            }
+        }
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+            synchronized (mPm.mLock) {
+                mPm.mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+            mPm.dumpComputer(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)
+                && packageName == null) {
+            mPm.dumpComputer(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+            mPm.dumpComputer(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+            synchronized (mPm.mLock) {
+                mPm.mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+            synchronized (mPm.mLock) {
+                mPm.mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+            synchronized (mPm.mLock) {
+                mPm.mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
+            }
+        }
+
+        if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
+            // This cannot be moved to ComputerEngine since some variables of the collections
+            // in PackageUserState such as suspendParams, disabledComponents and enabledComponents
+            // do not have a copy.
+            synchronized (mPm.mLock) {
+                mPm.mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin);
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_QUERIES)) {
+            mPm.dumpComputer(DumpState.DUMP_QUERIES, fd, pw, dumpState);
+        }
+
+        if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+            // This cannot be moved to ComputerEngine since the set of packages in the
+            // SharedUserSetting do not have a copy.
+            synchronized (mPm.mLock) {
+                mPm.mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState,
+                        checkin);
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_CHANGES)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            pw.println("Package Changes:");
+            synchronized (mPm.mLock) {
+                pw.print("  Sequence number="); pw.println(mPm.mChangedPackagesSequenceNumber);
+                final int numChangedPackages = mPm.mChangedPackages.size();
+                for (int i = 0; i < numChangedPackages; i++) {
+                    final SparseArray<String> changes = mPm.mChangedPackages.valueAt(i);
+                    pw.print("  User "); pw.print(mPm.mChangedPackages.keyAt(i)); pw.println(":");
+                    final int numChanges = changes.size();
+                    if (numChanges == 0) {
+                        pw.print("    "); pw.println("No packages changed");
+                    } else {
+                        for (int j = 0; j < numChanges; j++) {
+                            final String pkgName = changes.valueAt(j);
+                            final int sequenceNumber = changes.keyAt(j);
+                            pw.print("    ");
+                            pw.print("seq=");
+                            pw.print(sequenceNumber);
+                            pw.print(", package=");
+                            pw.println(pkgName);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_FROZEN)
+                && packageName == null) {
+            // XXX should handle packageName != null by dumping only install data that
+            // the given package is involved with.
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+            ipw.println();
+            ipw.println("Frozen packages:");
+            ipw.increaseIndent();
+            synchronized (mPm.mLock) {
+                if (mPm.mFrozenPackages.size() == 0) {
+                    ipw.println("(none)");
+                } else {
+                    for (int i = 0; i < mPm.mFrozenPackages.size(); i++) {
+                        ipw.println(mPm.mFrozenPackages.valueAt(i));
+                    }
+                }
+            }
+            ipw.decreaseIndent();
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_VOLUMES)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+            ipw.println();
+            ipw.println("Loaded volumes:");
+            ipw.increaseIndent();
+            synchronized (mPm.mLoadedVolumes) {
+                if (mPm.mLoadedVolumes.size() == 0) {
+                    ipw.println("(none)");
+                } else {
+                    for (int i = 0; i < mPm.mLoadedVolumes.size(); i++) {
+                        ipw.println(mPm.mLoadedVolumes.valueAt(i));
+                    }
+                }
+            }
+            ipw.decreaseIndent();
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+                && packageName == null) {
+            synchronized (mPm.mLock) {
+                mPm.mComponentResolver.dumpServicePermissions(pw, dumpState);
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
+            mPm.dumpComputer(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
+            mPm.dumpComputer(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
+        }
+
+        if (dumpState.isDumping(DumpState.DUMP_MESSAGES)
+                && packageName == null) {
+            if (!checkin) {
+                if (dumpState.onTitlePrinted()) {
+                    pw.println();
+                }
+                synchronized (mPm.mLock) {
+                    mPm.mSettings.dumpReadMessagesLPr(pw, dumpState);
+                }
+                pw.println();
+                pw.println("Package warning messages:");
+                dumpCriticalInfo(pw, null);
+            } else {
+                dumpCriticalInfo(pw, "msg,");
+            }
+        }
+
+        // PackageInstaller should be called outside of mPackages lock
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_INSTALLS)
+                && packageName == null) {
+            // XXX should handle packageName != null by dumping only install data that
+            // the given package is involved with.
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            mPm.mInstallerService.dump(new IndentingPrintWriter(pw, "  ", 120));
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_APEX)
+                && (packageName == null || mPm.mApexManager.isApexPackage(packageName))) {
+            mPm.mApexManager.dump(pw, packageName);
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            pw.println("Per UID read timeouts:");
+            pw.println("    Default timeouts flag: " + PackageManagerService.getDefaultTimeouts());
+            pw.println("    Known digesters list flag: "
+                    + PackageManagerService.getKnownDigestersList());
+
+            PerUidReadTimeouts[] items = mPm.getPerUidReadTimeouts();
+            pw.println("    Timeouts (" + items.length + "):");
+            for (PerUidReadTimeouts item : items) {
+                pw.print("        (");
+                pw.print("uid=" + item.uid + ", ");
+                pw.print("minTimeUs=" + item.minTimeUs + ", ");
+                pw.print("minPendingTimeUs=" + item.minPendingTimeUs + ", ");
+                pw.print("maxPendingTimeUs=" + item.maxPendingTimeUs);
+                pw.println(")");
+            }
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            pw.println("Snapshot statistics");
+            mPm.dumpSnapshotStats(pw, dumpState.isBrief());
+        }
+
+        if (!checkin
+                && dumpState.isDumping(DumpState.DUMP_PROTECTED_BROADCASTS)
+                && packageName == null) {
+            if (dumpState.onTitlePrinted()) {
+                pw.println();
+            }
+            pw.println("Protected broadcast actions:");
+            synchronized (mPm.mProtectedBroadcasts) {
+                for (int i = 0; i < mPm.mProtectedBroadcasts.size(); i++) {
+                    pw.print("  ");
+                    pw.println(mPm.mProtectedBroadcasts.valueAt(i));
+                }
+            }
+
+        }
+    }
+
+    private void printHelp(PrintWriter pw) {
+        pw.println("Package manager dump options:");
+        pw.println("  [-h] [-f] [--checkin] [--all-components] [cmd] ...");
+        pw.println("    --checkin: dump for a checkin");
+        pw.println("    -f: print details of intent filters");
+        pw.println("    -h: print this help");
+        pw.println("    --all-components: include all component names in package dump");
+        pw.println("  cmd may be one of:");
+        pw.println("    apex: list active APEXes and APEX session state");
+        pw.println("    l[ibraries]: list known shared libraries");
+        pw.println("    f[eatures]: list device features");
+        pw.println("    k[eysets]: print known keysets");
+        pw.println("    r[esolvers] [activity|service|receiver|content]: dump intent resolvers");
+        pw.println("    perm[issions]: dump permissions");
+        pw.println("    permission [name ...]: dump declaration and use of given permission");
+        pw.println("    pref[erred]: print preferred package settings");
+        pw.println("    preferred-xml [--full]: print preferred package settings as xml");
+        pw.println("    prov[iders]: dump content providers");
+        pw.println("    p[ackages]: dump installed packages");
+        pw.println("    q[ueries]: dump app queryability calculations");
+        pw.println("    s[hared-users]: dump shared user IDs");
+        pw.println("    m[essages]: print collected runtime messages");
+        pw.println("    v[erifiers]: print package verifier info");
+        pw.println("    d[omain-preferred-apps]: print domains preferred apps");
+        pw.println("    i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
+        pw.println("    t[imeouts]: print read timeouts for known digesters");
+        pw.println("    version: print database version info");
+        pw.println("    write: write current settings now");
+        pw.println("    installs: details about install sessions");
+        pw.println("    check-permission <permission> <package> [<user>]: does pkg hold perm?");
+        pw.println("    dexopt: dump dexopt state");
+        pw.println("    compiler-stats: dump compiler statistics");
+        pw.println("    service-permissions: dump permissions required by services");
+        pw.println("    snapshot: dump snapshot statistics");
+        pw.println("    protected-broadcasts: print list of protected broadcast actions");
+        pw.println("    known-packages: dump known packages");
+        pw.println("    <package.name>: info about given package");
+    }
+
+    private void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+        synchronized (mPm.mLock) {
+            final long requiredVerifierPackageToken =
+                    proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
+            proto.write(PackageServiceDumpProto.PackageShortProto.NAME,
+                    mPm.mRequiredVerifierPackage);
+            proto.write(
+                    PackageServiceDumpProto.PackageShortProto.UID,
+                    mPm.getPackageUid(
+                            mPm.mRequiredVerifierPackage,
+                            MATCH_DEBUG_TRIAGED_MISSING,
+                            UserHandle.USER_SYSTEM));
+            proto.end(requiredVerifierPackageToken);
+
+            DomainVerificationProxy proxy = mPm.mDomainVerificationManager.getProxy();
+            ComponentName verifierComponent = proxy.getComponentName();
+            if (verifierComponent != null) {
+                String verifierPackageName = verifierComponent.getPackageName();
+                final long verifierPackageToken =
+                        proto.start(PackageServiceDumpProto.VERIFIER_PACKAGE);
+                proto.write(PackageServiceDumpProto.PackageShortProto.NAME, verifierPackageName);
+                proto.write(
+                        PackageServiceDumpProto.PackageShortProto.UID,
+                        mPm.getPackageUid(
+                                verifierPackageName,
+                                MATCH_DEBUG_TRIAGED_MISSING,
+                                UserHandle.USER_SYSTEM));
+                proto.end(verifierPackageToken);
+            }
+
+            dumpSharedLibrariesProto(proto);
+            dumpFeaturesProto(proto);
+            mPm.mSettings.dumpPackagesProto(proto);
+            mPm.mSettings.dumpSharedUsersProto(proto);
+            dumpCriticalInfo(proto);
+        }
+        proto.flush();
+    }
+
+    private void dumpFeaturesProto(ProtoOutputStream proto) {
+        synchronized (mPm.mAvailableFeatures) {
+            final int count = mPm.mAvailableFeatures.size();
+            for (int i = 0; i < count; i++) {
+                mPm.mAvailableFeatures.valueAt(i).dumpDebug(proto,
+                        PackageServiceDumpProto.FEATURES);
+            }
+        }
+    }
+
+    private void dumpSharedLibrariesProto(ProtoOutputStream proto) {
+        final int count = mPm.mSharedLibraries.size();
+        for (int i = 0; i < count; i++) {
+            final String libName = mPm.mSharedLibraries.keyAt(i);
+            WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+                    mPm.mSharedLibraries.get(libName);
+            if (versionedLib == null) {
+                continue;
+            }
+            final int versionCount = versionedLib.size();
+            for (int j = 0; j < versionCount; j++) {
+                final SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
+                final long sharedLibraryToken =
+                        proto.start(PackageServiceDumpProto.SHARED_LIBRARIES);
+                proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libraryInfo.getName());
+                final boolean isJar = (libraryInfo.getPath() != null);
+                proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar);
+                if (isJar) {
+                    proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH,
+                            libraryInfo.getPath());
+                } else {
+                    proto.write(PackageServiceDumpProto.SharedLibraryProto.APK,
+                            libraryInfo.getPackageName());
+                }
+                proto.end(sharedLibraryToken);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/FileInstallArgs.java b/services/core/java/com/android/server/pm/FileInstallArgs.java
index cb174eb..02c8c12 100644
--- a/services/core/java/com/android/server/pm/FileInstallArgs.java
+++ b/services/core/java/com/android/server/pm/FileInstallArgs.java
@@ -71,7 +71,10 @@
         super(params);
     }
 
-    /** Existing install */
+    /**
+     * Create args that describe an existing installed package. Typically used
+     * when cleaning up old installs, or used as a move source.
+     */
     FileInstallArgs(String codePath, String[] instructionSets, PackageManagerService pm) {
         super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
                 null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 9ba69f8..5ff0a6f 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -35,8 +35,12 @@
 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
 import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
 import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
+import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
 import static com.android.server.pm.PackageManagerService.TAG;
 import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
 import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
@@ -55,6 +59,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.system.ErrnoException;
+import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -62,6 +67,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.F2fsUtils;
 import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.om.OverlayConfig;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
@@ -90,60 +96,136 @@
     private final RemovePackageHelper mRemovePackageHelper;
     private final AppDataHelper mAppDataHelper;
 
+    private final List<ScanPartition> mDirsToScanAsSystem;
+    private final int mScanFlags;
+    private final int mSystemParseFlags;
+    private final int mSystemScanFlags;
+
+    /**
+     * Tracks new system packages [received in an OTA] that we expect to
+     * find updated user-installed versions. Keys are package name, values
+     * are package location.
+     */
+    private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+
     // TODO(b/198166813): remove PMS dependency
     InitAndSystemPackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
             AppDataHelper appDataHelper) {
         mPm = pm;
         mRemovePackageHelper = removePackageHelper;
         mAppDataHelper = appDataHelper;
+        mDirsToScanAsSystem = getSystemScanPartitions();
+        // Set flag to monitor and not change apk file paths when scanning install directories.
+        int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
+        if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) {
+            mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
+        } else {
+            mScanFlags = scanFlags;
+        }
+        mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
+        mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
     }
 
+    private List<ScanPartition> getSystemScanPartitions() {
+        final List<ScanPartition> scanPartitions = new ArrayList<>();
+        scanPartitions.addAll(mPm.mInjector.getSystemPartitions());
+        scanPartitions.addAll(getApexScanPartitions());
+        Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
+        return scanPartitions;
+    }
+
+    private List<ScanPartition> getApexScanPartitions() {
+        final List<ScanPartition> scanPartitions = new ArrayList<>();
+        final List<ApexManager.ActiveApexInfo> activeApexInfos =
+                mPm.mApexManager.getActiveApexInfos();
+        for (int i = 0; i < activeApexInfos.size(); i++) {
+            final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
+            if (scanPartition != null) {
+                scanPartitions.add(scanPartition);
+            }
+        }
+        return scanPartitions;
+    }
+
+    private static @Nullable ScanPartition resolveApexToScanPartition(
+            ApexManager.ActiveApexInfo apexInfo) {
+        for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+            ScanPartition sp = SYSTEM_PARTITIONS.get(i);
+            if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
+                    sp.getFolder().getAbsolutePath())) {
+                return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
+            }
+        }
+        return null;
+    }
+
+    public OverlayConfig setUpSystemPackages(
+            WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds,
+            long startTime) {
+        PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser();
+
+        ExecutorService executorService = ParallelPackageParser.makeExecutorService();
+        // Prepare apex package info before scanning APKs, these information are needed when
+        // scanning apk in apex.
+        mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService);
+
+        scanSystemDirs(packageParser, executorService);
+        // Parse overlay configuration files to set default enable state, mutability, and
+        // priority of system overlays.
+        final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
+        for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) {
+            for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+                apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
+            }
+        }
+        OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
+                consumer -> mPm.forEachPackage(
+                        pkg -> consumer.accept(pkg, pkg.isSystem(),
+                          apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
+        cleanupSystemPackagesAndInstallStubs(packageParser, executorService, packageSettings,
+                startTime, userIds);
+        packageParser.close();
+        return overlayConfig;
+    }
     /**
      * First part of init dir scanning
      */
-    // TODO(b/197876467): consolidate this with cleanupSystemPackagesAndInstallStubs
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    public void scanSystemDirs(List<ScanPartition>  dirsToScanAsSystem,
-            boolean isUpgrade, PackageParser2 packageParser,
-            ExecutorService executorService, AndroidPackage platformPackage,
-            boolean isPreNMR1Upgrade, int systemParseFlags, int systemScanFlags) {
+    private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
         File frameworkDir = new File(Environment.getRootDirectory(), "framework");
 
         // Collect vendor/product/system_ext overlay packages. (Do this before scanning
         // any apps.)
         // For security and version matching reason, only consider overlay packages if they
         // reside in the right directory.
-        for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
-            final ScanPartition partition = dirsToScanAsSystem.get(i);
+        for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
+            final ScanPartition partition = mDirsToScanAsSystem.get(i);
             if (partition.getOverlayFolder() == null) {
                 continue;
             }
-            scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
-                    systemScanFlags | partition.scanFlag, 0,
-                    packageParser, executorService, platformPackage, isUpgrade,
-                    isPreNMR1Upgrade);
+            scanDirTracedLI(partition.getOverlayFolder(), mSystemParseFlags,
+                    mSystemScanFlags | partition.scanFlag, 0,
+                    packageParser, executorService);
         }
 
-        scanDirTracedLI(frameworkDir, systemParseFlags,
-                systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
-                packageParser, executorService, platformPackage, isUpgrade, isPreNMR1Upgrade);
+        scanDirTracedLI(frameworkDir, mSystemParseFlags,
+                mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
+                packageParser, executorService);
         if (!mPm.mPackages.containsKey("android")) {
             throw new IllegalStateException(
                     "Failed to load frameworks package; check log for warnings");
         }
 
-        for (int i = 0, size = dirsToScanAsSystem.size(); i < size; i++) {
-            final ScanPartition partition = dirsToScanAsSystem.get(i);
+        for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
+            final ScanPartition partition = mDirsToScanAsSystem.get(i);
             if (partition.getPrivAppFolder() != null) {
-                scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
-                        systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
-                        packageParser, executorService, platformPackage, isUpgrade,
-                        isPreNMR1Upgrade);
+                scanDirTracedLI(partition.getPrivAppFolder(), mSystemParseFlags,
+                        mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
+                        packageParser, executorService);
             }
-            scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
-                    systemScanFlags | partition.scanFlag, 0,
-                    packageParser, executorService, platformPackage, isUpgrade,
-                    isPreNMR1Upgrade);
+            scanDirTracedLI(partition.getAppFolder(), mSystemParseFlags,
+                    mSystemScanFlags | partition.scanFlag, 0,
+                    packageParser, executorService);
         }
     }
 
@@ -151,20 +233,17 @@
      * Second part of init dir scanning
      */
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    public void cleanupSystemPackagesAndInstallStubs(List<ScanPartition> dirsToScanAsSystem,
-            boolean isUpgrade, PackageParser2 packageParser,
-            ExecutorService executorService, boolean onlyCore,
+    private void cleanupSystemPackagesAndInstallStubs(PackageParser2 packageParser,
+            ExecutorService executorService,
             WatchedArrayMap<String, PackageSetting> packageSettings,
-            long startTime, File appInstallDir, AndroidPackage platformPackage,
-            boolean isPreNMR1Upgrade, int scanFlags, int systemParseFlags, int systemScanFlags,
-            int[] userIds) {
+            long startTime, int[] userIds) {
         // Prune any system packages that no longer exist.
         final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
         // Stub packages must either be replaced with full versions in the /data
         // partition or be disabled.
         final List<String> stubSystemApps = new ArrayList<>();
 
-        if (!onlyCore) {
+        if (!mPm.isOnlyCoreApps()) {
             // do this first before mucking with mPackages for the "expecting better" case
             final int numPackages = mPm.mPackages.size();
             for (int index = 0; index < numPackages; index++) {
@@ -209,7 +288,7 @@
                                         + "; scanned versionCode="
                                         + scannedPkg.getLongVersionCode());
                         mRemovePackageHelper.removePackageLI(scannedPkg, true);
-                        mPm.mExpectingBetter.put(ps.getPackageName(), ps.getPath());
+                        mExpectingBetter.put(ps.getPackageName(), ps.getPath());
                     }
 
                     continue;
@@ -233,9 +312,7 @@
                         // We're expecting that the system app should remain disabled, but add
                         // it to expecting better to recover in case the data version cannot
                         // be scanned.
-                        // TODO(b/197869066): mExpectingBetter should be able to pulled out into
-                        // this class and used only by the PMS initialization
-                        mPm.mExpectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
+                        mExpectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
                     }
                 }
             }
@@ -252,19 +329,18 @@
                 + " , timePerPackage: "
                 + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
                 + " , cached: " + cachedSystemApps);
-        if (isUpgrade && systemPackagesCount > 0) {
+        if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
             //CHECKSTYLE:OFF IndentationCheck
             FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
                     BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
                     systemScanTime / systemPackagesCount);
             //CHECKSTYLE:ON IndentationCheck
         }
-        if (!onlyCore) {
+        if (!mPm.isOnlyCoreApps()) {
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                     SystemClock.uptimeMillis());
-            scanDirTracedLI(appInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
-                    packageParser, executorService, platformPackage, isUpgrade,
-                    isPreNMR1Upgrade);
+            scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN, 0,
+                    packageParser, executorService);
 
         }
 
@@ -274,7 +350,7 @@
                     + unfinishedTasks);
         }
 
-        if (!onlyCore) {
+        if (!mPm.isOnlyCoreApps()) {
             final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
             // Remove disable package settings for updated system apps that were
             // removed via an OTA. If the update is no longer present, remove the
@@ -308,7 +384,7 @@
                     mRemovePackageHelper.removePackageLI(pkg, true);
                     try {
                         final File codePath = new File(pkg.getPath());
-                        scanPackageHelper.scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
+                        scanPackageHelper.scanPackageTracedLI(codePath, 0, mScanFlags, 0, null);
                     } catch (PackageManagerException e) {
                         Slog.e(TAG, "Failed to parse updated, ex-system package: "
                                 + e.getMessage());
@@ -332,27 +408,27 @@
              * the userdata partition actually showed up. If they never
              * appeared, crawl back and revive the system version.
              */
-            for (int i = 0; i < mPm.mExpectingBetter.size(); i++) {
-                final String packageName = mPm.mExpectingBetter.keyAt(i);
+            for (int i = 0; i < mExpectingBetter.size(); i++) {
+                final String packageName = mExpectingBetter.keyAt(i);
                 if (!mPm.mPackages.containsKey(packageName)) {
-                    final File scanFile = mPm.mExpectingBetter.valueAt(i);
+                    final File scanFile = mExpectingBetter.valueAt(i);
 
                     logCriticalInfo(Log.WARN, "Expected better " + packageName
                             + " but never showed up; reverting to system");
 
                     @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
                     @PackageManagerService.ScanFlags int rescanFlags = 0;
-                    for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
-                        final ScanPartition partition = dirsToScanAsSystem.get(i1);
+                    for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
+                        final ScanPartition partition = mDirsToScanAsSystem.get(i1);
                         if (partition.containsPrivApp(scanFile)) {
-                            reparseFlags = systemParseFlags;
-                            rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+                            reparseFlags = mSystemParseFlags;
+                            rescanFlags = mSystemScanFlags | SCAN_AS_PRIVILEGED
                                     | partition.scanFlag;
                             break;
                         }
                         if (partition.containsApp(scanFile)) {
-                            reparseFlags = systemParseFlags;
-                            rescanFlags = systemScanFlags | partition.scanFlag;
+                            reparseFlags = mSystemParseFlags;
+                            rescanFlags = mSystemScanFlags | partition.scanFlag;
                             break;
                         }
                     }
@@ -378,7 +454,7 @@
 
             // Uncompress and install any stubbed system applications.
             // This must be done last to ensure all stubs are replaced or disabled.
-            installSystemStubPackages(stubSystemApps, scanFlags);
+            installSystemStubPackages(stubSystemApps, mScanFlags);
 
             final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
                     - cachedSystemApps;
@@ -390,7 +466,7 @@
                     + " , timePerPackage: "
                     + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
                     + " , cached: " + cachedNonSystemApps);
-            if (isUpgrade && dataPackagesCount > 0) {
+            if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
                 //CHECKSTYLE:OFF IndentationCheck
                 FrameworkStatsLog.write(
                         FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
@@ -399,19 +475,17 @@
                 //CHECKSTYLE:OFF IndentationCheck
             }
         }
-        mPm.mExpectingBetter.clear();
+        mExpectingBetter.clear();
 
         mPm.mSettings.pruneRenamedPackagesLPw();
     }
 
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
     private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
-            long currentTime, PackageParser2 packageParser, ExecutorService executorService,
-            AndroidPackage platformPackage, boolean isUpgrade, boolean isPreNMR1Upgrade) {
+            long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
         try {
-            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService,
-                    platformPackage, isUpgrade, isPreNMR1Upgrade);
+            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -419,8 +493,7 @@
 
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
     private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
-            PackageParser2 packageParser, ExecutorService executorService,
-            AndroidPackage platformPackage, boolean isUpgrade, boolean isPreNMR1Upgrade) {
+            PackageParser2 packageParser, ExecutorService executorService) {
         final File[] files = scanDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             Log.d(TAG, "No files in app dir " + scanDir);
@@ -465,8 +538,7 @@
                 }
                 try {
                     scanPackageHelper.addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
-                            currentTime, null, platformPackage, isUpgrade,
-                            isPreNMR1Upgrade);
+                            currentTime, null);
                 } catch (PackageManagerException e) {
                     errorCode = e.error;
                     errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
@@ -564,9 +636,8 @@
      */
     @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
     public boolean enableCompressedPackage(AndroidPackage stubPkg,
-            @NonNull PackageSetting stubPkgSetting, int defParseFlags,
-            List<ScanPartition> dirsToScanAsSystem) {
-        final int parseFlags = defParseFlags | ParsingPackageUtils.PARSE_CHATTY
+            @NonNull PackageSetting stubPkgSetting) {
+        final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
                 | ParsingPackageUtils.PARSE_ENFORCE_CODE;
         synchronized (mPm.mInstallLock) {
             final AndroidPackage pkg;
@@ -599,7 +670,7 @@
                     installPackageFromSystemLIF(stubPkg.getPath(),
                             mPm.mUserManager.getUserIds() /*allUserHandles*/,
                             null /*origUserHandles*/,
-                            true /*writeSettings*/, defParseFlags, dirsToScanAsSystem);
+                            true /*writeSettings*/);
                 } catch (PackageManagerException pme) {
                     // Serious WTF; we have to be able to install the stub
                     Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
@@ -715,7 +786,7 @@
             // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
             // When we no longer need to read that setting, cblock release can occur always
             // occur here directly
-            if (!mPm.mSystemReady) {
+            if (!mPm.isSystemReady()) {
                 if (mPm.mReleaseOnSystemReady == null) {
                     mPm.mReleaseOnSystemReady = new ArrayList<>();
                 }
@@ -749,7 +820,7 @@
     public void restoreDisabledSystemPackageLIF(DeletePackageAction action,
             PackageSetting deletedPs, @NonNull int[] allUserHandles,
             @Nullable PackageRemovedInfo outInfo,
-            boolean writeSettings, int defParseFlags, List<ScanPartition> dirsToScanAsSystem,
+            boolean writeSettings,
             PackageSetting disabledPs)
             throws SystemDeleteException {
         // writer
@@ -768,8 +839,7 @@
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         try {
             installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
-                    outInfo == null ? null : outInfo.mOrigUsers, writeSettings, defParseFlags,
-                    dirsToScanAsSystem);
+                    outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
                     + e.getMessage());
@@ -808,17 +878,16 @@
      */
     @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
     private void installPackageFromSystemLIF(@NonNull String codePathString,
-            @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings,
-            int defParseFlags, List<ScanPartition> dirsToScanAsSystem)
+            @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
             throws PackageManagerException {
         final File codePath = new File(codePathString);
         @ParsingPackageUtils.ParseFlags int parseFlags =
-                defParseFlags
+                mPm.getDefParseFlags()
                         | ParsingPackageUtils.PARSE_MUST_BE_APK
                         | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
         @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
-        for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
-            ScanPartition partition = dirsToScanAsSystem.get(i);
+        for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
+            ScanPartition partition = mDirsToScanAsSystem.get(i);
             if (partition.containsFile(codePath)) {
                 scanFlags |= partition.scanFlag;
                 if (partition.containsPrivApp(codePath)) {
@@ -894,4 +963,8 @@
             }
         }
     }
+
+    public boolean isExpectingBetter(String packageName) {
+        return mExpectingBetter.containsKey(packageName);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
new file mode 100644
index 0000000..7569900
--- /dev/null
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -0,0 +1,880 @@
+/*
+ * 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.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
+import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.POST_INSTALL;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_IGNORE_FROZEN;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.backup.IBackupManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.DataLoaderType;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ComponentMutateUtils;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.os.Binder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.rollback.RollbackManagerInternal;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+final class InstallPackageHelper {
+    private final PackageManagerService mPm;
+    private final AppDataHelper mAppDataHelper;
+    private final PackageManagerServiceInjector mInjector;
+
+    // TODO(b/198166813): remove PMS dependency
+    InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
+        mPm = pm;
+        mInjector = pm.mInjector;
+        mAppDataHelper = appDataHelper;
+    }
+
+    InstallPackageHelper(PackageManagerService pm) {
+        this(pm, new AppDataHelper(pm));
+    }
+
+    InstallPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
+        mPm = pm;
+        mInjector = injector;
+        mAppDataHelper = new AppDataHelper(pm, mInjector);
+    }
+
+    @GuardedBy("mPm.mLock")
+    public Map<String, ReconciledPackage> reconcilePackagesLocked(
+            final ReconcileRequest request, KeySetManagerService ksms,
+            PackageManagerServiceInjector injector)
+            throws ReconcileFailure {
+        final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
+
+        final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
+
+        // make a copy of the existing set of packages so we can combine them with incoming packages
+        final ArrayMap<String, AndroidPackage> combinedPackages =
+                new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
+
+        combinedPackages.putAll(request.mAllPackages);
+
+        final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
+                new ArrayMap<>();
+
+        for (String installPackageName : scannedPackages.keySet()) {
+            final ScanResult scanResult = scannedPackages.get(installPackageName);
+
+            // add / replace existing with incoming packages
+            combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
+                    scanResult.mRequest.mParsedPackage);
+
+            // in the first pass, we'll build up the set of incoming shared libraries
+            final List<SharedLibraryInfo> allowedSharedLibInfos =
+                    SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
+                            request.mSharedLibrarySource);
+            final SharedLibraryInfo staticLib = scanResult.mStaticSharedLibraryInfo;
+            if (allowedSharedLibInfos != null) {
+                for (SharedLibraryInfo info : allowedSharedLibInfos) {
+                    if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
+                            incomingSharedLibraries, info)) {
+                        throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
+                                + " is being installed twice in this set!");
+                    }
+                }
+            }
+
+            // the following may be null if we're just reconciling on boot (and not during install)
+            final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
+            final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
+            final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
+            final boolean isInstall = installArgs != null;
+            if (isInstall && (res == null || prepareResult == null)) {
+                throw new ReconcileFailure("Reconcile arguments are not balanced for "
+                        + installPackageName + "!");
+            }
+
+            final DeletePackageAction deletePackageAction;
+            // we only want to try to delete for non system apps
+            if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
+                final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
+                final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+                        | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
+                deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
+                        prepareResult.mOriginalPs, prepareResult.mDisabledPs,
+                        deleteFlags, null /* all users */);
+                if (deletePackageAction == null) {
+                    throw new ReconcileFailure(
+                            PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+                            "May not delete " + installPackageName + " to replace");
+                }
+            } else {
+                deletePackageAction = null;
+            }
+
+            final int scanFlags = scanResult.mRequest.mScanFlags;
+            final int parseFlags = scanResult.mRequest.mParseFlags;
+            final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+
+            final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
+            final PackageSetting lastStaticSharedLibSetting =
+                    request.mLastStaticSharedLibSettings.get(installPackageName);
+            final PackageSetting signatureCheckPs =
+                    (prepareResult != null && lastStaticSharedLibSetting != null)
+                            ? lastStaticSharedLibSetting
+                            : scanResult.mPkgSetting;
+            boolean removeAppKeySetData = false;
+            boolean sharedUserSignaturesChanged = false;
+            SigningDetails signingDetails = null;
+            if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+                if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
+                    // We just determined the app is signed correctly, so bring
+                    // over the latest parsed certs.
+                } else {
+                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+                        throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                "Package " + parsedPackage.getPackageName()
+                                        + " upgrade keys do not match the previously installed"
+                                        + " version");
+                    } else {
+                        String msg = "System package " + parsedPackage.getPackageName()
+                                + " signature changed; retaining data.";
+                        PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+                    }
+                }
+                signingDetails = parsedPackage.getSigningDetails();
+            } else {
+                try {
+                    final Settings.VersionInfo versionInfo =
+                            request.mVersionInfos.get(installPackageName);
+                    final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
+                    final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
+                    final boolean isRollback = installArgs != null
+                            && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+                    final boolean compatMatch = verifySignatures(signatureCheckPs,
+                            disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
+                            compareRecover, isRollback);
+                    // The new KeySets will be re-added later in the scanning process.
+                    if (compatMatch) {
+                        removeAppKeySetData = true;
+                    }
+                    // We just determined the app is signed correctly, so bring
+                    // over the latest parsed certs.
+                    signingDetails = parsedPackage.getSigningDetails();
+
+                    // if this is is a sharedUser, check to see if the new package is signed by a
+                    // newer
+                    // signing certificate than the existing one, and if so, copy over the new
+                    // details
+                    if (signatureCheckPs.getSharedUser() != null) {
+                        // Attempt to merge the existing lineage for the shared SigningDetails with
+                        // the lineage of the new package; if the shared SigningDetails are not
+                        // returned this indicates the new package added new signers to the lineage
+                        // and/or changed the capabilities of existing signers in the lineage.
+                        SigningDetails sharedSigningDetails =
+                                signatureCheckPs.getSharedUser().signatures.mSigningDetails;
+                        SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
+                                signingDetails);
+                        if (mergedDetails != sharedSigningDetails) {
+                            signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+                                    mergedDetails;
+                        }
+                        if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
+                            signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
+                        }
+                    }
+                } catch (PackageManagerException e) {
+                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+                        throw new ReconcileFailure(e);
+                    }
+                    signingDetails = parsedPackage.getSigningDetails();
+
+                    // If the system app is part of a shared user we allow that shared user to
+                    // change
+                    // signatures as well as part of an OTA. We still need to verify that the
+                    // signatures
+                    // are consistent within the shared user for a given boot, so only allow
+                    // updating
+                    // the signatures on the first package scanned for the shared user (i.e. if the
+                    // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
+                    if (signatureCheckPs.getSharedUser() != null) {
+                        final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
+                                .signatures.mSigningDetails.getSignatures();
+                        if (signatureCheckPs.getSharedUser().signaturesChanged != null
+                                && compareSignatures(sharedUserSignatures,
+                                parsedPackage.getSigningDetails().getSignatures())
+                                != PackageManager.SIGNATURE_MATCH) {
+                            if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
+                                // Mismatched signatures is an error and silently skipping system
+                                // packages will likely break the device in unforeseen ways.
+                                // However, we allow the device to boot anyway because, prior to Q,
+                                // vendors were not expecting the platform to crash in this
+                                // situation.
+                                // This WILL be a hard failure on any new API levels after Q.
+                                throw new ReconcileFailure(
+                                        INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                                        "Signature mismatch for shared user: "
+                                                + scanResult.mPkgSetting.getSharedUser());
+                            } else {
+                                // Treat mismatched signatures on system packages using a shared
+                                // UID as
+                                // fatal for the system overall, rather than just failing to install
+                                // whichever package happened to be scanned later.
+                                throw new IllegalStateException(
+                                        "Signature mismatch on system package "
+                                                + parsedPackage.getPackageName()
+                                                + " for shared user "
+                                                + scanResult.mPkgSetting.getSharedUser());
+                            }
+                        }
+
+                        sharedUserSignaturesChanged = true;
+                        signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+                                parsedPackage.getSigningDetails();
+                        signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
+                    }
+                    // File a report about this.
+                    String msg = "System package " + parsedPackage.getPackageName()
+                            + " signature changed; retaining data.";
+                    PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+                } catch (IllegalArgumentException e) {
+                    // should never happen: certs matched when checking, but not when comparing
+                    // old to new for sharedUser
+                    throw new RuntimeException(
+                            "Signing certificates comparison made on incomparable signing details"
+                                    + " but somehow passed verifySignatures!", e);
+                }
+            }
+
+            result.put(installPackageName,
+                    new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
+                            res, request.mPreparedPackages.get(installPackageName), scanResult,
+                            deletePackageAction, allowedSharedLibInfos, signingDetails,
+                            sharedUserSignaturesChanged, removeAppKeySetData));
+        }
+
+        for (String installPackageName : scannedPackages.keySet()) {
+            // Check all shared libraries and map to their actual file path.
+            // We only do this here for apps not on a system dir, because those
+            // are the only ones that can fail an install due to this.  We
+            // will take care of the system apps by updating all of their
+            // library paths after the scan is done. Also during the initial
+            // scan don't update any libs as we do this wholesale after all
+            // apps are scanned to avoid dependency based scanning.
+            final ScanResult scanResult = scannedPackages.get(installPackageName);
+            if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
+                    || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
+                    != 0) {
+                continue;
+            }
+            try {
+                result.get(installPackageName).mCollectedSharedLibraryInfos =
+                        SharedLibraryHelper.collectSharedLibraryInfos(
+                                scanResult.mRequest.mParsedPackage,
+                                combinedPackages, request.mSharedLibrarySource,
+                                incomingSharedLibraries, injector.getCompatibility());
+
+            } catch (PackageManagerException e) {
+                throw new ReconcileFailure(e.error, e.getMessage());
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Commits the package scan and modifies system state.
+     * <p><em>WARNING:</em> The method may throw an exception in the middle
+     * of committing the package, leaving the system in an inconsistent state.
+     * This needs to be fixed so, once we get to this point, no errors are
+     * possible and the system is not left in an inconsistent state.
+     */
+    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+    public AndroidPackage commitReconciledScanResultLocked(
+            @NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
+        final ScanResult result = reconciledPkg.mScanResult;
+        final ScanRequest request = result.mRequest;
+        // TODO(b/135203078): Move this even further away
+        ParsedPackage parsedPackage = request.mParsedPackage;
+        if ("android".equals(parsedPackage.getPackageName())) {
+            // TODO(b/135203078): Move this to initial parse
+            parsedPackage.setVersionCode(mPm.getSdkVersion())
+                    .setVersionCodeMajor(0);
+        }
+
+        final AndroidPackage oldPkg = request.mOldPkg;
+        final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
+        final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
+        final PackageSetting oldPkgSetting = request.mOldPkgSetting;
+        final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
+        final UserHandle user = request.mUser;
+        final String realPkgName = request.mRealPkgName;
+        final List<String> changedAbiCodePath = result.mChangedAbiCodePath;
+        final PackageSetting pkgSetting;
+        if (request.mPkgSetting != null && request.mPkgSetting.getSharedUser() != null
+                && request.mPkgSetting.getSharedUser() != result.mPkgSetting.getSharedUser()) {
+            // shared user changed, remove from old shared user
+            request.mPkgSetting.getSharedUser().removePackage(request.mPkgSetting);
+        }
+        if (result.mExistingSettingCopied) {
+            pkgSetting = request.mPkgSetting;
+            pkgSetting.updateFrom(result.mPkgSetting);
+        } else {
+            pkgSetting = result.mPkgSetting;
+            if (originalPkgSetting != null) {
+                mPm.mSettings.addRenamedPackageLPw(
+                        AndroidPackageUtils.getRealPackageOrNull(parsedPackage),
+                        originalPkgSetting.getPackageName());
+                mPm.mTransferredPackages.add(originalPkgSetting.getPackageName());
+            } else {
+                mPm.mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
+            }
+        }
+        if (pkgSetting.getSharedUser() != null) {
+            pkgSetting.getSharedUser().addPackage(pkgSetting);
+        }
+        if (reconciledPkg.mInstallArgs != null
+                && reconciledPkg.mInstallArgs.mForceQueryableOverride) {
+            pkgSetting.setForceQueryableOverride(true);
+        }
+
+        // If this is part of a standard install, set the initiating package name, else rely on
+        // previous device state.
+        if (reconciledPkg.mInstallArgs != null) {
+            InstallSource installSource = reconciledPkg.mInstallArgs.mInstallSource;
+            if (installSource.initiatingPackageName != null) {
+                final PackageSetting ips = mPm.mSettings.getPackageLPr(
+                        installSource.initiatingPackageName);
+                if (ips != null) {
+                    installSource = installSource.setInitiatingPackageSignatures(
+                            ips.getSignatures());
+                }
+            }
+            pkgSetting.setInstallSource(installSource);
+        }
+
+        // TODO(toddke): Consider a method specifically for modifying the Package object
+        // post scan; or, moving this stuff out of the Package object since it has nothing
+        // to do with the package on disk.
+        // We need to have this here because addUserToSettingLPw() is sometimes responsible
+        // for creating the application ID. If we did this earlier, we would be saving the
+        // correct ID.
+        parsedPackage.setUid(pkgSetting.getAppId());
+        final AndroidPackage pkg = parsedPackage.hideAsFinal();
+
+        mPm.mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
+
+        if (realPkgName != null) {
+            mPm.mTransferredPackages.add(pkg.getPackageName());
+        }
+
+        if (reconciledPkg.mCollectedSharedLibraryInfos != null) {
+            mPm.executeSharedLibrariesUpdateLPr(pkg, pkgSetting, null, null,
+                    reconciledPkg.mCollectedSharedLibraryInfos, allUsers);
+        }
+
+        final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+        if (reconciledPkg.mRemoveAppKeySetData) {
+            ksms.removeAppKeySetDataLPw(pkg.getPackageName());
+        }
+        if (reconciledPkg.mSharedUserSignaturesChanged) {
+            pkgSetting.getSharedUser().signaturesChanged = Boolean.TRUE;
+            pkgSetting.getSharedUser().signatures.mSigningDetails = reconciledPkg.mSigningDetails;
+        }
+        pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails);
+
+        if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
+            for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
+                final String codePathString = changedAbiCodePath.get(i);
+                try {
+                    mPm.mInstaller.rmdex(codePathString,
+                            getDexCodeInstructionSet(getPreferredInstructionSet()));
+                } catch (Installer.InstallerException ignored) {
+                }
+            }
+        }
+
+        final int userId = user == null ? 0 : user.getIdentifier();
+        // Modify state for the given package setting
+        commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
+                (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
+        if (pkgSetting.getInstantApp(userId)) {
+            mPm.mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.getAppId());
+        }
+        pkgSetting.setStatesOnCommit();
+
+        return pkg;
+    }
+
+    /**
+     * Adds a scanned package to the system. When this method is finished, the package will
+     * be available for query, resolution, etc...
+     */
+    private void commitPackageSettings(@NonNull AndroidPackage pkg, @Nullable AndroidPackage oldPkg,
+            @NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting,
+            final @PackageManagerService.ScanFlags int scanFlags, boolean chatty,
+            ReconciledPackage reconciledPkg) {
+        final String pkgName = pkg.getPackageName();
+        if (mPm.mCustomResolverComponentName != null
+                && mPm.mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
+            mPm.setUpCustomResolverActivity(pkg, pkgSetting);
+        }
+
+        if (pkg.getPackageName().equals("android")) {
+            mPm.setPlatformPackage(pkg, pkgSetting);
+        }
+
+        ArrayList<AndroidPackage> clientLibPkgs = null;
+        // writer
+        synchronized (mPm.mLock) {
+            if (!ArrayUtils.isEmpty(reconciledPkg.mAllowedSharedLibraryInfos)) {
+                for (SharedLibraryInfo info : reconciledPkg.mAllowedSharedLibraryInfos) {
+                    mPm.commitSharedLibraryInfoLocked(info);
+                }
+                final Map<String, AndroidPackage> combinedSigningDetails =
+                        reconciledPkg.getCombinedAvailablePackages();
+                try {
+                    // Shared libraries for the package need to be updated.
+                    mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
+                            combinedSigningDetails);
+                } catch (PackageManagerException e) {
+                    Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
+                }
+                // Update all applications that use this library. Skip when booting
+                // since this will be done after all packages are scaned.
+                if ((scanFlags & SCAN_BOOTING) == 0) {
+                    clientLibPkgs = mPm.updateAllSharedLibrariesLocked(pkg, pkgSetting,
+                            combinedSigningDetails);
+                }
+            }
+        }
+        if (reconciledPkg.mInstallResult != null) {
+            reconciledPkg.mInstallResult.mLibraryConsumers = clientLibPkgs;
+        }
+
+        if ((scanFlags & SCAN_BOOTING) != 0) {
+            // No apps can run during boot scan, so they don't need to be frozen
+        } else if ((scanFlags & SCAN_DONT_KILL_APP) != 0) {
+            // Caller asked to not kill app, so it's probably not frozen
+        } else if ((scanFlags & SCAN_IGNORE_FROZEN) != 0) {
+            // Caller asked us to ignore frozen check for some reason; they
+            // probably didn't know the package name
+        } else {
+            // We're doing major surgery on this package, so it better be frozen
+            // right now to keep it from launching
+            mPm.checkPackageFrozen(pkgName);
+        }
+
+        // Also need to kill any apps that are dependent on the library.
+        if (clientLibPkgs != null) {
+            for (int i = 0; i < clientLibPkgs.size(); i++) {
+                AndroidPackage clientPkg = clientLibPkgs.get(i);
+                mPm.killApplication(clientPkg.getPackageName(),
+                        clientPkg.getUid(), "update lib");
+            }
+        }
+
+        // writer
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
+
+        synchronized (mPm.mLock) {
+            // We don't expect installation to fail beyond this point
+            // Add the new setting to mSettings
+            mPm.mSettings.insertPackageSettingLPw(pkgSetting, pkg);
+            // Add the new setting to mPackages
+            mPm.mPackages.put(pkg.getPackageName(), pkg);
+            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
+                mPm.mApexManager.registerApkInApex(pkg);
+            }
+
+            // Add the package's KeySets to the global KeySetManagerService
+            KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+            ksms.addScannedPackageLPw(pkg);
+
+            mPm.mComponentResolver.addAllComponents(pkg, chatty);
+            final boolean isReplace =
+                    reconciledPkg.mPrepareResult != null && reconciledPkg.mPrepareResult.mReplace;
+            mPm.mAppsFilter.addPackage(pkgSetting, isReplace);
+            mPm.addAllPackageProperties(pkg);
+
+            if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) {
+                mPm.mDomainVerificationManager.addPackage(pkgSetting);
+            } else {
+                mPm.mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting);
+            }
+
+            int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
+            StringBuilder r = null;
+            int i;
+            for (i = 0; i < collectionSize; i++) {
+                ParsedInstrumentation a = pkg.getInstrumentations().get(i);
+                ComponentMutateUtils.setPackageName(a, pkg.getPackageName());
+                mPm.addInstrumentation(a.getComponentName(), a);
+                if (chatty) {
+                    if (r == null) {
+                        r = new StringBuilder(256);
+                    } else {
+                        r.append(' ');
+                    }
+                    r.append(a.getName());
+                }
+            }
+            if (r != null) {
+                if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Instrumentation: " + r);
+            }
+
+            final List<String> protectedBroadcasts = pkg.getProtectedBroadcasts();
+            if (!protectedBroadcasts.isEmpty()) {
+                synchronized (mPm.mProtectedBroadcasts) {
+                    mPm.mProtectedBroadcasts.addAll(protectedBroadcasts);
+                }
+            }
+
+            mPm.mPermissionManager.onPackageAdded(pkg,
+                    (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
+        }
+
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+    }
+
+    /**
+     * If the database version for this type of package (internal storage or
+     * external storage) is less than the version where package signatures
+     * were updated, return true.
+     */
+    public boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
+        return isCompatSignatureUpdateNeeded(mPm.getSettingsVersionForPackage(pkg));
+    }
+
+    public static boolean isCompatSignatureUpdateNeeded(Settings.VersionInfo ver) {
+        return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_END_ENTITY;
+    }
+
+    public boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
+        return isRecoverSignatureUpdateNeeded(mPm.getSettingsVersionForPackage(pkg));
+    }
+
+    public static boolean isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver) {
+        return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
+    }
+
+    public int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
+            @PackageManager.InstallFlags int installFlags,
+            @PackageManager.InstallReason int installReason,
+            @Nullable List<String> allowlistedRestrictedPermissions,
+            @Nullable IntentSender intentSender) {
+        if (DEBUG_INSTALL) {
+            Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
+                    + " installFlags=" + installFlags + " installReason=" + installReason
+                    + " allowlistedRestrictedPermissions=" + allowlistedRestrictedPermissions);
+        }
+
+        final int callingUid = Binder.getCallingUid();
+        if (mPm.mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
+                != PackageManager.PERMISSION_GRANTED
+                && mPm.mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Neither user " + callingUid + " nor current process has "
+                    + android.Manifest.permission.INSTALL_PACKAGES + ".");
+        }
+        PackageSetting pkgSetting;
+        mPm.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                true /* checkShell */, "installExistingPackage for user " + userId);
+        if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
+            return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
+        }
+
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            boolean installed = false;
+            final boolean instantApp =
+                    (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+            final boolean fullApp =
+                    (installFlags & PackageManager.INSTALL_FULL_APP) != 0;
+
+            // writer
+            synchronized (mPm.mLock) {
+                pkgSetting = mPm.mSettings.getPackageLPr(packageName);
+                if (pkgSetting == null) {
+                    return PackageManager.INSTALL_FAILED_INVALID_URI;
+                }
+                if (!mPm.canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
+                    // only allow the existing package to be used if it's installed as a full
+                    // application for at least one user
+                    boolean installAllowed = false;
+                    for (int checkUserId : mPm.mUserManager.getUserIds()) {
+                        installAllowed = !pkgSetting.getInstantApp(checkUserId);
+                        if (installAllowed) {
+                            break;
+                        }
+                    }
+                    if (!installAllowed) {
+                        return PackageManager.INSTALL_FAILED_INVALID_URI;
+                    }
+                }
+                if (!pkgSetting.getInstalled(userId)) {
+                    pkgSetting.setInstalled(true, userId);
+                    pkgSetting.setHidden(false, userId);
+                    pkgSetting.setInstallReason(installReason, userId);
+                    pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId);
+                    mPm.mSettings.writePackageRestrictionsLPr(userId);
+                    mPm.mSettings.writeKernelMappingLPr(pkgSetting);
+                    installed = true;
+                } else if (fullApp && pkgSetting.getInstantApp(userId)) {
+                    // upgrade app from instant to full; we don't allow app downgrade
+                    installed = true;
+                }
+                mPm.setInstantAppForUser(mInjector, pkgSetting, userId, instantApp, fullApp);
+            }
+
+            if (installed) {
+                if (pkgSetting.getPkg() != null) {
+                    final PermissionManagerServiceInternal.PackageInstalledParams.Builder
+                            permissionParamsBuilder =
+                            new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
+                    if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
+                            != 0) {
+                        permissionParamsBuilder.setAllowlistedRestrictedPermissions(
+                                pkgSetting.getPkg().getRequestedPermissions());
+                    }
+                    mPm.mPermissionManager.onPackageInstalled(pkgSetting.getPkg(),
+                            Process.INVALID_UID /* previousAppId */,
+                            permissionParamsBuilder.build(), userId);
+                }
+
+                if (pkgSetting.getPkg() != null) {
+                    synchronized (mPm.mInstallLock) {
+                        // We don't need to freeze for a brand new install
+                        mAppDataHelper.prepareAppDataAfterInstallLIF(pkgSetting.getPkg());
+                    }
+                }
+                mPm.sendPackageAddedForUser(packageName, pkgSetting, userId, DataLoaderType.NONE);
+                synchronized (mPm.mLock) {
+                    mPm.updateSequenceNumberLP(pkgSetting, new int[]{ userId });
+                }
+                // start async restore with no post-install since we finish install here
+                PackageInstalledInfo res = new PackageInstalledInfo(
+                        PackageManager.INSTALL_SUCCEEDED);
+                res.mPkg = pkgSetting.getPkg();
+                res.mNewUsers = new int[]{ userId };
+
+                PostInstallData postInstallData =
+                        new PostInstallData(null, res, () -> {
+                            mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
+                                    userId);
+                            if (intentSender != null) {
+                                onRestoreComplete(res.mReturnCode, mPm.mContext, intentSender);
+                            }
+                        });
+                restoreAndPostInstall(userId, res, postInstallData);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+
+    private static void onRestoreComplete(int returnCode, Context context, IntentSender target) {
+        Intent fillIn = new Intent();
+        fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                PackageManager.installStatusToPublicStatus(returnCode));
+        try {
+            target.sendIntent(context, 0, fillIn, null, null);
+        } catch (IntentSender.SendIntentException ignored) {
+        }
+    }
+
+    /** @param data Post-install is performed only if this is non-null. */
+    public void restoreAndPostInstall(
+            int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
+        if (DEBUG_INSTALL) {
+            Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.mPkg);
+        }
+
+        // A restore should be requested at this point if (a) the install
+        // succeeded, (b) the operation is not an update.
+        final boolean update = res.mRemovedInfo != null
+                && res.mRemovedInfo.mRemovedPackage != null;
+        boolean doRestore = !update && res.mPkg != null;
+
+        // Set up the post-install work request bookkeeping.  This will be used
+        // and cleaned up by the post-install event handling regardless of whether
+        // there's a restore pass performed.  Token values are >= 1.
+        int token;
+        if (mPm.mNextInstallToken < 0) mPm.mNextInstallToken = 1;
+        token = mPm.mNextInstallToken++;
+        if (data != null) {
+            mPm.mRunningInstalls.put(token, data);
+        } else if (DEBUG_INSTALL) {
+            Log.v(TAG, "No post-install required for " + token);
+        }
+
+        if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
+
+        if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
+            // Pass responsibility to the Backup Manager.  It will perform a
+            // restore if appropriate, then pass responsibility back to the
+            // Package Manager to run the post-install observer callbacks
+            // and broadcasts.
+            if (res.mFreezer != null) {
+                res.mFreezer.close();
+            }
+            doRestore = performBackupManagerRestore(userId, token, res);
+        }
+
+        // If this is an update to a package that might be potentially downgraded, then we
+        // need to check with the rollback manager whether there's any userdata that might
+        // need to be snapshotted or restored for the package.
+        //
+        // TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
+        if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
+            doRestore = performRollbackManagerRestore(userId, token, res, data);
+        }
+
+        if (!doRestore) {
+            // No restore possible, or the Backup Manager was mysteriously not
+            // available -- just fire the post-install work request directly.
+            if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
+
+            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);
+
+            Message msg = mPm.mHandler.obtainMessage(POST_INSTALL, token, 0);
+            mPm.mHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Perform Backup Manager restore for a given {@link PackageInstalledInfo}.
+     * Returns whether the restore successfully completed.
+     */
+    private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
+        IBackupManager bm = IBackupManager.Stub.asInterface(
+                ServiceManager.getService(Context.BACKUP_SERVICE));
+        if (bm != null) {
+            // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
+            // in the BackupManager. USER_ALL is used in compatibility tests.
+            if (userId == UserHandle.USER_ALL) {
+                userId = UserHandle.USER_SYSTEM;
+            }
+            if (DEBUG_INSTALL) {
+                Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
+            }
+            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
+            try {
+                if (bm.isUserReadyForBackup(userId)) {
+                    bm.restoreAtInstallForUser(
+                            userId, res.mPkg.getPackageName(), token);
+                } else {
+                    Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
+                            + "didn't take place.");
+                    return false;
+                }
+            } catch (RemoteException e) {
+                // can't happen; the backup manager is local
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception trying to enqueue restore", e);
+                return false;
+            }
+        } else {
+            Slog.e(TAG, "Backup Manager not found!");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Perform Rollback Manager restore for a given {@link PackageInstalledInfo}.
+     * Returns whether the restore successfully completed.
+     */
+    private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
+            PostInstallData data) {
+        RollbackManagerInternal rm = mInjector.getLocalService(RollbackManagerInternal.class);
+
+        final String packageName = res.mPkg.getPackageName();
+        final int[] allUsers = mPm.mUserManager.getUserIds();
+        final int[] installedUsers;
+
+        final PackageSetting ps;
+        int appId = -1;
+        long ceDataInode = -1;
+        synchronized (mPm.mLock) {
+            ps = mPm.mSettings.getPackageLPr(packageName);
+            if (ps != null) {
+                appId = ps.getAppId();
+                ceDataInode = ps.getCeDataInode(userId);
+            }
+
+            // NOTE: We ignore the user specified in the InstallParam because we know this is
+            // an update, and hence need to restore data for all installed users.
+            installedUsers = ps.queryInstalledUsers(allUsers, true);
+        }
+
+        boolean doSnapshotOrRestore = data != null && data.args != null
+                && ((data.args.mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
+                || (data.args.mInstallFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
+
+        if (ps != null && doSnapshotOrRestore) {
+            final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
+            rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
+                    appId, ceDataInode, seInfo, token);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 676d12e..bfb5f76 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -87,6 +87,7 @@
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ComponentMutateUtils;
 import android.content.pm.parsing.component.ParsedPermission;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
 import android.content.pm.parsing.result.ParseResult;
@@ -163,6 +164,7 @@
     final int mDataLoaderType;
     final long mRequiredInstalledVersionCode;
     final PackageLite mPackageLite;
+    final InstallPackageHelper mInstallPackageHelper;
 
     InstallParams(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
             int installFlags, InstallSource installSource, String volumeUuid,
@@ -187,6 +189,7 @@
         mDataLoaderType = DataLoaderType.NONE;
         mRequiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
         mPackageLite = packageLite;
+        mInstallPackageHelper = new InstallPackageHelper(mPm);
     }
 
     InstallParams(File stagedDir, IPackageInstallObserver2 observer,
@@ -213,6 +216,7 @@
                 ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
         mRequiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
         mPackageLite = packageLite;
+        mInstallPackageHelper = new InstallPackageHelper(mPm);
     }
 
     @Override
@@ -463,7 +467,7 @@
                 }
             }
             for (InstallRequest request : apkInstallRequests) {
-                mPm.restoreAndPostInstall(request.mArgs.mUser.getIdentifier(),
+                mInstallPackageHelper.restoreAndPostInstall(request.mArgs.mUser.getIdentifier(),
                         request.mInstallResult,
                         new PostInstallData(request.mArgs,
                                 request.mInstallResult, null));
@@ -624,7 +628,7 @@
                 Map<String, ReconciledPackage> reconciledPackages;
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
-                    reconciledPackages = mPm.reconcilePackagesLocked(
+                    reconciledPackages = mInstallPackageHelper.reconcilePackagesLocked(
                             reconcileRequest, mPm.mSettings.getKeySetManagerService(),
                             mPm.mInjector);
                 } catch (ReconcileFailure e) {
@@ -730,7 +734,7 @@
 
         // Retrieve PackageSettings and parse package
         @ParsingPackageUtils.ParseFlags final int parseFlags =
-                mPm.mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
+                mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
                 | ParsingPackageUtils.PARSE_ENFORCE_CODE
                 | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
 
@@ -882,10 +886,10 @@
                     }
                 } else {
                     try {
-                        final boolean compareCompat = mPm.isCompatSignatureUpdateNeeded(
-                                parsedPackage);
-                        final boolean compareRecover = mPm.isRecoverSignatureUpdateNeeded(
-                                parsedPackage);
+                        final boolean compareCompat =
+                                mInstallPackageHelper.isCompatSignatureUpdateNeeded(parsedPackage);
+                        final boolean compareRecover =
+                                mInstallPackageHelper.isRecoverSignatureUpdateNeeded(parsedPackage);
                         // We don't care about disabledPkgSetting on install for now.
                         final boolean compatMatch = verifySignatures(signatureCheckPs, null,
                                 parsedPackage.getSigningDetails(), compareCompat, compareRecover,
@@ -945,7 +949,7 @@
                     Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()
                             + " attempting to delcare ephemeral permission "
                             + perm.getName() + "; Removing ephemeral.");
-                    perm.setProtectionLevel(
+                    ComponentMutateUtils.setProtectionLevel(perm,
                             perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT);
                 }
 
@@ -985,7 +989,8 @@
                                         + " trying to change a non-runtime permission "
                                         + perm.getName()
                                         + " to runtime; keeping old protection level");
-                                perm.setProtectionLevel(bp.getProtectionLevel());
+                                ComponentMutateUtils.setProtectionLevel(perm,
+                                        bp.getProtectionLevel());
                             }
                         }
                     }
@@ -1561,13 +1566,13 @@
                         // We didn't need to disable the .apk as a current system package,
                         // which means we are replacing another update that is already
                         // installed.  We need to make sure to delete the older one's .apk.
-                        res.mRemovedInfo.mArgs = mPm.createInstallArgsForExisting(
+                        res.mRemovedInfo.mArgs = new FileInstallArgs(
                                 oldPackage.getPath(),
                                 getAppDexInstructionSets(
                                         AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
                                                 deletedPkgSetting),
                                         AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
-                                                deletedPkgSetting)));
+                                                deletedPkgSetting)), mPm);
                     } else {
                         res.mRemovedInfo.mArgs = null;
                     }
@@ -1627,8 +1632,8 @@
                 }
             }
 
-            AndroidPackage pkg = mPm.commitReconciledScanResultLocked(reconciledPkg,
-                    request.mAllUsers);
+            AndroidPackage pkg = mInstallPackageHelper.commitReconciledScanResultLocked(
+                    reconciledPkg, request.mAllUsers);
             updateSettingsLI(pkg, reconciledPkg, request.mAllUsers, res);
 
             final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
deleted file mode 100644
index 399b03c..0000000
--- a/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2015 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.server.pm;
-
-import java.util.Arrays;
-
-/**
- * This is the key for the map of {@link android.content.pm.IntentFilterVerificationInfo}s
- * maintained by the  {@link com.android.server.pm.PackageManagerService}
- */
-class IntentFilterVerificationKey {
-    public String domains;
-    public String packageName;
-    public String className;
-
-    public IntentFilterVerificationKey(String[] domains, String packageName, String className) {
-        StringBuilder sb = new StringBuilder();
-        for (String host : domains) {
-            sb.append(host);
-        }
-        this.domains = sb.toString();
-        this.packageName = packageName;
-        this.className = className;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        IntentFilterVerificationKey that = (IntentFilterVerificationKey) o;
-
-        if (domains != null ? !domains.equals(that.domains) : that.domains != null) return false;
-        if (className != null ? !className.equals(that.className) : that.className != null)
-            return false;
-        if (packageName != null ? !packageName.equals(that.packageName) : that.packageName != null)
-            return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = domains != null ? domains.hashCode() : 0;
-        result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
-        result = 31 * result + (className != null ? className.hashCode() : 0);
-        return result;
-    }
-}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
deleted file mode 100644
index ead399b..0000000
--- a/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2015 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.server.pm;
-
-
-import java.util.List;
-
-/* package private */ class IntentFilterVerificationResponse {
-    public final int callerUid;
-    public final int code;
-    public final List<String> failedDomains;
-
-    public IntentFilterVerificationResponse(int callerUid, int code, List<String> failedDomains) {
-        this.callerUid = callerUid;
-        this.code = code;
-        this.failedDomains = failedDomains;
-    }
-
-    public String getFailedDomainsString() {
-        StringBuilder sb = new StringBuilder();
-        for (String domain : failedDomains) {
-            if (sb.length() > 0) {
-                sb.append(" ");
-            }
-            sb.append(domain);
-        }
-        return sb.toString();
-    }
-}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
deleted file mode 100644
index 9dc545a..0000000
--- a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2015 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.server.pm;
-
-import android.content.pm.PackageManager;
-import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.util.ArraySet;
-import android.util.Slog;
-
-import java.util.ArrayList;
-
-public class IntentFilterVerificationState {
-    static final String TAG = IntentFilterVerificationState.class.getName();
-
-    public final static int STATE_UNDEFINED = 0;
-    public final static int STATE_VERIFICATION_PENDING = 1;
-    public final static int STATE_VERIFICATION_SUCCESS = 2;
-    public final static int STATE_VERIFICATION_FAILURE = 3;
-
-    private int mRequiredVerifierUid = 0;
-
-    private int mState;
-
-    private ArrayList<ParsedIntentInfo> mFilters = new ArrayList<>();
-    private ArraySet<String> mHosts = new ArraySet<>();
-    private int mUserId;
-
-    private String mPackageName;
-    private boolean mVerificationComplete;
-
-    public IntentFilterVerificationState(int verifierUid, int userId, String packageName) {
-        mRequiredVerifierUid = verifierUid;
-        mUserId = userId;
-        mPackageName = packageName;
-        mState = STATE_UNDEFINED;
-        mVerificationComplete = false;
-    }
-
-    public void setState(int state) {
-        if (state > STATE_VERIFICATION_FAILURE || state < STATE_UNDEFINED) {
-            mState = STATE_UNDEFINED;
-        } else {
-            mState = state;
-        }
-    }
-
-    public int getState() {
-        return mState;
-    }
-
-    public void setPendingState() {
-        setState(STATE_VERIFICATION_PENDING);
-    }
-
-    public ArrayList<ParsedIntentInfo> getFilters() {
-        return mFilters;
-    }
-
-    public boolean isVerificationComplete() {
-        return mVerificationComplete;
-    }
-
-    public boolean isVerified() {
-        if (mVerificationComplete) {
-            return (mState == STATE_VERIFICATION_SUCCESS);
-        }
-        return false;
-    }
-
-    public int getUserId() {
-        return mUserId;
-    }
-
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    public String getHostsString() {
-        StringBuilder sb = new StringBuilder();
-        final int count = mHosts.size();
-        for (int i=0; i<count; i++) {
-            if (i > 0) {
-                sb.append(" ");
-            }
-            String host = mHosts.valueAt(i);
-            // "*.example.tld" is validated via https://example.tld
-            if (host.startsWith("*.")) {
-                host = host.substring(2);
-            }
-            sb.append(host);
-        }
-        return sb.toString();
-    }
-
-    public boolean setVerifierResponse(int callerUid, int code) {
-        if (mRequiredVerifierUid == callerUid) {
-            int state = STATE_UNDEFINED;
-            if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
-                state = STATE_VERIFICATION_SUCCESS;
-            } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
-                state = STATE_VERIFICATION_FAILURE;
-            }
-            mVerificationComplete = true;
-            setState(state);
-            return true;
-        }
-        Slog.d(TAG, "Cannot set verifier response with callerUid:" + callerUid + " and code:" +
-                code + " as required verifierUid is:" + mRequiredVerifierUid);
-        return false;
-    }
-
-    public void addFilter(ParsedIntentInfo filter) {
-        mFilters.add(filter);
-        mHosts.addAll(filter.getHostsList());
-    }
-}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 375ffc4..9c11cd4 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1263,7 +1263,7 @@
 
         /** Returns whether or not the given appId is in allow list */
         private static boolean isCallingAppIdAllowed(int[] appIdAllowList, @AppIdInt int appId) {
-            if (appIdAllowList == null) {
+            if (appIdAllowList == null || appId < Process.FIRST_APPLICATION_UID) {
                 return true;
             }
             return Arrays.binarySearch(appIdAllowList, appId) > -1;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 9595e15..68801d6 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -128,7 +128,7 @@
                 PLATFORM_PACKAGE_NAME.equals(pkgSetting.getPkg().getPackageName());
         synchronized (mPackageManagerService.mLock) {
             // Important: the packages we need to run with ab-ota compiler-reason.
-            important = PackageManagerServiceUtils.getPackagesForDexopt(
+            important = DexOptHelper.getPackagesForDexopt(
                     mPackageManagerService.mSettings.getPackagesLocked().values(),
                     mPackageManagerService, DEBUG_DEXOPT);
             // Remove Platform Package from A/B OTA b/160735835.
@@ -160,7 +160,7 @@
         long spaceAvailable = getAvailableSpace();
         if (spaceAvailable < BULK_DELETE_THRESHOLD) {
             Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
-                    + PackageManagerServiceUtils.packagesToString(others));
+                    + DexOptHelper.packagesToString(others));
             for (PackageSetting pkg : others) {
                 mPackageManagerService.deleteOatArtifactsOfPackage(pkg.getPackageName());
             }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d89af91..2595bb4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1115,9 +1115,10 @@
 
     @Override
     public void installExistingPackage(String packageName, int installFlags, int installReason,
-            IntentSender statusReceiver, int userId, List<String> whiteListedPermissions) {
-        mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason,
-                whiteListedPermissions, statusReceiver);
+            IntentSender statusReceiver, int userId, List<String> allowListedPermissions) {
+        final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
+        installPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+                installReason, allowListedPermissions, statusReceiver);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 020c23d..3805977 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2406,13 +2406,6 @@
         final VerificationParams verifyingSession = prepareForVerification();
         if (isMultiPackage()) {
             final List<PackageInstallerSession> childSessions = getChildSessions();
-            // Spot check to reject a non-staged multi package install of APEXes and APKs.
-            if (!params.isStaged && containsApkSession()
-                    && sessionContains(s -> s.isApexSession())) {
-                throw new PackageManagerException(
-                    PackageManager.INSTALL_FAILED_SESSION_INVALID,
-                    "Non-staged multi package install of APEX and APK packages is not supported");
-            }
             List<VerificationParams> verifyingChildSessions =
                     new ArrayList<>(childSessions.size());
             boolean success = true;
@@ -4172,6 +4165,12 @@
                     + childSession.sessionId + " and session " + sessionId
                     + " have inconsistent rollback settings");
         }
+        boolean hasAPK = containsApkSession() || !childSession.isApexSession();
+        boolean hasAPEX = sessionContains(s -> s.isApexSession()) || childSession.isApexSession();
+        if (!params.isStaged && hasAPK && hasAPEX) {
+            throw new IllegalStateException("Mix of APK and APEX is not supported for "
+                    + "non-staged multi-package session");
+        }
 
         try {
             acquireTransactionLock();
diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java
new file mode 100644
index 0000000..37daf11
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerNative.java
@@ -0,0 +1,237 @@
+/*
+ * 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.server.pm;
+
+import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageChangeObserver;
+import android.content.pm.IPackageManagerNative;
+import android.content.pm.IStagedApexObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.StagedApexInfo;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.Arrays;
+
+final class PackageManagerNative extends IPackageManagerNative.Stub {
+    private final PackageManagerService mPm;
+
+    PackageManagerNative(PackageManagerService pm) {
+        mPm = pm;
+    }
+
+    @Override
+    public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+        synchronized (mPm.mPackageChangeObservers) {
+            try {
+                observer.asBinder().linkToDeath(
+                        new PackageChangeObserverDeathRecipient(observer), 0);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.getMessage());
+            }
+            mPm.mPackageChangeObservers.add(observer);
+            Log.d(TAG, "Size of mPackageChangeObservers after registry is "
+                    + mPm.mPackageChangeObservers.size());
+        }
+    }
+
+    @Override
+    public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+        synchronized (mPm.mPackageChangeObservers) {
+            mPm.mPackageChangeObservers.remove(observer);
+            Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
+                    + mPm.mPackageChangeObservers.size());
+        }
+    }
+
+    @Override
+    public String[] getAllPackages() {
+        return mPm.getAllPackages().toArray(new String[0]);
+    }
+
+    @Override
+    public String[] getNamesForUids(int[] uids) throws RemoteException {
+        String[] names = null;
+        String[] results = null;
+        try {
+            if (uids == null || uids.length == 0) {
+                return null;
+            }
+            names = mPm.getNamesForUids(uids);
+            results = (names != null) ? names : new String[uids.length];
+            // massage results so they can be parsed by the native binder
+            for (int i = results.length - 1; i >= 0; --i) {
+                if (results[i] == null) {
+                    results[i] = "";
+                }
+            }
+            return results;
+        } catch (Throwable t) {
+            // STOPSHIP(186558987): revert addition of try/catch/log
+            Slog.e(TAG, "uids: " + Arrays.toString(uids));
+            Slog.e(TAG, "names: " + Arrays.toString(names));
+            Slog.e(TAG, "results: " + Arrays.toString(results));
+            Slog.e(TAG, "throwing exception", t);
+            throw t;
+        }
+    }
+
+    // NB: this differentiates between preloads and sideloads
+    @Override
+    public String getInstallerForPackage(String packageName) throws RemoteException {
+        final String installerName = mPm.getInstallerPackageName(packageName);
+        if (!TextUtils.isEmpty(installerName)) {
+            return installerName;
+        }
+        // differentiate between preload and sideload
+        int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+        ApplicationInfo appInfo = mPm.getApplicationInfo(packageName,
+                /*flags*/ 0,
+                /*userId*/ callingUser);
+        if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+            return "preload";
+        }
+        return "";
+    }
+
+    @Override
+    public long getVersionCodeForPackage(String packageName) throws RemoteException {
+        try {
+            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+            PackageInfo pInfo = mPm.getPackageInfo(packageName, 0, callingUser);
+            if (pInfo != null) {
+                return pInfo.getLongVersionCode();
+            }
+        } catch (Exception e) {
+        }
+        return 0;
+    }
+
+    @Override
+    public int getTargetSdkVersionForPackage(String packageName) throws RemoteException {
+        int targetSdk = mPm.getTargetSdkVersion(packageName);
+        if (targetSdk != -1) {
+            return targetSdk;
+        }
+
+        throw new RemoteException("Couldn't get targetSdkVersion for package " + packageName);
+    }
+
+    @Override
+    public boolean isPackageDebuggable(String packageName) throws RemoteException {
+        int callingUser = UserHandle.getCallingUserId();
+        ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0, callingUser);
+        if (appInfo != null) {
+            return (0 != (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE));
+        }
+
+        throw new RemoteException("Couldn't get debug flag for package " + packageName);
+    }
+
+    @Override
+    public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
+            throws RemoteException {
+        int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+        boolean[] results = new boolean[packageNames.length];
+        for (int i = results.length - 1; i >= 0; --i) {
+            ApplicationInfo appInfo = mPm.getApplicationInfo(packageNames[i], 0, callingUser);
+            results[i] = appInfo != null && appInfo.isAudioPlaybackCaptureAllowed();
+        }
+        return results;
+    }
+
+    @Override
+    public int getLocationFlags(String packageName) throws RemoteException {
+        int callingUser = UserHandle.getUserId(Binder.getCallingUid());
+        ApplicationInfo appInfo = mPm.getApplicationInfo(packageName,
+                /*flags*/ 0,
+                /*userId*/ callingUser);
+        if (appInfo == null) {
+            throw new RemoteException(
+                    "Couldn't get ApplicationInfo for package " + packageName);
+        }
+        return ((appInfo.isSystemApp() ? IPackageManagerNative.LOCATION_SYSTEM : 0)
+                | (appInfo.isVendor() ? IPackageManagerNative.LOCATION_VENDOR : 0)
+                | (appInfo.isProduct() ? IPackageManagerNative.LOCATION_PRODUCT : 0));
+    }
+
+    @Override
+    public String getModuleMetadataPackageName() throws RemoteException {
+        return mPm.getModuleMetadataPackageName();
+    }
+
+    @Override
+    public boolean hasSha256SigningCertificate(String packageName, byte[] certificate)
+            throws RemoteException {
+        return mPm.hasSigningCertificate(packageName, certificate, CERT_INPUT_SHA256);
+    }
+
+    @Override
+    public boolean hasSystemFeature(String featureName, int version) {
+        return mPm.hasSystemFeature(featureName, version);
+    }
+
+    @Override
+    public void registerStagedApexObserver(IStagedApexObserver observer) {
+        mPm.mInstallerService.getStagingManager().registerStagedApexObserver(observer);
+    }
+
+    @Override
+    public void unregisterStagedApexObserver(IStagedApexObserver observer) {
+        mPm.mInstallerService.getStagingManager().unregisterStagedApexObserver(observer);
+    }
+
+    @Override
+    public String[] getStagedApexModuleNames() {
+        return mPm.mInstallerService.getStagingManager()
+                .getStagedApexModuleNames().toArray(new String[0]);
+    }
+
+    @Override
+    @Nullable
+    public StagedApexInfo getStagedApexInfo(String moduleName) {
+        return mPm.mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
+    }
+
+    private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
+        private final IPackageChangeObserver mObserver;
+
+        PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
+            mObserver = observer;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mPm.mPackageChangeObservers) {
+                mPm.mPackageChangeObservers.remove(mObserver);
+                Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
+                        + mPm.mPackageChangeObservers.size());
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7607fa4..3eaa26e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -21,7 +21,6 @@
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
 import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.content.Intent.CATEGORY_DEFAULT;
 import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
 import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -29,10 +28,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
@@ -45,7 +40,6 @@
 import static android.content.pm.PackageManager.TYPE_PROVIDER;
 import static android.content.pm.PackageManager.TYPE_RECEIVER;
 import static android.content.pm.PackageManager.TYPE_UNKNOWN;
-import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
@@ -54,14 +48,10 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 
 import android.Manifest;
 import android.annotation.AppIdInt;
@@ -75,10 +65,8 @@
 import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
 import android.app.IActivityManager;
-import android.app.PendingIntent;
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
-import android.app.backup.IBackupManager;
 import android.app.role.RoleManager;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
@@ -87,7 +75,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IIntentReceiver;
-import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
@@ -111,10 +98,8 @@
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageLoadingProgressCallback;
 import android.content.pm.IPackageManager;
-import android.content.pm.IPackageManagerNative;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
-import android.content.pm.IStagedApexObserver;
 import android.content.pm.IncrementalStatesInfo;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppInfo;
@@ -148,19 +133,18 @@
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
-import android.content.pm.StagedApexInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.TestUtilityService;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
-import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.IArtManager;
 import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedActivityImpl;
 import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.component.ParsedProvider;
 import android.content.pm.pkg.PackageUserState;
@@ -205,7 +189,6 @@
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 import android.security.KeyStore;
-import android.service.pm.PackageServiceDumpProto;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -215,17 +198,13 @@
 import android.util.ExceptionUtils;
 import android.util.IntArray;
 import android.util.Log;
-import android.util.LogPrinter;
-import android.util.PackageUtils;
 import android.util.Pair;
-import android.util.PrintStreamPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
-import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 
 import com.android.internal.R;
@@ -235,7 +214,6 @@
 import com.android.internal.content.F2fsUtils;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.content.om.OverlayConfig;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -243,7 +221,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.FunctionalUtils;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.permission.persistence.RuntimePermissionsPersistence;
 import com.android.server.EventLogTags;
@@ -255,17 +232,13 @@
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
 import com.android.server.apphibernation.AppHibernationManagerInternal;
-import com.android.server.apphibernation.AppHibernationService;
 import com.android.server.compat.CompatChange;
 import com.android.server.compat.PlatformCompat;
-import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.ArtUtils;
 import com.android.server.pm.dex.DexManager;
-import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.dex.ViewCompiler;
 import com.android.server.pm.parsing.PackageCacher;
@@ -285,7 +258,6 @@
 import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
-import com.android.server.rollback.RollbackManagerInternal;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.SnapshotCache;
@@ -303,9 +275,6 @@
 import libcore.util.EmptyArray;
 import libcore.util.HexEncoding;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -315,7 +284,6 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.Certificate;
@@ -324,7 +292,6 @@
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -336,9 +303,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
@@ -400,7 +365,6 @@
     public static final boolean DEBUG_PACKAGE_SCANNING = false;
     static final boolean DEBUG_VERIFY = false;
     public static final boolean DEBUG_PERMISSIONS = false;
-    private static final boolean DEBUG_SHARED_LIBRARIES = false;
     public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
     public static final boolean TRACE_SNAPSHOTS = false;
     private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false;
@@ -413,9 +377,6 @@
     static final boolean DEBUG_ABI_SELECTION = false;
     public static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
 
-    /** REMOVE. According to Svet, this was only used to reset permissions during development. */
-    static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
-
     static final boolean HIDE_EPHEMERAL_APIS = false;
 
     static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
@@ -482,40 +443,40 @@
         PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface PackageStartability {}
+    private @interface PackageStartability {}
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is allowed to start.
      */
-    static final int PACKAGE_STARTABILITY_OK = 0;
+    private static final int PACKAGE_STARTABILITY_OK = 0;
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is <b>not</b> allowed to start because it's not found
      * (could be due to that package is invisible to the given user).
      */
-    static final int PACKAGE_STARTABILITY_NOT_FOUND = 1;
+    private static final int PACKAGE_STARTABILITY_NOT_FOUND = 1;
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is <b>not</b> allowed to start because it's not a system app
      * and the system is running in safe mode.
      */
-    static final int PACKAGE_STARTABILITY_NOT_SYSTEM = 2;
+    private static final int PACKAGE_STARTABILITY_NOT_SYSTEM = 2;
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is <b>not</b> allowed to start because it's currently frozen.
      */
-    static final int PACKAGE_STARTABILITY_FROZEN = 3;
+    private static final int PACKAGE_STARTABILITY_FROZEN = 3;
 
     /**
      * Used as the result code of the {@link #getPackageStartability} to indicate
      * the given package is <b>not</b> allowed to start because it doesn't support
      * direct boot.
      */
-    static final int PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED = 4;
+    private static final int PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED = 4;
 
     private static final String STATIC_SHARED_LIB_DELIMITER = "_";
     /** Extension of the compressed packages */
@@ -584,23 +545,6 @@
     private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
             150857253;
 
-    /**
-     * Apps targeting Android S and above need to declare dependencies to the public native
-     * shared libraries that are defined by the device maker using {@code uses-native-library} tag
-     * in its {@code AndroidManifest.xml}.
-     *
-     * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
-     * the package manager rejects to install the app. The dependency can be specified as optional
-     * using {@code android:required} attribute in the tag, in which case failing to satisfy the
-     * dependency doesn't stop the installation.
-     * <p>Once installed, an app is provided with only the native shared libraries that are
-     * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
-     * in the app manifest will fail even if it actually exists on the device.
-     */
-    @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
-    private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
-
     public static final String PLATFORM_PACKAGE_NAME = "android";
 
     static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
@@ -627,19 +571,6 @@
 
     public static final int REASON_LAST = REASON_SHARED;
 
-    /**
-     * The initial enabled state of the cache before other checks are done.
-     */
-    private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
-
-    /**
-     * Whether to skip all other checks and force the cache to be enabled.
-     *
-     * Setting this to true will cause the cache to be named "debug" to avoid eviction from
-     * build fingerprint changes.
-     */
-    private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false;
-
     static final String RANDOM_DIR_PREFIX = "~~";
 
     final Handler mHandler;
@@ -648,20 +579,17 @@
 
     private final boolean mEnableFreeCacheV2;
 
-    final int mSdkVersion;
+    private final int mSdkVersion;
     final Context mContext;
     final boolean mFactoryTest;
-    final boolean mOnlyCore;
+    private final boolean mOnlyCore;
     final DisplayMetrics mMetrics;
-    final int mDefParseFlags;
-    final String[] mSeparateProcesses;
-    final boolean mIsUpgrade;
-    final boolean mIsPreNUpgrade;
-    final boolean mIsPreNMR1Upgrade;
-    final boolean mIsPreQUpgrade;
-
-    @GuardedBy("mLock")
-    private boolean mDexOptDialogShown;
+    private final int mDefParseFlags;
+    private final String[] mSeparateProcesses;
+    private final boolean mIsUpgrade;
+    private final boolean mIsPreNUpgrade;
+    private final boolean mIsPreNMR1Upgrade;
+    private final boolean mIsPreQUpgrade;
 
     // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
     // LOCK HELD.  Can be called with mInstallLock held.
@@ -709,13 +637,6 @@
                                    "PackageManagerService.mIsolatedOwners");
 
     /**
-     * Tracks new system packages [received in an OTA] that we expect to
-     * find updated user-installed versions. Keys are package name, values
-     * are package location.
-     */
-    final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
-
-    /**
      * Tracks existing packages prior to receiving an OTA. Keys are package name.
      * Only non-null during an OTA, and even then it is nulled again once systemReady().
      */
@@ -761,7 +682,7 @@
     @GuardedBy("mLoadedVolumes")
     final ArraySet<String> mLoadedVolumes = new ArraySet<>();
 
-    boolean mFirstBoot;
+    private boolean mFirstBoot;
 
     final boolean mIsEngBuild;
     private final boolean mIsUserDebugBuild;
@@ -807,12 +728,10 @@
     public static final List<ScanPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
             PackagePartitions.getOrderedPartitions(ScanPartition::new));
 
-    private final List<ScanPartition> mDirsToScanAsSystem;
-
-    final OverlayConfig mOverlayConfig;
+    private @NonNull final OverlayConfig mOverlayConfig;
 
     @GuardedBy("itself")
-    final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
+    final ArrayList<IPackageChangeObserver> mPackageChangeObservers =
         new ArrayList<>();
 
     // Cached parsed flag value. Invalidated on each flag change.
@@ -919,7 +838,7 @@
     int mPendingEnableRollbackToken = 0;
 
     @Watched(manual = true)
-    volatile boolean mSystemReady;
+    private volatile boolean mSystemReady;
     @Watched(manual = true)
     private volatile boolean mSafeMode;
     @Watched
@@ -927,16 +846,16 @@
             new WatchedSparseBooleanArray();
 
     @Watched(manual = true)
-    ApplicationInfo mAndroidApplication;
+    private ApplicationInfo mAndroidApplication;
     @Watched(manual = true)
-    final ActivityInfo mResolveActivity = new ActivityInfo();
-    final ResolveInfo mResolveInfo = new ResolveInfo();
+    private final ActivityInfo mResolveActivity = new ActivityInfo();
+    private final ResolveInfo mResolveInfo = new ResolveInfo();
     @Watched(manual = true)
-    private ComponentName mResolveComponentName;
-    AndroidPackage mPlatformPackage;
+    ComponentName mResolveComponentName;
+    private AndroidPackage mPlatformPackage;
     ComponentName mCustomResolverComponentName;
 
-    boolean mResolverReplaced = false;
+    private boolean mResolverReplaced = false;
 
     @NonNull
     final DomainVerificationManagerInternal mDomainVerificationManager;
@@ -1023,10 +942,6 @@
     final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
     int mNextInstallToken = 1;  // nonzero; will be wrapped back to 1 when ++ overflows
 
-    // XML tags for backup/restore of various bits of state
-    private static final String TAG_PREFERRED_BACKUP = "pa";
-    private static final String TAG_DEFAULT_APPS = "da";
-
     final @Nullable String mRequiredVerifierPackage;
     final @NonNull String mRequiredInstallerPackage;
     final @NonNull String mRequiredUninstallerPackage;
@@ -1055,6 +970,9 @@
     private final DeletePackageHelper mDeletePackageHelper;
     private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
     private final AppDataHelper mAppDataHelper;
+    private final PreferredActivityHelper mPreferredActivityHelper;
+    private final ResolveIntentHelper mResolveIntentHelper;
+    private final DexOptHelper mDexOptHelper;
 
     /**
      * Invalidate the package info cache, which includes updating the cached computer.
@@ -1282,7 +1200,7 @@
      * Report a locally-detected change to observers.  The <what> parameter is left null,
      * but it signifies that the change was detected by PackageManagerService itself.
      */
-    private static void onChanged() {
+    static void onChanged() {
         onChange(null);
     }
 
@@ -1449,7 +1367,7 @@
         scheduleWritePackageRestrictionsLocked(userId);
     }
 
-    private void scheduleWritePackageRestrictionsLocked(int userId) {
+    void scheduleWritePackageRestrictionsLocked(int userId) {
         invalidatePackageInfoCache();
         final int[] userIds = (userId == UserHandle.USER_ALL)
                 ? mUserManager.getUserIds() : new int[]{userId};
@@ -1582,15 +1500,15 @@
         injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_R_CHANGES,
                 selinuxChangeListener);
 
-        m.installWhitelistedSystemPackages();
+        m.installAllowlistedSystemPackages();
         ServiceManager.addService("package", m);
-        final PackageManagerNative pmn = m.new PackageManagerNative();
+        final PackageManagerNative pmn = new PackageManagerNative(m);
         ServiceManager.addService("package_native", pmn);
         return m;
     }
 
     /** Install/uninstall system packages for all users based on their user-type, as applicable. */
-    private void installWhitelistedSystemPackages() {
+    private void installAllowlistedSystemPackages() {
         synchronized (mLock) {
             final boolean scheduleWrite = mUserManager.installWhitelistedSystemPackages(
                     isFirstBoot(), isDeviceUpgrading(), mExistingPackages);
@@ -1601,39 +1519,6 @@
         }
     }
 
-    /**
-     * Requests that files preopted on a secondary system partition be copied to the data partition
-     * if possible.  Note that the actual copying of the files is accomplished by init for security
-     * reasons. This simply requests that the copy takes place and awaits confirmation of its
-     * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
-     */
-    private static void requestCopyPreoptedFiles() {
-        final int WAIT_TIME_MS = 100;
-        final String CP_PREOPT_PROPERTY = "sys.cppreopt";
-        if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
-            SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
-            // We will wait for up to 100 seconds.
-            final long timeStart = SystemClock.uptimeMillis();
-            final long timeEnd = timeStart + 100 * 1000;
-            long timeNow = timeStart;
-            while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
-                try {
-                    Thread.sleep(WAIT_TIME_MS);
-                } catch (InterruptedException e) {
-                    // Do nothing
-                }
-                timeNow = SystemClock.uptimeMillis();
-                if (timeNow > timeEnd) {
-                    SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
-                    Slog.wtf(TAG, "cppreopt did not finish!");
-                    break;
-                }
-            }
-
-            Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
-        }
-    }
-
     // Link watchables to the class
     private void registerObserver() {
         mPackages.registerObserver(mWatcher);
@@ -1683,7 +1568,6 @@
         mDefaultAppProvider = testParams.defaultAppProvider;
         mLegacyPermissionManager = testParams.legacyPermissionManagerInternal;
         mDexManager = testParams.dexManager;
-        mDirsToScanAsSystem = testParams.dirsToScanAsSystem;
         mFactoryTest = testParams.factoryTest;
         mIncrementalManager = testParams.incrementalManager;
         mInstallerService = testParams.installerService;
@@ -1744,13 +1628,14 @@
         mIncrementalVersion = testParams.incrementalVersion;
         mDomainVerificationConnection = new DomainVerificationConnection(this);
 
-        mBroadcastHelper = new BroadcastHelper(mInjector);
-        mAppDataHelper = new AppDataHelper(this);
-        mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
-        mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this, mRemovePackageHelper,
-                mAppDataHelper);
-        mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
-                mInitAndSystemPackageHelper, mAppDataHelper);
+        mBroadcastHelper = testParams.broadcastHelper;
+        mAppDataHelper = testParams.appDataHelper;
+        mRemovePackageHelper = testParams.removePackageHelper;
+        mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
+        mDeletePackageHelper = testParams.deletePackageHelper;
+        mPreferredActivityHelper = testParams.preferredActivityHelper;
+        mResolveIntentHelper = testParams.resolveIntentHelper;
+        mDexOptHelper = testParams.dexOptHelper;
 
         invalidatePackageInfoCache();
     }
@@ -1873,22 +1758,8 @@
         mApexManager = injector.getApexManager();
         mAppsFilter = mInjector.getAppsFilter();
 
-        final List<ScanPartition> scanPartitions = new ArrayList<>();
-        final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
-        for (int i = 0; i < activeApexInfos.size(); i++) {
-            final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
-            if (scanPartition != null) {
-                scanPartitions.add(scanPartition);
-            }
-        }
-
         mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager, mPmInternal);
 
-        mDirsToScanAsSystem = new ArrayList<>();
-        mDirsToScanAsSystem.addAll(injector.getSystemPartitions());
-        mDirsToScanAsSystem.addAll(scanPartitions);
-        Slog.d(TAG, "Directories scanned as system partitions: " + mDirsToScanAsSystem);
-
         mAppInstallDir = new File(Environment.getDataDirectory(), "app");
         mAppLib32InstallDir = getAppLib32InstallDir();
 
@@ -1903,6 +1774,9 @@
                 mAppDataHelper);
         mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
                 mInitAndSystemPackageHelper, mAppDataHelper);
+        mPreferredActivityHelper = new PreferredActivityHelper(this);
+        mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
+        mDexOptHelper = new DexOptHelper(this);
 
         synchronized (mLock) {
             // Create the computer as soon as the state objects have been installed.  The
@@ -1967,7 +1841,7 @@
             mPermissionManager.readLegacyPermissionStateTEMP();
 
             if (!mOnlyCore && mFirstBoot) {
-                requestCopyPreoptedFiles();
+                DexOptHelper.requestCopyPreoptedFiles();
             }
 
             String customResolverActivityName = Resources.getSystem().getString(
@@ -2024,40 +1898,12 @@
                 }
             }
 
-            mCacheDir = preparePackageParserCache(mIsEngBuild);
+            mCacheDir = PackageManagerServiceUtils.preparePackageParserCache(
+                    mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
 
-            // Set flag to monitor and not change apk file paths when
-            // scanning install directories.
-            int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
-
-            if (mIsUpgrade || mFirstBoot) {
-                scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
-            }
-
-            final int systemParseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
-            final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
-
-            PackageParser2 packageParser = injector.getScanningCachingPackageParser();
-
-            ExecutorService executorService = ParallelPackageParser.makeExecutorService();
-            // Prepare apex package info before scanning APKs, these information are needed when
-            // scanning apk in apex.
-            mApexManager.scanApexPackagesTraced(packageParser, executorService);
-
-            mInitAndSystemPackageHelper.scanSystemDirs(mDirsToScanAsSystem, mIsUpgrade,
-                    packageParser, executorService, mPlatformPackage, mIsPreNMR1Upgrade,
-                    systemParseFlags, systemScanFlags);
-            // Parse overlay configuration files to set default enable state, mutability, and
-            // priority of system overlays.
-            mOverlayConfig = OverlayConfig.initializeSystemInstance(
-                    consumer -> mPmInternal.forEachPackage(
-                            pkg -> consumer.accept(pkg, pkg.isSystem())));
             final int[] userIds = mUserManager.getUserIds();
-            mInitAndSystemPackageHelper.cleanupSystemPackagesAndInstallStubs(mDirsToScanAsSystem,
-                    mIsUpgrade, packageParser, executorService, mOnlyCore, packageSettings,
-                    startTime, mAppInstallDir, mPlatformPackage, mIsPreNMR1Upgrade,
-                    scanFlags, systemParseFlags, systemScanFlags, userIds);
-            packageParser.close();
+            mOverlayConfig = mInitAndSystemPackageHelper.setUpSystemPackages(packageSettings,
+                    userIds, startTime);
 
             // Resolve the storage manager.
             mStorageManagerPackage = getStorageManagerPackageName();
@@ -2113,7 +1959,7 @@
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                     SystemClock.uptimeMillis());
             Slog.i(TAG, "Time to scan packages: "
-                    + ((SystemClock.uptimeMillis()-startTime)/1000f)
+                    + ((SystemClock.uptimeMillis() - startTime) / 1000f)
                     + " seconds");
 
             // If the build fingerprint has changed since the last time we booted,
@@ -2164,7 +2010,7 @@
             // Legacy existing (installed before Q) non-system apps to hide
             // their icons in launcher.
             if (!mOnlyCore && mIsPreQUpgrade) {
-                Slog.i(TAG, "Whitelisting all existing apps to hide their icons");
+                Slog.i(TAG, "Allowlisting all existing apps to hide their icons");
                 int size = packageSettings.size();
                 for (int i = 0; i < size; i++) {
                     final PackageSetting ps = packageSettings.valueAt(i);
@@ -2316,80 +2162,6 @@
         setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
     }
 
-    private @Nullable File preparePackageParserCache(boolean forEngBuild) {
-        if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
-            if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
-                return null;
-            }
-
-            // Disable package parsing on eng builds to allow for faster incremental development.
-            if (forEngBuild) {
-                return null;
-            }
-
-            if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
-                Slog.i(TAG, "Disabling package parser cache due to system property.");
-                return null;
-            }
-        }
-
-        // The base directory for the package parser cache lives under /data/system/.
-        final File cacheBaseDir = Environment.getPackageCacheDirectory();
-        if (!FileUtils.createDir(cacheBaseDir)) {
-            return null;
-        }
-
-        // There are several items that need to be combined together to safely
-        // identify cached items. In particular, changing the value of certain
-        // feature flags should cause us to invalidate any caches.
-        final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
-                : SystemProperties.digestOf("ro.build.fingerprint");
-
-        // Reconcile cache directories, keeping only what we'd actually use.
-        for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
-            if (Objects.equals(cacheName, cacheDir.getName())) {
-                Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
-            } else {
-                Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
-                FileUtils.deleteContentsAndDir(cacheDir);
-            }
-        }
-
-        // Return the versioned package cache directory.
-        File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
-
-        if (cacheDir == null) {
-            // Something went wrong. Attempt to delete everything and return.
-            Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
-            FileUtils.deleteContentsAndDir(cacheBaseDir);
-            return null;
-        }
-
-        // The following is a workaround to aid development on non-numbered userdebug
-        // builds or cases where "adb sync" is used on userdebug builds. If we detect that
-        // the system partition is newer.
-        //
-        // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
-        // that starts with "eng." to signify that this is an engineering build and not
-        // destined for release.
-        if (mIsUserDebugBuild && mIncrementalVersion.startsWith("eng.")) {
-            Slog.w(TAG, "Wiping cache directory because the system partition changed.");
-
-            // Heuristic: If the /system directory has been modified recently due to an "adb sync"
-            // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
-            // in general and should not be used for production changes. In this specific case,
-            // we know that they will work.
-            File frameworkDir =
-                    new File(Environment.getRootDirectory(), "framework");
-            if (cacheDir.lastModified() < frameworkDir.lastModified()) {
-                FileUtils.deleteContents(cacheBaseDir);
-                cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
-            }
-        }
-
-        return cacheDir;
-    }
-
     @Override
     public boolean isFirstBoot() {
         // allow instant applications
@@ -2413,9 +2185,10 @@
     private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
         final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
 
-        final List<ResolveInfo> matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
-                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                UserHandle.USER_SYSTEM, Binder.getCallingUid());
+        final List<ResolveInfo> matches =
+                mResolveIntentHelper.queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
+                        MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                        UserHandle.USER_SYSTEM, Binder.getCallingUid());
         if (matches.size() == 1) {
             return matches.get(0).getComponentInfo().packageName;
         } else if (matches.size() == 0) {
@@ -2509,9 +2282,10 @@
     private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
         final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
 
-        final List<ResolveInfo> matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
-                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                UserHandle.USER_SYSTEM, Binder.getCallingUid());
+        final List<ResolveInfo> matches =
+                mResolveIntentHelper.queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
+                        MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                        UserHandle.USER_SYSTEM, Binder.getCallingUid());
         ResolveInfo best = null;
         final int N = matches.size();
         for (int i = 0; i < N; i++) {
@@ -2537,9 +2311,10 @@
     @Nullable
     private ComponentName getDomainVerificationAgentComponentNameLPr() {
         Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION);
-        List<ResolveInfo> matches = queryIntentReceiversInternal(intent, null,
-                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                UserHandle.USER_SYSTEM, Binder.getCallingUid());
+        List<ResolveInfo> matches =
+                mResolveIntentHelper.queryIntentReceiversInternal(intent, null,
+                        MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                        UserHandle.USER_SYSTEM, Binder.getCallingUid());
         ResolveInfo best = null;
         final int N = matches.size();
         for (int i = 0; i < N; i++) {
@@ -3005,7 +2780,6 @@
                 filterCallingUid, userId);
     }
 
-
     @Override
     public void deletePreloadsFileCache() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE,
@@ -3261,7 +3035,7 @@
      * action and a {@code android.intent.category.BROWSABLE} category</li>
      * </ul>
      */
-    private int updateFlagsForResolve(int flags, int userId, int callingUid,
+    int updateFlagsForResolve(int flags, int userId, int callingUid,
             boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
         return mComputer.updateFlagsForResolve(flags, userId, callingUid,
                 wantInstantApps, isImplicitImageCaptureIntentAndNotSetByDpc);
@@ -3324,8 +3098,9 @@
                 return false;
             }
             for (int i=0; i< a.getIntents().size(); i++) {
-                if (a.getIntents().get(i).match(intent.getAction(), resolvedType, intent.getScheme(),
-                        intent.getData(), intent.getCategories(), TAG) >= 0) {
+                if (a.getIntents().get(i).getIntentFilter()
+                        .match(intent.getAction(), resolvedType, intent.getScheme(),
+                                intent.getData(), intent.getCategories(), TAG) >= 0) {
                     return true;
                 }
             }
@@ -4037,32 +3812,12 @@
         }
     }
 
-    /**
-     * If the database version for this type of package (internal storage or
-     * external storage) is less than the version where package signatures
-     * were updated, return true.
-     */
-    boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
-        return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
-    }
-
-    static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
-        return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
-    }
-
-    boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
-        return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
-    }
-
-    static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
-        return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
-    }
-
     @Override
     public List<String> getAllPackages() {
         // Allow iorapd to call this method.
         if (Binder.getCallingUid() != Process.IORAPD_UID) {
-            enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers");
+            PackageManagerServiceUtils.enforceSystemOrRootOrShell(
+                    "getAllPackages is limited to privileged callers");
         }
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
@@ -4322,124 +4077,25 @@
     @Override
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             int flags, int userId) {
-        return resolveIntentInternal(intent, resolvedType, flags, 0 /*privateResolveFlags*/,
-                userId, false, Binder.getCallingUid());
-    }
-
-    /**
-     * Normally instant apps can only be resolved when they're visible to the caller.
-     * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
-     * since we need to allow the system to start any installed application.
-     */
-    private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags,
-            @PrivateResolveFlags int privateResolveFlags, int userId, boolean resolveForStart,
-            int filterCallingUid) {
-        try {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
-
-            if (!mUserManager.exists(userId)) return null;
-            final int callingUid = Binder.getCallingUid();
-            flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
-                    isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
-                            flags));
-            enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
-                    false /*checkShell*/, "resolve intent");
-
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
-            final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
-                    flags, privateResolveFlags, filterCallingUid, userId, resolveForStart,
-                    true /*allowDynamicSplits*/);
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
-            final boolean queryMayBeFiltered =
-                    UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
-                            && !resolveForStart;
-
-            final ResolveInfo bestChoice =
-                    chooseBestActivity(
-                            intent, resolvedType, flags, privateResolveFlags, query, userId,
-                            queryMayBeFiltered);
-            final boolean nonBrowserOnly =
-                    (privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
-            if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
-                return null;
-            }
-            return bestChoice;
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
+        return mResolveIntentHelper.resolveIntentInternal(intent, resolvedType, flags,
+                0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
     }
 
     @Override
     public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) {
-        if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
-            throw new SecurityException(
-                    "findPersistentPreferredActivity can only be run by the system");
-        }
-        if (!mUserManager.exists(userId)) {
-            return null;
-        }
-        final int callingUid = Binder.getCallingUid();
-        intent = PackageManagerServiceUtils.updateIntentForResolve(intent);
-        final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
-        final int flags = updateFlagsForResolve(
-                0, userId, callingUid, false /*includeInstantApps*/,
-                isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, 0));
-        final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
-                userId);
-        synchronized (mLock) {
-            return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
-                    userId);
-        }
+        return mPreferredActivityHelper.findPersistentPreferredActivity(intent, userId);
     }
 
     @Override
     public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
             IntentFilter filter, int match, ComponentName activity) {
-        setLastChosenActivity(intent, resolvedType, flags,
+        mPreferredActivityHelper.setLastChosenActivity(intent, resolvedType, flags,
                               new WatchedIntentFilter(filter), match, activity);
     }
 
-    /**
-     * Variant that takes a {@link WatchedIntentFilter}
-     */
-    public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
-            WatchedIntentFilter filter, int match, ComponentName activity) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return;
-        }
-        final int userId = UserHandle.getCallingUserId();
-        if (DEBUG_PREFERRED) {
-            Log.v(TAG, "setLastChosenActivity intent=" + intent
-                + " resolvedType=" + resolvedType
-                + " flags=" + flags
-                + " filter=" + filter
-                + " match=" + match
-                + " activity=" + activity);
-            filter.dump(new PrintStreamPrinter(System.out), "    ");
-        }
-        intent.setComponent(null);
-        final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
-                userId);
-        // Find any earlier preferred or last chosen entries and nuke them
-        findPreferredActivityNotLocked(
-                intent, resolvedType, flags, query, false, true, false, userId);
-        // Add the new activity as the last chosen for this filter
-        addPreferredActivity(filter, match, null, activity, false, userId,
-                "Setting last chosen", false);
-    }
-
     @Override
     public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return null;
-        }
-        final int userId = UserHandle.getCallingUserId();
-        if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
-        final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
-                userId);
-        return findPreferredActivityNotLocked(
-                intent, resolvedType, flags, query, false, false, false, userId);
+        return mPreferredActivityHelper.getLastChosenActivity(intent, resolvedType, flags);
     }
 
     private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
@@ -4454,115 +4110,6 @@
         mHandler.sendMessage(msg);
     }
 
-    private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
-            int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
-            boolean queryMayBeFiltered) {
-        if (query != null) {
-            final int N = query.size();
-            if (N == 1) {
-                return query.get(0);
-            } else if (N > 1) {
-                final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
-                // If there is more than one activity with the same priority,
-                // then let the user decide between them.
-                ResolveInfo r0 = query.get(0);
-                ResolveInfo r1 = query.get(1);
-                if (DEBUG_INTENT_MATCHING || debug) {
-                    Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs "
-                            + r1.activityInfo.name + "=" + r1.priority);
-                }
-                // If the first activity has a higher priority, or a different
-                // default, then it is always desirable to pick it.
-                if (r0.priority != r1.priority
-                        || r0.preferredOrder != r1.preferredOrder
-                        || r0.isDefault != r1.isDefault) {
-                    return query.get(0);
-                }
-                // If we have saved a preference for a preferred activity for
-                // this Intent, use that.
-                ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType,
-                        flags, query, true, false, debug, userId, queryMayBeFiltered);
-                if (ri != null) {
-                    return ri;
-                }
-                int browserCount = 0;
-                for (int i = 0; i < N; i++) {
-                    ri = query.get(i);
-                    if (ri.handleAllWebDataURI) {
-                        browserCount++;
-                    }
-                    // If we have an ephemeral app, use it
-                    if (ri.activityInfo.applicationInfo.isInstantApp()) {
-                        final String packageName = ri.activityInfo.packageName;
-                        final PackageSetting ps = mSettings.getPackageLPr(packageName);
-                        if (ps != null && PackageManagerServiceUtils.hasAnyDomainApproval(
-                                mDomainVerificationManager, ps, intent, flags, userId)) {
-                            return ri;
-                        }
-                    }
-                }
-                if ((privateResolveFlags
-                        & PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
-                    return null;
-                }
-                ri = new ResolveInfo(mResolveInfo);
-                // if all resolve options are browsers, mark the resolver's info as if it were
-                // also a browser.
-                ri.handleAllWebDataURI = browserCount == N;
-                ri.activityInfo = new ActivityInfo(ri.activityInfo);
-                ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
-                // If all of the options come from the same package, show the application's
-                // label and icon instead of the generic resolver's.
-                // Some calls like Intent.resolveActivityInfo query the ResolveInfo from here
-                // and then throw away the ResolveInfo itself, meaning that the caller loses
-                // the resolvePackageName. Therefore the activityInfo.labelRes above provides
-                // a fallback for this case; we only set the target package's resources on
-                // the ResolveInfo, not the ActivityInfo.
-                final String intentPackage = intent.getPackage();
-                if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
-                    final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
-                    ri.resolvePackageName = intentPackage;
-                    if (userNeedsBadging(userId)) {
-                        ri.noResourceId = true;
-                    } else {
-                        ri.icon = appi.icon;
-                    }
-                    ri.iconResourceId = appi.icon;
-                    ri.labelRes = appi.labelRes;
-                }
-                ri.activityInfo.applicationInfo = new ApplicationInfo(
-                        ri.activityInfo.applicationInfo);
-                if (userId != 0) {
-                    ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
-                            UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
-                }
-                // Make sure that the resolver is displayable in car mode
-                if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();
-                ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);
-                return ri;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Return true if the given list is not empty and all of its contents have
-     * an activityInfo with the given package name.
-     */
-    private boolean allHavePackage(List<ResolveInfo> list, String packageName) {
-        if (ArrayUtils.isEmpty(list)) {
-            return false;
-        }
-        for (int i = 0, N = list.size(); i < N; i++) {
-            final ResolveInfo ri = list.get(i);
-            final ActivityInfo ai = ri != null ? ri.activityInfo : null;
-            if (ai == null || !packageName.equals(ai.packageName)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     /**
      * From Android R, camera intents have to match system apps. The only exception to this is if
      * the DPC has set the camera persistent preferred activity. This case was introduced
@@ -4573,14 +4120,14 @@
      * activity was not set by the DPC.
      */
     @GuardedBy("mLock")
-    private boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
+    boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
             String resolvedType, int flags) {
         return mComputer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
                 resolvedType, flags);
     }
 
     @GuardedBy("mLock")
-    private ResolveInfo findPersistentPreferredActivityLP(Intent intent,
+    ResolveInfo findPersistentPreferredActivityLP(Intent intent,
             String resolvedType,
             int flags, List<ResolveInfo> query, boolean debug, int userId) {
         return mComputer.findPersistentPreferredActivityLP(intent,
@@ -4595,7 +4142,7 @@
         ResolveInfo mPreferredResolveInfo;
     }
 
-    private FindPreferredActivityBodyResult findPreferredActivityInternal(
+    FindPreferredActivityBodyResult findPreferredActivityInternal(
             Intent intent, String resolvedType, int flags,
             List<ResolveInfo> query, boolean always,
             boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
@@ -4605,42 +4152,6 @@
             removeMatches, debug, userId, queryMayBeFiltered);
     }
 
-    private ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType,
-            int flags, List<ResolveInfo> query, boolean always, boolean removeMatches,
-            boolean debug, int userId) {
-        return findPreferredActivityNotLocked(
-                intent, resolvedType, flags, query, always, removeMatches, debug, userId,
-                UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
-    }
-
-    // TODO: handle preferred activities missing while user has amnesia
-    /** <b>must not hold {@link #mLock}</b> */
-    private ResolveInfo findPreferredActivityNotLocked(
-            Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean always,
-            boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
-        if (Thread.holdsLock(mLock)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
-                    + " is holding mLock", new Throwable());
-        }
-        if (!mUserManager.exists(userId)) return null;
-
-        FindPreferredActivityBodyResult body = findPreferredActivityInternal(
-                intent, resolvedType, flags, query, always,
-                removeMatches, debug, userId, queryMayBeFiltered);
-        if (body.mChanged) {
-            if (DEBUG_PREFERRED) {
-                Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
-            }
-            synchronized (mLock) {
-                scheduleWritePackageRestrictionsLocked(userId);
-            }
-        }
-        if ((DEBUG_PREFERRED || debug) && body.mPreferredResolveInfo == null) {
-            Slog.v(TAG, "No preferred activity to return");
-        }
-        return body.mPreferredResolveInfo;
-    }
-
     /*
      * Returns if intent can be forwarded from the sourceUserId to the targetUserId
      */
@@ -4705,17 +4216,17 @@
      * Returns the package name of the calling Uid if it's an instant app. If it isn't
      * instant, returns {@code null}.
      */
-    private String getInstantAppPackageName(int callingUid) {
+    String getInstantAppPackageName(int callingUid) {
         return mComputer.getInstantAppPackageName(callingUid);
     }
 
-    private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+    @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
             String resolvedType, int flags, int userId) {
         return mComputer.queryIntentActivitiesInternal(intent,
                 resolvedType, flags, userId);
     }
 
-    private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+    @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
             String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
             int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
         return mComputer.queryIntentActivitiesInternal(intent,
@@ -4740,7 +4251,7 @@
      * @param intent
      * @return A filtered list of resolved activities.
      */
-    private List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
+    List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
             String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
             boolean resolveForStart, int userId, Intent intent) {
         return mComputer.applyPostResolutionFilter(resolveInfos,
@@ -4752,311 +4263,22 @@
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
             Intent[] specifics, String[] specificTypes, Intent intent,
             String resolvedType, int flags, int userId) {
-        return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics,
-                specificTypes, intent, resolvedType, flags, userId));
-    }
-
-    private @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
-            Intent[] specifics, String[] specificTypes, Intent intent,
-            String resolvedType, int flags, int userId) {
-        if (!mUserManager.exists(userId)) return Collections.emptyList();
-        final int callingUid = Binder.getCallingUid();
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
-                isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
-                        flags));
-        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
-                false /*checkShell*/, "query intent activity options");
-        final String resultsAction = intent.getAction();
-
-        final List<ResolveInfo> results = queryIntentActivitiesInternal(intent, resolvedType, flags
-                | PackageManager.GET_RESOLVED_FILTER, userId);
-
-        if (DEBUG_INTENT_MATCHING) {
-            Log.v(TAG, "Query " + intent + ": " + results);
-        }
-
-        int specificsPos = 0;
-        int N;
-
-        // todo: note that the algorithm used here is O(N^2).  This
-        // isn't a problem in our current environment, but if we start running
-        // into situations where we have more than 5 or 10 matches then this
-        // should probably be changed to something smarter...
-
-        // First we go through and resolve each of the specific items
-        // that were supplied, taking care of removing any corresponding
-        // duplicate items in the generic resolve list.
-        if (specifics != null) {
-            for (int i=0; i<specifics.length; i++) {
-                final Intent sintent = specifics[i];
-                if (sintent == null) {
-                    continue;
-                }
-
-                if (DEBUG_INTENT_MATCHING) {
-                    Log.v(TAG, "Specific #" + i + ": " + sintent);
-                }
-
-                String action = sintent.getAction();
-                if (resultsAction != null && resultsAction.equals(action)) {
-                    // If this action was explicitly requested, then don't
-                    // remove things that have it.
-                    action = null;
-                }
-
-                ResolveInfo ri = null;
-                ActivityInfo ai = null;
-
-                ComponentName comp = sintent.getComponent();
-                if (comp == null) {
-                    ri = resolveIntent(
-                        sintent,
-                        specificTypes != null ? specificTypes[i] : null,
-                            flags, userId);
-                    if (ri == null) {
-                        continue;
-                    }
-                    if (ri == mResolveInfo) {
-                        // ACK!  Must do something better with this.
-                    }
-                    ai = ri.activityInfo;
-                    comp = new ComponentName(ai.applicationInfo.packageName,
-                            ai.name);
-                } else {
-                    ai = getActivityInfo(comp, flags, userId);
-                    if (ai == null) {
-                        continue;
-                    }
-                }
-
-                // Look for any generic query activities that are duplicates
-                // of this specific one, and remove them from the results.
-                if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Specific #" + i + ": " + ai);
-                N = results.size();
-                int j;
-                for (j=specificsPos; j<N; j++) {
-                    ResolveInfo sri = results.get(j);
-                    if ((sri.activityInfo.name.equals(comp.getClassName())
-                            && sri.activityInfo.applicationInfo.packageName.equals(
-                                    comp.getPackageName()))
-                        || (action != null && sri.filter.matchAction(action))) {
-                        results.remove(j);
-                        if (DEBUG_INTENT_MATCHING) Log.v(
-                            TAG, "Removing duplicate item from " + j
-                            + " due to specific " + specificsPos);
-                        if (ri == null) {
-                            ri = sri;
-                        }
-                        j--;
-                        N--;
-                    }
-                }
-
-                // Add this specific item to its proper place.
-                if (ri == null) {
-                    ri = new ResolveInfo();
-                    ri.activityInfo = ai;
-                }
-                results.add(specificsPos, ri);
-                ri.specificIndex = i;
-                specificsPos++;
-            }
-        }
-
-        // Now we go through the remaining generic results and remove any
-        // duplicate actions that are found here.
-        N = results.size();
-        for (int i=specificsPos; i<N-1; i++) {
-            final ResolveInfo rii = results.get(i);
-            if (rii.filter == null) {
-                continue;
-            }
-
-            // Iterate over all of the actions of this result's intent
-            // filter...  typically this should be just one.
-            final Iterator<String> it = rii.filter.actionsIterator();
-            if (it == null) {
-                continue;
-            }
-            while (it.hasNext()) {
-                final String action = it.next();
-                if (resultsAction != null && resultsAction.equals(action)) {
-                    // If this action was explicitly requested, then don't
-                    // remove things that have it.
-                    continue;
-                }
-                for (int j=i+1; j<N; j++) {
-                    final ResolveInfo rij = results.get(j);
-                    if (rij.filter != null && rij.filter.hasAction(action)) {
-                        results.remove(j);
-                        if (DEBUG_INTENT_MATCHING) Log.v(
-                            TAG, "Removing duplicate item from " + j
-                            + " due to action " + action + " at " + i);
-                        j--;
-                        N--;
-                    }
-                }
-            }
-
-            // If the caller didn't request filter information, drop it now
-            // so we don't have to marshall/unmarshall it.
-            if ((flags&PackageManager.GET_RESOLVED_FILTER) == 0) {
-                rii.filter = null;
-            }
-        }
-
-        // Filter out the caller activity if so requested.
-        if (caller != null) {
-            N = results.size();
-            for (int i=0; i<N; i++) {
-                ActivityInfo ainfo = results.get(i).activityInfo;
-                if (caller.getPackageName().equals(ainfo.applicationInfo.packageName)
-                        && caller.getClassName().equals(ainfo.name)) {
-                    results.remove(i);
-                    break;
-                }
-            }
-        }
-
-        // If the caller didn't request filter information,
-        // drop them now so we don't have to
-        // marshall/unmarshall it.
-        if ((flags&PackageManager.GET_RESOLVED_FILTER) == 0) {
-            N = results.size();
-            for (int i=0; i<N; i++) {
-                results.get(i).filter = null;
-            }
-        }
-
-        if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Result: " + results);
-        return results;
+        return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
+                caller, specifics, specificTypes, intent, resolvedType, flags, userId));
     }
 
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
             String resolvedType, int flags, int userId) {
-        return new ParceledListSlice<>(queryIntentReceiversInternal(intent, resolvedType,
-                flags, userId, Binder.getCallingUid()));
-    }
-
-    // In this method, we have to know the actual calling UID, but in some cases Binder's
-    // call identity is removed, so the UID has to be passed in explicitly.
-    private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
-            String resolvedType, int flags, int userId, int filterCallingUid) {
-        if (!mUserManager.exists(userId)) return Collections.emptyList();
-        enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
-                false /*checkShell*/, "query intent receivers");
-        final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
-        flags = updateFlagsForResolve(flags, userId, filterCallingUid, false /*includeInstantApps*/,
-                isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
-                        flags));
-        Intent originalIntent = null;
-        ComponentName comp = intent.getComponent();
-        if (comp == null) {
-            if (intent.getSelector() != null) {
-                originalIntent = intent;
-                intent = intent.getSelector();
-                comp = intent.getComponent();
-            }
-        }
-        List<ResolveInfo> list = Collections.emptyList();
-        if (comp != null) {
-            final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
-            if (ai != null) {
-                // When specifying an explicit component, we prevent the activity from being
-                // used when either 1) the calling package is normal and the activity is within
-                // an instant application or 2) the calling package is ephemeral and the
-                // activity is not visible to instant applications.
-                final boolean matchInstantApp =
-                        (flags & PackageManager.MATCH_INSTANT) != 0;
-                final boolean matchVisibleToInstantAppOnly =
-                        (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
-                final boolean matchExplicitlyVisibleOnly =
-                        (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
-                final boolean isCallerInstantApp =
-                        instantAppPkgName != null;
-                final boolean isTargetSameInstantApp =
-                        comp.getPackageName().equals(instantAppPkgName);
-                final boolean isTargetInstantApp =
-                        (ai.applicationInfo.privateFlags
-                                & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
-                final boolean isTargetVisibleToInstantApp =
-                        (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
-                final boolean isTargetExplicitlyVisibleToInstantApp =
-                        isTargetVisibleToInstantApp
-                        && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
-                final boolean isTargetHiddenFromInstantApp =
-                        !isTargetVisibleToInstantApp
-                        || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
-                final boolean blockResolution =
-                        !isTargetSameInstantApp
-                        && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
-                                || (matchVisibleToInstantAppOnly && isCallerInstantApp
-                                        && isTargetHiddenFromInstantApp));
-                if (!blockResolution) {
-                    ResolveInfo ri = new ResolveInfo();
-                    ri.activityInfo = ai;
-                    list = new ArrayList<>(1);
-                    list.add(ri);
-                    PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
-                            mInjector.getCompatibility(), mComponentResolver,
-                            list, true, intent, resolvedType, filterCallingUid);
-                }
-            }
-        } else {
-            // reader
-            synchronized (mLock) {
-                String pkgName = intent.getPackage();
-                if (pkgName == null) {
-                    final List<ResolveInfo> result =
-                            mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
-                    if (result != null) {
-                        list = result;
-                    }
-                }
-                final AndroidPackage pkg = mPackages.get(pkgName);
-                if (pkg != null) {
-                    final List<ResolveInfo> result = mComponentResolver.queryReceivers(
-                            intent, resolvedType, flags, pkg.getReceivers(), userId);
-                    if (result != null) {
-                        list = result;
-                    }
-                }
-            }
-        }
-
-        if (originalIntent != null) {
-            // We also have to ensure all components match the original intent
-            PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
-                    mInjector.getCompatibility(), mComponentResolver,
-                    list, true, originalIntent, resolvedType, filterCallingUid);
-        }
-
-        return applyPostResolutionFilter(
-                list, instantAppPkgName, false, filterCallingUid, false, userId, intent);
+        return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(intent,
+                resolvedType, flags, userId, Binder.getCallingUid()));
     }
 
     @Override
     public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
         final int callingUid = Binder.getCallingUid();
-        return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid);
-    }
-
-    private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
-            int userId, int callingUid) {
-        if (!mUserManager.exists(userId)) return null;
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
-                false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
-        List<ResolveInfo> query = queryIntentServicesInternal(
-                intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
-        if (query != null) {
-            if (query.size() >= 1) {
-                // If there is more than one service with the same priority,
-                // just arbitrarily pick the first one.
-                return query.get(0);
-            }
-        }
-        return null;
+        return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
+                callingUid);
     }
 
     @Override
@@ -5067,7 +4289,7 @@
                 intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/));
     }
 
-    private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
+    @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
             String resolvedType, int flags, int userId, int callingUid,
             boolean includeInstantApps) {
         return mComputer.queryIntentServicesInternal(intent,
@@ -5078,148 +4300,8 @@
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
             String resolvedType, int flags, int userId) {
-        return new ParceledListSlice<>(
-                queryIntentContentProvidersInternal(intent, resolvedType, flags, userId));
-    }
-
-    private @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
-            Intent intent, String resolvedType, int flags, int userId) {
-        if (!mUserManager.exists(userId)) return Collections.emptyList();
-        final int callingUid = Binder.getCallingUid();
-        final String instantAppPkgName = getInstantAppPackageName(callingUid);
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
-                false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
-        ComponentName comp = intent.getComponent();
-        if (comp == null) {
-            if (intent.getSelector() != null) {
-                intent = intent.getSelector();
-                comp = intent.getComponent();
-            }
-        }
-        if (comp != null) {
-            final List<ResolveInfo> list = new ArrayList<>(1);
-            final ProviderInfo pi = getProviderInfo(comp, flags, userId);
-            if (pi != null) {
-                // When specifying an explicit component, we prevent the provider from being
-                // used when either 1) the provider is in an instant application and the
-                // caller is not the same instant application or 2) the calling package is an
-                // instant application and the provider is not visible to instant applications.
-                final boolean matchInstantApp =
-                        (flags & PackageManager.MATCH_INSTANT) != 0;
-                final boolean matchVisibleToInstantAppOnly =
-                        (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
-                final boolean isCallerInstantApp =
-                        instantAppPkgName != null;
-                final boolean isTargetSameInstantApp =
-                        comp.getPackageName().equals(instantAppPkgName);
-                final boolean isTargetInstantApp =
-                        (pi.applicationInfo.privateFlags
-                                & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
-                final boolean isTargetHiddenFromInstantApp =
-                        (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
-                final boolean blockResolution =
-                        !isTargetSameInstantApp
-                        && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
-                                || (matchVisibleToInstantAppOnly && isCallerInstantApp
-                                        && isTargetHiddenFromInstantApp));
-                final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
-                        && shouldFilterApplicationLocked(
-                        getPackageSettingInternal(pi.applicationInfo.packageName,
-                                Process.SYSTEM_UID), callingUid, userId);
-                if (!blockResolution && !blockNormalResolution) {
-                    final ResolveInfo ri = new ResolveInfo();
-                    ri.providerInfo = pi;
-                    list.add(ri);
-                }
-            }
-            return list;
-        }
-
-        // reader
-        synchronized (mLock) {
-            String pkgName = intent.getPackage();
-            if (pkgName == null) {
-                final List<ResolveInfo> resolveInfos = mComponentResolver.queryProviders(intent,
-                        resolvedType, flags, userId);
-                if (resolveInfos == null) {
-                    return Collections.emptyList();
-                }
-                return applyPostContentProviderResolutionFilter(
-                        resolveInfos, instantAppPkgName, userId, callingUid);
-            }
-            final AndroidPackage pkg = mPackages.get(pkgName);
-            if (pkg != null) {
-                final List<ResolveInfo> resolveInfos = mComponentResolver.queryProviders(intent,
-                        resolvedType, flags,
-                        pkg.getProviders(), userId);
-                if (resolveInfos == null) {
-                    return Collections.emptyList();
-                }
-                return applyPostContentProviderResolutionFilter(
-                        resolveInfos, instantAppPkgName, userId, callingUid);
-            }
-            return Collections.emptyList();
-        }
-    }
-
-    private List<ResolveInfo> applyPostContentProviderResolutionFilter(
-            List<ResolveInfo> resolveInfos, String instantAppPkgName,
-            @UserIdInt int userId, int callingUid) {
-        for (int i = resolveInfos.size() - 1; i >= 0; i--) {
-            final ResolveInfo info = resolveInfos.get(i);
-
-            if (instantAppPkgName == null) {
-                SettingBase callingSetting =
-                        mSettings.getSettingLPr(UserHandle.getAppId(callingUid));
-                PackageSetting resolvedSetting =
-                        getPackageSettingInternal(info.providerInfo.packageName, 0);
-                if (!mAppsFilter.shouldFilterApplication(
-                        callingUid, callingSetting, resolvedSetting, userId)) {
-                    continue;
-                }
-            }
-
-            final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp();
-            // allow providers that are defined in the provided package
-            if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) {
-                if (info.providerInfo.splitName != null
-                        && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames,
-                                info.providerInfo.splitName)) {
-                    if (mInstantAppInstallerActivity == null) {
-                        if (DEBUG_INSTANT) {
-                            Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
-                        }
-                        resolveInfos.remove(i);
-                        continue;
-                    }
-                    // requested provider is defined in a split that hasn't been installed yet.
-                    // add the installer to the resolve list
-                    if (DEBUG_INSTANT) {
-                        Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
-                    }
-                    final ResolveInfo installerInfo = new ResolveInfo(
-                            mInstantAppInstallerInfo);
-                    installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
-                            null /*failureActivity*/,
-                            info.providerInfo.packageName,
-                            info.providerInfo.applicationInfo.longVersionCode,
-                            info.providerInfo.splitName);
-                    // add a non-generic filter
-                    installerInfo.filter = new IntentFilter();
-                    // load resources from the correct package
-                    installerInfo.resolvePackageName = info.getComponentInfo().packageName;
-                    resolveInfos.set(i, installerInfo);
-                }
-                continue;
-            }
-            // allow providers that have been explicitly exposed to instant applications
-            if (!isEphemeralApp
-                    && ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
-                continue;
-            }
-            resolveInfos.remove(i);
-        }
-        return resolveInfos;
+        return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
+                intent, resolvedType, flags, userId));
     }
 
     @Override
@@ -5463,7 +4545,7 @@
         }
     }
 
-    private boolean isCallerSameApp(String packageName, int uid) {
+    boolean isCallerSameApp(String packageName, int uid) {
         return mComputer.isCallerSameApp(packageName, uid);
     }
 
@@ -5702,34 +4784,6 @@
     }
 
     /**
-     * Enforces that only the system UID or root's UID can call a method exposed
-     * via Binder.
-     *
-     * @param message used as message if SecurityException is thrown
-     * @throws SecurityException if the caller is not system or root
-     */
-    private static void enforceSystemOrRoot(String message) {
-        final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
-            throw new SecurityException(message);
-        }
-    }
-
-    /**
-     * Enforces that only the system UID or root's UID or shell's UID can call
-     * a method exposed via Binder.
-     *
-     * @param message used as message if SecurityException is thrown
-     * @throws SecurityException if the caller is not system or shell
-     */
-    private static void enforceSystemOrRootOrShell(String message) {
-        final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_UID) {
-            throw new SecurityException(message);
-        }
-    }
-
-    /**
      * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
      * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
      *
@@ -5764,7 +4818,7 @@
 
     @Override
     public void performFstrimIfNeeded() {
-        enforceSystemOrRoot("Only the system can request fstrim");
+        PackageManagerServiceUtils.enforceSystemOrRoot("Only the system can request fstrim");
 
         // Before everything else, see whether we need to fstrim.
         try {
@@ -5786,7 +4840,7 @@
                 if (doTrim) {
                     final boolean dexOptDialogShown;
                     synchronized (mLock) {
-                        dexOptDialogShown = mDexOptDialogShown;
+                        dexOptDialogShown = mDexOptHelper.isDexOptDialogShown();
                     }
                     if (!isFirstBoot() && dexOptDialogShown) {
                         try {
@@ -5808,208 +4862,7 @@
 
     @Override
     public void updatePackagesIfNeeded() {
-        enforceSystemOrRoot("Only the system can request package update");
-
-        // We need to re-extract after an OTA.
-        boolean causeUpgrade = isDeviceUpgrading();
-
-        // First boot or factory reset.
-        // Note: we also handle devices that are upgrading to N right now as if it is their
-        //       first boot, as they do not have profile data.
-        boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
-
-        if (!causeUpgrade && !causeFirstBoot) {
-            return;
-        }
-
-        List<PackageSetting> pkgSettings;
-        synchronized (mLock) {
-            pkgSettings = PackageManagerServiceUtils.getPackagesForDexopt(
-                    mSettings.getPackagesLocked().values(), this);
-        }
-
-        List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
-        for (int index = 0; index < pkgSettings.size(); index++) {
-            pkgs.add(pkgSettings.get(index).getPkg());
-        }
-
-        final long startTime = System.nanoTime();
-        final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
-                    causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA,
-                    false /* bootComplete */);
-
-        final int elapsedTimeSeconds =
-                (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
-
-        MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]);
-        MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]);
-        MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]);
-        MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size());
-        MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds);
-    }
-
-    /*
-     * Return the prebuilt profile path given a package base code path.
-     */
-    private static String getPrebuildProfilePath(AndroidPackage pkg) {
-        return pkg.getBaseApkPath() + ".prof";
-    }
-
-    /**
-     * Performs dexopt on the set of packages in {@code packages} and returns an int array
-     * containing statistics about the invocation. The array consists of three elements,
-     * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
-     * and {@code numberOfPackagesFailed}.
-     */
-    private int[] performDexOptUpgrade(List<AndroidPackage> pkgs, boolean showDialog,
-            final int compilationReason, boolean bootComplete) {
-
-        int numberOfPackagesVisited = 0;
-        int numberOfPackagesOptimized = 0;
-        int numberOfPackagesSkipped = 0;
-        int numberOfPackagesFailed = 0;
-        final int numberOfPackagesToDexopt = pkgs.size();
-
-        for (AndroidPackage pkg : pkgs) {
-            numberOfPackagesVisited++;
-
-            boolean useProfileForDexopt = false;
-
-            if ((isFirstBoot() || isDeviceUpgrading()) && pkg.isSystem()) {
-                // Copy over initial preopt profiles since we won't get any JIT samples for methods
-                // that are already compiled.
-                File profileFile = new File(getPrebuildProfilePath(pkg));
-                // Copy profile if it exists.
-                if (profileFile.exists()) {
-                    try {
-                        // We could also do this lazily before calling dexopt in
-                        // PackageDexOptimizer to prevent this happening on first boot. The issue
-                        // is that we don't have a good way to say "do this only once".
-                        if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
-                                pkg.getUid(), pkg.getPackageName(),
-                                ArtManager.getProfileName(null))) {
-                            Log.e(TAG, "Installer failed to copy system profile!");
-                        } else {
-                            // Disabled as this causes speed-profile compilation during first boot
-                            // even if things are already compiled.
-                            // useProfileForDexopt = true;
-                        }
-                    } catch (Exception e) {
-                        Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
-                                e);
-                    }
-                } else {
-                    PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(
-                            pkg.getPackageName());
-                    // Handle compressed APKs in this path. Only do this for stubs with profiles to
-                    // minimize the number off apps being speed-profile compiled during first boot.
-                    // The other paths will not change the filter.
-                    if (disabledPs != null && disabledPs.getPkg().isStub()) {
-                        // The package is the stub one, remove the stub suffix to get the normal
-                        // package and APK names.
-                        String systemProfilePath = getPrebuildProfilePath(disabledPs.getPkg())
-                                .replace(STUB_SUFFIX, "");
-                        profileFile = new File(systemProfilePath);
-                        // If we have a profile for a compressed APK, copy it to the reference
-                        // location.
-                        // Note that copying the profile here will cause it to override the
-                        // reference profile every OTA even though the existing reference profile
-                        // may have more data. We can't copy during decompression since the
-                        // directories are not set up at that point.
-                        if (profileFile.exists()) {
-                            try {
-                                // We could also do this lazily before calling dexopt in
-                                // PackageDexOptimizer to prevent this happening on first boot. The
-                                // issue is that we don't have a good way to say "do this only
-                                // once".
-                                if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
-                                        pkg.getUid(), pkg.getPackageName(),
-                                        ArtManager.getProfileName(null))) {
-                                    Log.e(TAG, "Failed to copy system profile for stub package!");
-                                } else {
-                                    useProfileForDexopt = true;
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "Failed to copy profile " +
-                                        profileFile.getAbsolutePath() + " ", e);
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
-                if (DEBUG_DEXOPT) {
-                    Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName());
-                }
-                numberOfPackagesSkipped++;
-                continue;
-            }
-
-            if (DEBUG_DEXOPT) {
-                Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " +
-                        numberOfPackagesToDexopt + ": " + pkg.getPackageName());
-            }
-
-            if (showDialog) {
-                try {
-                    ActivityManager.getService().showBootMessage(
-                            mContext.getResources().getString(R.string.android_upgrading_apk,
-                                    numberOfPackagesVisited, numberOfPackagesToDexopt), true);
-                } catch (RemoteException e) {
-                }
-                synchronized (mLock) {
-                    mDexOptDialogShown = true;
-                }
-            }
-
-            int pkgCompilationReason = compilationReason;
-            if (useProfileForDexopt) {
-                // Use background dexopt mode to try and use the profile. Note that this does not
-                // guarantee usage of the profile.
-                pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
-            }
-
-            if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
-                mArtManagerService.compileLayouts(pkg);
-            }
-
-            // checkProfiles is false to avoid merging profiles during boot which
-            // might interfere with background compilation (b/28612421).
-            // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
-            // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
-            // trade-off worth doing to save boot time work.
-            int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
-            if (compilationReason == REASON_FIRST_BOOT) {
-                // TODO: This doesn't cover the upgrade case, we should check for this too.
-                dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
-            }
-            int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
-                    pkg.getPackageName(),
-                    pkgCompilationReason,
-                    dexoptFlags));
-
-            switch (primaryDexOptStaus) {
-                case PackageDexOptimizer.DEX_OPT_PERFORMED:
-                    numberOfPackagesOptimized++;
-                    break;
-                case PackageDexOptimizer.DEX_OPT_SKIPPED:
-                    numberOfPackagesSkipped++;
-                    break;
-                case PackageDexOptimizer.DEX_OPT_CANCELLED:
-                    // ignore this case
-                    break;
-                case PackageDexOptimizer.DEX_OPT_FAILED:
-                    numberOfPackagesFailed++;
-                    break;
-                default:
-                    Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus);
-                    break;
-            }
-        }
-
-        return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped,
-                numberOfPackagesFailed };
+        mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
     }
 
     @Override
@@ -6103,13 +4956,8 @@
     public boolean performDexOptMode(String packageName,
             boolean checkProfiles, String targetCompilerFilter, boolean force,
             boolean bootComplete, String splitName) {
-        enforceSystemOrRootOrShell("performDexOptMode");
-
-        int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) |
-                (force ? DexoptOptions.DEXOPT_FORCE : 0) |
-                (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
-        return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE,
-                targetCompilerFilter, splitName, flags));
+        return mDexOptHelper.performDexOptMode(packageName, checkProfiles, targetCompilerFilter,
+                force, bootComplete, splitName);
     }
 
     /**
@@ -6122,147 +4970,7 @@
     @Override
     public boolean performDexOptSecondary(String packageName, String compilerFilter,
             boolean force) {
-        int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
-                DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
-                DexoptOptions.DEXOPT_BOOT_COMPLETE |
-                (force ? DexoptOptions.DEXOPT_FORCE : 0);
-        return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
-    }
-
-    /*package*/ boolean performDexOpt(DexoptOptions options) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return false;
-        } else if (isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
-            return false;
-        }
-
-        if (options.isDexoptOnlySecondaryDex()) {
-            return mDexManager.dexoptSecondaryDex(options);
-        } else {
-            int dexoptStatus = performDexOptWithStatus(options);
-            return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
-        }
-    }
-
-    /*package*/ void controlDexOptBlocking(boolean block) {
-        mPackageDexOptimizer.controlDexOptBlocking(block);
-    }
-
-    /**
-     * Perform dexopt on the given package and return one of following result:
-     *  {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
-     *  {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
-     *  {@link PackageDexOptimizer#DEX_OPT_CANCELLED}
-     *  {@link PackageDexOptimizer#DEX_OPT_FAILED}
-     */
-    @PackageDexOptimizer.DexOptResult
-    /* package */ int performDexOptWithStatus(DexoptOptions options) {
-        return performDexOptTraced(options);
-    }
-
-    private int performDexOptTraced(DexoptOptions options) {
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-        try {
-            return performDexOptInternal(options);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-    }
-
-    // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
-    // if the package can now be considered up to date for the given filter.
-    private int performDexOptInternal(DexoptOptions options) {
-        AndroidPackage p;
-        PackageSetting pkgSetting;
-        synchronized (mLock) {
-            p = mPackages.get(options.getPackageName());
-            pkgSetting = mSettings.getPackageLPr(options.getPackageName());
-            if (p == null || pkgSetting == null) {
-                // Package could not be found. Report failure.
-                return PackageDexOptimizer.DEX_OPT_FAILED;
-            }
-            mPackageUsage.maybeWriteAsync(mSettings.getPackagesLocked());
-            mCompilerStats.maybeWriteAsync();
-        }
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mInstallLock) {
-                return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    public ArraySet<String> getOptimizablePackages() {
-        ArraySet<String> pkgs = new ArraySet<>();
-        synchronized (mLock) {
-            for (AndroidPackage p : mPackages.values()) {
-                if (PackageDexOptimizer.canOptimizePackage(p)) {
-                    pkgs.add(p.getPackageName());
-                }
-            }
-        }
-        if (AppHibernationService.isAppHibernationEnabled()) {
-            AppHibernationManagerInternal appHibernationManager =
-                    mInjector.getLocalService(AppHibernationManagerInternal.class);
-            pkgs.removeIf(pkgName -> appHibernationManager.isHibernatingGlobally(pkgName));
-        }
-        return pkgs;
-    }
-
-    private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
-            @NonNull PackageSetting pkgSetting, DexoptOptions options) {
-        // System server gets a special path.
-        if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
-            return mDexManager.dexoptSystemServer(options);
-        }
-
-        // Select the dex optimizer based on the force parameter.
-        // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
-        //       allocate an object here.
-        PackageDexOptimizer pdo = options.isForce()
-                ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
-                : mPackageDexOptimizer;
-
-        // Dexopt all dependencies first. Note: we ignore the return value and march on
-        // on errors.
-        // Note that we are going to call performDexOpt on those libraries as many times as
-        // they are referenced in packages. When we do a batch of performDexOpt (for example
-        // at boot, or background job), the passed 'targetCompilerFilter' stays the same,
-        // and the first package that uses the library will dexopt it. The
-        // others will see that the compiled code for the library is up to date.
-        Collection<SharedLibraryInfo> deps = findSharedLibraries(pkgSetting);
-        final String[] instructionSets = getAppDexInstructionSets(
-                AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
-                AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
-        if (!deps.isEmpty()) {
-            DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
-                    options.getCompilationReason(), options.getCompilerFilter(),
-                    options.getSplitName(),
-                    options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
-            for (SharedLibraryInfo info : deps) {
-                AndroidPackage depPackage = null;
-                PackageSetting depPackageSetting = null;
-                synchronized (mLock) {
-                    depPackage = mPackages.get(info.getPackageName());
-                    depPackageSetting = mSettings.getPackageLPr(info.getPackageName());
-                }
-                if (depPackage != null && depPackageSetting != null) {
-                    // TODO: Analyze and investigate if we (should) profile libraries.
-                    pdo.performDexOpt(depPackage, depPackageSetting, instructionSets,
-                            getOrCreateCompilerPackageStats(depPackage),
-                            mDexManager.getPackageUseInfoOrDefault(depPackage.getPackageName()),
-                            libraryOptions);
-                } else {
-                    // TODO(ngeoffray): Support dexopting system shared libraries.
-                }
-            }
-        }
-
-        return pdo.performDexOpt(p, pkgSetting, instructionSets,
-                getOrCreateCompilerPackageStats(p),
-                mDexManager.getPackageUseInfoOrDefault(p.getPackageName()), options);
+        return mDexOptHelper.performDexOptSecondary(packageName, compilerFilter, force);
     }
 
     /**
@@ -6284,35 +4992,8 @@
         return mDexManager;
     }
 
-    private static List<SharedLibraryInfo> findSharedLibraries(PackageSetting pkgSetting) {
-        if (!pkgSetting.getPkgState().getUsesLibraryInfos().isEmpty()) {
-            ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
-            Set<String> collectedNames = new HashSet<>();
-            for (SharedLibraryInfo info : pkgSetting.getPkgState().getUsesLibraryInfos()) {
-                findSharedLibrariesRecursive(info, retValue, collectedNames);
-            }
-            return retValue;
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
-            ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
-        if (!collectedNames.contains(info.getName())) {
-            collectedNames.add(info.getName());
-            collected.add(info);
-
-            if (info.getDependencies() != null) {
-                for (SharedLibraryInfo dep : info.getDependencies()) {
-                    findSharedLibrariesRecursive(dep, collected, collectedNames);
-                }
-            }
-        }
-    }
-
     List<PackageSetting> findSharedNonSystemLibraries(PackageSetting pkgSetting) {
-        List<SharedLibraryInfo> deps = findSharedLibraries(pkgSetting);
+        List<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
         if (!deps.isEmpty()) {
             List<PackageSetting> retValue = new ArrayList<>();
             synchronized (mLock) {
@@ -6335,27 +5016,6 @@
         return mComputer.getSharedLibraryInfoLPr(name, version);
     }
 
-    @Nullable
-    public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
-            @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
-        if (newLibraries != null) {
-            final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
-            SharedLibraryInfo info = null;
-            if (versionedLib != null) {
-                info = versionedLib.get(version);
-            }
-            if (info != null) {
-                return info;
-            }
-        }
-        final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
-        if (versionedLib == null) {
-            return null;
-        }
-        return versionedLib.get(version);
-    }
-
     SharedLibraryInfo getLatestSharedLibraVersionLPr(AndroidPackage pkg) {
         WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
                 pkg.getStaticSharedLibName());
@@ -6437,33 +5097,7 @@
 
     @Override
     public void forceDexOpt(String packageName) {
-        enforceSystemOrRoot("forceDexOpt");
-
-        AndroidPackage pkg;
-        PackageSetting pkgSetting;
-        synchronized (mLock) {
-            pkg = mPackages.get(packageName);
-            pkgSetting = mSettings.getPackageLPr(packageName);
-            if (pkg == null || pkgSetting == null) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
-        }
-
-        synchronized (mInstallLock) {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
-            // Whoever is calling forceDexOpt wants a compiled package.
-            // Don't use profiles since that may cause compilation to be skipped.
-            final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
-                    new DexoptOptions(packageName,
-                            getDefaultCompilerFilter(),
-                            DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
-
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-            if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
-                throw new IllegalStateException("Failed to dexopt: " + res);
-            }
-        }
+        mDexOptHelper.forceDexOpt(packageName);
     }
 
     int[] resolveUserIds(int userId) {
@@ -6535,58 +5169,15 @@
             @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
             Map<String, AndroidPackage> availablePackages)
             throws PackageManagerException {
-        final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
-                pkgSetting.getPkg(), availablePackages, mSharedLibraries, null /* newLibraries */,
-                mInjector.getCompatibility());
+        final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
+                SharedLibraryHelper.collectSharedLibraryInfos(
+                        pkgSetting.getPkg(), availablePackages, mSharedLibraries,
+                        null /* newLibraries */, mInjector.getCompatibility());
         executeSharedLibrariesUpdateLPr(pkg, pkgSetting, changingLib, changingLibSetting,
                 sharedLibraryInfos, mUserManager.getUserIds());
     }
 
-    private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
-            Map<String, AndroidPackage> availablePackages,
-            @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
-            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries,
-            PlatformCompat platformCompat) throws PackageManagerException {
-        if (pkg == null) {
-            return null;
-        }
-        // The collection used here must maintain the order of addition (so
-        // that libraries are searched in the correct order) and must have no
-        // duplicates.
-        ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
-        if (!pkg.getUsesLibraries().isEmpty()) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
-                    pkg.getPackageName(), true, pkg.getTargetSdkVersion(), null,
-                    availablePackages, existingLibraries, newLibraries);
-        }
-        if (!pkg.getUsesStaticLibraries().isEmpty()) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
-                    pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
-                    pkg.getPackageName(), true, pkg.getTargetSdkVersion(), usesLibraryInfos,
-                    availablePackages, existingLibraries, newLibraries);
-        }
-        if (!pkg.getUsesOptionalLibraries().isEmpty()) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(),
-                    null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
-                    usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
-        }
-        if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
-                pkg.getPackageName(), pkg.getTargetSdkVersion())) {
-            if (!pkg.getUsesNativeLibraries().isEmpty()) {
-                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
-                        null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
-                        usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
-            }
-            if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
-                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
-                        null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
-                        usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
-            }
-        }
-        return usesLibraryInfos;
-    }
-
-    private void executeSharedLibrariesUpdateLPr(AndroidPackage pkg,
+    void executeSharedLibrariesUpdateLPr(AndroidPackage pkg,
             @NonNull PackageSetting pkgSetting, @Nullable AndroidPackage changingLib,
             @Nullable PackageSetting changingLibSetting,
             ArrayList<SharedLibraryInfo> usesLibraryInfos, int[] allUsers) {
@@ -6634,102 +5225,6 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
-            @NonNull List<String> requestedLibraries,
-            @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
-            @NonNull String packageName, boolean required, int targetSdk,
-            @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
-            @NonNull final Map<String, AndroidPackage> availablePackages,
-            @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
-            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
-            throws PackageManagerException {
-        final int libCount = requestedLibraries.size();
-        for (int i = 0; i < libCount; i++) {
-            final String libName = requestedLibraries.get(i);
-            final long libVersion = requiredVersions != null ? requiredVersions[i]
-                    : SharedLibraryInfo.VERSION_UNDEFINED;
-            final SharedLibraryInfo libraryInfo =
-                    getSharedLibraryInfo(libName, libVersion, existingLibraries, newLibraries);
-            if (libraryInfo == null) {
-                if (required) {
-                    throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                            "Package " + packageName + " requires unavailable shared library "
-                                    + libName + "; failing!");
-                } else if (DEBUG_SHARED_LIBRARIES) {
-                    Slog.i(TAG, "Package " + packageName
-                            + " desires unavailable shared library "
-                            + libName + "; ignoring!");
-                }
-            } else {
-                if (requiredVersions != null && requiredCertDigests != null) {
-                    if (libraryInfo.getLongVersion() != requiredVersions[i]) {
-                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                            "Package " + packageName + " requires unavailable static shared"
-                                    + " library " + libName + " version "
-                                    + libraryInfo.getLongVersion() + "; failing!");
-                    }
-                    AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
-                    SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
-                    if (libPkg == null) {
-                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                "Package " + packageName + " requires unavailable static shared"
-                                        + " library; failing!");
-                    }
-                    final String[] expectedCertDigests = requiredCertDigests[i];
-                    if (expectedCertDigests.length > 1) {
-                        // For apps targeting O MR1 we require explicit enumeration of all certs.
-                        final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
-                                ? PackageUtils.computeSignaturesSha256Digests(
-                                libPkg.getSignatures())
-                                : PackageUtils.computeSignaturesSha256Digests(
-                                        new Signature[]{libPkg.getSignatures()[0]});
-
-                        // Take a shortcut if sizes don't match. Note that if an app doesn't
-                        // target O we don't parse the "additional-certificate" tags similarly
-                        // how we only consider all certs only for apps targeting O (see above).
-                        // Therefore, the size check is safe to make.
-                        if (expectedCertDigests.length != libCertDigests.length) {
-                            throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                    "Package " + packageName + " requires differently signed" +
-                                            " static shared library; failing!");
-                        }
-
-                        // Use a predictable order as signature order may vary
-                        Arrays.sort(libCertDigests);
-                        Arrays.sort(expectedCertDigests);
-
-                        final int certCount = libCertDigests.length;
-                        for (int j = 0; j < certCount; j++) {
-                            if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
-                                throw new PackageManagerException(
-                                        INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                        "Package " + packageName + " requires differently signed" +
-                                                " static shared library; failing!");
-                            }
-                        }
-                    } else {
-                        // lib signing cert could have rotated beyond the one expected, check to see
-                        // if the new one has been blessed by the old
-                        byte[] digestBytes = HexEncoding.decode(
-                                expectedCertDigests[0], false /* allowSingleChar */);
-                        if (!libPkg.hasSha256Certificate(digestBytes)) {
-                            throw new PackageManagerException(
-                                    INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                                    "Package " + packageName + " requires differently signed" +
-                                            " static shared library; failing!");
-                        }
-                    }
-                }
-                if (outUsedLibraries == null) {
-                    outUsedLibraries = new ArrayList<>();
-                }
-                outUsedLibraries.add(libraryInfo);
-            }
-        }
-        return outUsedLibraries;
-    }
-
     private static boolean hasString(List<String> list, List<String> which) {
         if (list == null || which == null) {
             return false;
@@ -6745,7 +5240,7 @@
     }
 
     @GuardedBy("mLock")
-    private ArrayList<AndroidPackage> updateAllSharedLibrariesLocked(
+    ArrayList<AndroidPackage> updateAllSharedLibrariesLocked(
             @Nullable AndroidPackage updatedPkg, @Nullable PackageSetting updatedPkgSetting,
             Map<String, AndroidPackage> availablePackages) {
         ArrayList<AndroidPackage> resultList = null;
@@ -6811,130 +5306,6 @@
         return resultList;
     }
 
-    /**
-     * Commits the package scan and modifies system state.
-     * <p><em>WARNING:</em> The method may throw an excpetion in the middle
-     * of committing the package, leaving the system in an inconsistent state.
-     * This needs to be fixed so, once we get to this point, no errors are
-     * possible and the system is not left in an inconsistent state.
-     */
-    @GuardedBy({"mLock", "mInstallLock"})
-    AndroidPackage commitReconciledScanResultLocked(
-            @NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
-        final ScanResult result = reconciledPkg.mScanResult;
-        final ScanRequest request = result.mRequest;
-        // TODO(b/135203078): Move this even further away
-        ParsedPackage parsedPackage = request.mParsedPackage;
-        if ("android".equals(parsedPackage.getPackageName())) {
-            // TODO(b/135203078): Move this to initial parse
-            parsedPackage.setVersionCode(mSdkVersion)
-                    .setVersionCodeMajor(0);
-        }
-
-        final AndroidPackage oldPkg = request.mOldPkg;
-        final @ParseFlags int parseFlags = request.mParseFlags;
-        final @ScanFlags int scanFlags = request.mScanFlags;
-        final PackageSetting oldPkgSetting = request.mOldPkgSetting;
-        final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
-        final UserHandle user = request.mUser;
-        final String realPkgName = request.mRealPkgName;
-        final List<String> changedAbiCodePath = result.mChangedAbiCodePath;
-        final PackageSetting pkgSetting;
-        if (request.mPkgSetting != null && request.mPkgSetting.getSharedUser() != null
-                && request.mPkgSetting.getSharedUser() != result.mPkgSetting.getSharedUser()) {
-            // shared user changed, remove from old shared user
-            request.mPkgSetting.getSharedUser().removePackage(request.mPkgSetting);
-        }
-        if (result.mExistingSettingCopied) {
-            pkgSetting = request.mPkgSetting;
-            pkgSetting.updateFrom(result.mPkgSetting);
-        } else {
-            pkgSetting = result.mPkgSetting;
-            if (originalPkgSetting != null) {
-                mSettings.addRenamedPackageLPw(
-                        AndroidPackageUtils.getRealPackageOrNull(parsedPackage),
-                        originalPkgSetting.getPackageName());
-                mTransferredPackages.add(originalPkgSetting.getPackageName());
-            } else {
-                mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
-            }
-        }
-        if (pkgSetting.getSharedUser() != null) {
-            pkgSetting.getSharedUser().addPackage(pkgSetting);
-        }
-        if (reconciledPkg.mInstallArgs != null
-                        && reconciledPkg.mInstallArgs.mForceQueryableOverride) {
-            pkgSetting.setForceQueryableOverride(true);
-        }
-
-        // If this is part of a standard install, set the initiating package name, else rely on
-        // previous device state.
-        if (reconciledPkg.mInstallArgs != null) {
-            InstallSource installSource = reconciledPkg.mInstallArgs.mInstallSource;
-            if (installSource.initiatingPackageName != null) {
-                final PackageSetting ips = mSettings.getPackageLPr(
-                        installSource.initiatingPackageName);
-                if (ips != null) {
-                    installSource = installSource.setInitiatingPackageSignatures(
-                            ips.getSignatures());
-                }
-            }
-            pkgSetting.setInstallSource(installSource);
-        }
-
-        // TODO(toddke): Consider a method specifically for modifying the Package object
-        // post scan; or, moving this stuff out of the Package object since it has nothing
-        // to do with the package on disk.
-        // We need to have this here because addUserToSettingLPw() is sometimes responsible
-        // for creating the application ID. If we did this earlier, we would be saving the
-        // correct ID.
-        parsedPackage.setUid(pkgSetting.getAppId());
-        final AndroidPackage pkg = parsedPackage.hideAsFinal();
-
-        mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
-
-        if (realPkgName != null) {
-            mTransferredPackages.add(pkg.getPackageName());
-        }
-
-        if (reconciledPkg.mCollectedSharedLibraryInfos != null) {
-            executeSharedLibrariesUpdateLPr(pkg, pkgSetting, null, null,
-                    reconciledPkg.mCollectedSharedLibraryInfos, allUsers);
-        }
-
-        final KeySetManagerService ksms = mSettings.getKeySetManagerService();
-        if (reconciledPkg.mRemoveAppKeySetData) {
-            ksms.removeAppKeySetDataLPw(pkg.getPackageName());
-        }
-        if (reconciledPkg.mSharedUserSignaturesChanged) {
-            pkgSetting.getSharedUser().signaturesChanged = Boolean.TRUE;
-            pkgSetting.getSharedUser().signatures.mSigningDetails = reconciledPkg.mSigningDetails;
-        }
-        pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails);
-
-        if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
-            for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
-                final String codePathString = changedAbiCodePath.get(i);
-                try {
-                    mInstaller.rmdex(codePathString,
-                            getDexCodeInstructionSet(getPreferredInstructionSet()));
-                } catch (InstallerException ignored) {
-                }
-            }
-        }
-
-        final int userId = user == null ? 0 : user.getIdentifier();
-        // Modify state for the given package setting
-        commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
-                (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
-        if (pkgSetting.getInstantApp(userId)) {
-            mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.getAppId());
-        }
-        pkgSetting.setStatesOnCommit();
-
-        return pkg;
-    }
-
     @GuardedBy("mLock")
     private void addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
         if (nonStaticSharedLibExistsLocked(entry.name)) {
@@ -6952,17 +5323,12 @@
 
     @GuardedBy("mLock")
     private boolean nonStaticSharedLibExistsLocked(String name) {
-        return sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED, mSharedLibraries);
-    }
-
-    private static boolean sharedLibExists(final String name, final long version,
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) {
-        WatchedLongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
-        return versionedLib != null && versionedLib.indexOfKey(version) >= 0;
+        return SharedLibraryHelper.sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED,
+                mSharedLibraries);
     }
 
     @GuardedBy("mLock")
-    private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) {
+    void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) {
         final String name = libraryInfo.getName();
         WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
@@ -6976,44 +5342,6 @@
         versionedLib.put(libraryInfo.getLongVersion(), libraryInfo);
     }
 
-    boolean removeSharedLibraryLPw(String name, long version) {
-        WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
-        if (versionedLib == null) {
-            return false;
-        }
-        final int libIdx = versionedLib.indexOfKey(version);
-        if (libIdx < 0) {
-            return false;
-        }
-        SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
-
-        // Remove the shared library overlays from its dependent packages.
-        for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
-            final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
-                    libraryInfo, 0, Process.SYSTEM_UID, currentUserId);
-            if (dependents == null) {
-                continue;
-            }
-            for (VersionedPackage dependentPackage : dependents) {
-                final PackageSetting ps = mSettings.getPackageLPr(
-                        dependentPackage.getPackageName());
-                if (ps != null) {
-                    ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
-                }
-            }
-        }
-
-        versionedLib.remove(version);
-        if (versionedLib.size() <= 0) {
-            mSharedLibraries.remove(name);
-            if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
-                mStaticLibsByDeclaringPackage.remove(libraryInfo.getDeclaringPackage()
-                        .getPackageName());
-            }
-        }
-        return true;
-    }
-
     @Override
     public Property getProperty(String propertyName, String packageName, String className) {
         Objects.requireNonNull(propertyName);
@@ -7045,201 +5373,6 @@
         return new ParceledListSlice<>(result);
     }
 
-    /**
-     * Adds a scanned package to the system. When this method is finished, the package will
-     * be available for query, resolution, etc...
-     */
-    private void commitPackageSettings(@NonNull AndroidPackage pkg, @Nullable AndroidPackage oldPkg,
-            @NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting,
-            final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
-        final String pkgName = pkg.getPackageName();
-        if (mCustomResolverComponentName != null &&
-                mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
-            setUpCustomResolverActivity(pkg, pkgSetting);
-        }
-
-        if (pkg.getPackageName().equals("android")) {
-            synchronized (mLock) {
-                // Set up information for our fall-back user intent resolution activity.
-                mPlatformPackage = pkg;
-
-                // The instance stored in PackageManagerService is special cased to be non-user
-                // specific, so initialize all the needed fields here.
-                mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0,
-                        PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
-
-                if (!mResolverReplaced) {
-                    mResolveActivity.applicationInfo = mAndroidApplication;
-                    mResolveActivity.name = ResolverActivity.class.getName();
-                    mResolveActivity.packageName = mAndroidApplication.packageName;
-                    mResolveActivity.processName = "system:ui";
-                    mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-                    mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
-                    mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
-                    mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
-                    mResolveActivity.exported = true;
-                    mResolveActivity.enabled = true;
-                    mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
-                    mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
-                            | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
-                            | ActivityInfo.CONFIG_SCREEN_LAYOUT
-                            | ActivityInfo.CONFIG_ORIENTATION
-                            | ActivityInfo.CONFIG_KEYBOARD
-                            | ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
-                    mResolveInfo.activityInfo = mResolveActivity;
-                    mResolveInfo.priority = 0;
-                    mResolveInfo.preferredOrder = 0;
-                    mResolveInfo.match = 0;
-                    mResolveComponentName = new ComponentName(
-                            mAndroidApplication.packageName, mResolveActivity.name);
-                }
-                onChanged();
-            }
-        }
-
-        ArrayList<AndroidPackage> clientLibPkgs = null;
-        // writer
-        synchronized (mLock) {
-            if (!ArrayUtils.isEmpty(reconciledPkg.mAllowedSharedLibraryInfos)) {
-                for (SharedLibraryInfo info : reconciledPkg.mAllowedSharedLibraryInfos) {
-                    commitSharedLibraryInfoLocked(info);
-                }
-                final Map<String, AndroidPackage> combinedSigningDetails =
-                        reconciledPkg.getCombinedAvailablePackages();
-                try {
-                    // Shared libraries for the package need to be updated.
-                    updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
-                            combinedSigningDetails);
-                } catch (PackageManagerException e) {
-                    Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
-                }
-                // Update all applications that use this library. Skip when booting
-                // since this will be done after all packages are scaned.
-                if ((scanFlags & SCAN_BOOTING) == 0) {
-                    clientLibPkgs = updateAllSharedLibrariesLocked(pkg, pkgSetting,
-                            combinedSigningDetails);
-                }
-            }
-        }
-        if (reconciledPkg.mInstallResult != null) {
-            reconciledPkg.mInstallResult.mLibraryConsumers = clientLibPkgs;
-        }
-
-        if ((scanFlags & SCAN_BOOTING) != 0) {
-            // No apps can run during boot scan, so they don't need to be frozen
-        } else if ((scanFlags & SCAN_DONT_KILL_APP) != 0) {
-            // Caller asked to not kill app, so it's probably not frozen
-        } else if ((scanFlags & SCAN_IGNORE_FROZEN) != 0) {
-            // Caller asked us to ignore frozen check for some reason; they
-            // probably didn't know the package name
-        } else {
-            // We're doing major surgery on this package, so it better be frozen
-            // right now to keep it from launching
-            checkPackageFrozen(pkgName);
-        }
-
-        // Also need to kill any apps that are dependent on the library.
-        if (clientLibPkgs != null) {
-            for (int i=0; i<clientLibPkgs.size(); i++) {
-                AndroidPackage clientPkg = clientLibPkgs.get(i);
-                killApplication(clientPkg.getPackageName(),
-                        clientPkg.getUid(), "update lib");
-            }
-        }
-
-        // writer
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
-
-        synchronized (mLock) {
-            // We don't expect installation to fail beyond this point
-            // Add the new setting to mSettings
-            mSettings.insertPackageSettingLPw(pkgSetting, pkg);
-            // Add the new setting to mPackages
-            mPackages.put(pkg.getPackageName(), pkg);
-            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
-                mApexManager.registerApkInApex(pkg);
-            }
-
-            // Add the package's KeySets to the global KeySetManagerService
-            KeySetManagerService ksms = mSettings.getKeySetManagerService();
-            ksms.addScannedPackageLPw(pkg);
-
-            mComponentResolver.addAllComponents(pkg, chatty);
-            final boolean isReplace =
-                    reconciledPkg.mPrepareResult != null && reconciledPkg.mPrepareResult.mReplace;
-            mAppsFilter.addPackage(pkgSetting, isReplace);
-            mPackageProperty.addAllProperties(pkg);
-
-            if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) {
-                mDomainVerificationManager.addPackage(pkgSetting);
-            } else {
-                mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting);
-            }
-
-            int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
-            StringBuilder r = null;
-            int i;
-            for (i = 0; i < collectionSize; i++) {
-                ParsedInstrumentation a = pkg.getInstrumentations().get(i);
-                a.setPackageName(pkg.getPackageName());
-                mInstrumentation.put(a.getComponentName(), a);
-                if (chatty) {
-                    if (r == null) {
-                        r = new StringBuilder(256);
-                    } else {
-                        r.append(' ');
-                    }
-                    r.append(a.getName());
-                }
-            }
-            if (r != null) {
-                if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Instrumentation: " + r);
-            }
-
-            final List<String> protectedBroadcasts = pkg.getProtectedBroadcasts();
-            if (!protectedBroadcasts.isEmpty()) {
-                synchronized (mProtectedBroadcasts) {
-                    mProtectedBroadcasts.addAll(protectedBroadcasts);
-                }
-            }
-
-            mPermissionManager.onPackageAdded(pkg, (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
-        }
-
-        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-    }
-
-    private void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
-        synchronized (mLock) {
-            mResolverReplaced = true;
-
-            // The instance created in PackageManagerService is special cased to be non-user
-            // specific, so initialize all the needed fields here.
-            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0,
-                    PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
-
-            // Set up information for custom user intent resolution activity.
-            mResolveActivity.applicationInfo = appInfo;
-            mResolveActivity.name = mCustomResolverComponentName.getClassName();
-            mResolveActivity.packageName = pkg.getPackageName();
-            mResolveActivity.processName = pkg.getProcessName();
-            mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-            mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
-                    ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
-            mResolveActivity.theme = 0;
-            mResolveActivity.exported = true;
-            mResolveActivity.enabled = true;
-            mResolveInfo.activityInfo = mResolveActivity;
-            mResolveInfo.priority = 0;
-            mResolveInfo.preferredOrder = 0;
-            mResolveInfo.match = 0;
-            mResolveComponentName = mCustomResolverComponentName;
-            onChanged();
-            Slog.i(TAG, "Replacing default ResolverActivity with custom activity: " +
-                    mResolveComponentName);
-        }
-    }
-
     private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) {
         if (installerActivity == null) {
             if (DEBUG_INSTANT) {
@@ -7269,7 +5402,7 @@
         onChanged();
     }
 
-    private void killApplication(String pkgName, @AppIdInt int appId, String reason) {
+    void killApplication(String pkgName, @AppIdInt int appId, String reason) {
         killApplication(pkgName, appId, UserHandle.USER_ALL, reason);
     }
 
@@ -7357,7 +5490,7 @@
         }
     }
 
-    private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
+    void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
             int userId, int dataLoaderType) {
         final boolean isSystem = PackageManagerServiceUtils.isSystemApp(pkgSetting)
                 || PackageManagerServiceUtils.isUpdatedSystemApp(pkgSetting);
@@ -7643,136 +5776,10 @@
     @Override
     public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
             int installReason, List<String> whiteListedPermissions) {
-        return installExistingPackageAsUser(packageName, userId, installFlags, installReason,
-                whiteListedPermissions, null);
-    }
-
-    int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
-            @PackageManager.InstallFlags int installFlags,
-            @PackageManager.InstallReason int installReason,
-            @Nullable List<String> allowlistedRestrictedPermissions,
-            @Nullable IntentSender intentSender) {
-        if (DEBUG_INSTALL) {
-            Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
-                    + " installFlags=" + installFlags + " installReason=" + installReason
-                    + " allowlistedRestrictedPermissions=" + allowlistedRestrictedPermissions);
-        }
-
-        final int callingUid = Binder.getCallingUid();
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
-                != PackageManager.PERMISSION_GRANTED
-                && mContext.checkCallingOrSelfPermission(
-                        android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Neither user " + callingUid + " nor current process has "
-                    + android.Manifest.permission.INSTALL_PACKAGES + ".");
-        }
-        PackageSetting pkgSetting;
-        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
-                true /* checkShell */, "installExistingPackage for user " + userId);
-        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
-            return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
-        }
-
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            boolean installed = false;
-            final boolean instantApp =
-                    (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
-            final boolean fullApp =
-                    (installFlags & PackageManager.INSTALL_FULL_APP) != 0;
-
-            // writer
-            synchronized (mLock) {
-                pkgSetting = mSettings.getPackageLPr(packageName);
-                if (pkgSetting == null) {
-                    return PackageManager.INSTALL_FAILED_INVALID_URI;
-                }
-                if (!canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
-                    // only allow the existing package to be used if it's installed as a full
-                    // application for at least one user
-                    boolean installAllowed = false;
-                    for (int checkUserId : mUserManager.getUserIds()) {
-                        installAllowed = !pkgSetting.getInstantApp(checkUserId);
-                        if (installAllowed) {
-                            break;
-                        }
-                    }
-                    if (!installAllowed) {
-                        return PackageManager.INSTALL_FAILED_INVALID_URI;
-                    }
-                }
-                if (!pkgSetting.getInstalled(userId)) {
-                    pkgSetting.setInstalled(true, userId);
-                    pkgSetting.setHidden(false, userId);
-                    pkgSetting.setInstallReason(installReason, userId);
-                    pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId);
-                    mSettings.writePackageRestrictionsLPr(userId);
-                    mSettings.writeKernelMappingLPr(pkgSetting);
-                    installed = true;
-                } else if (fullApp && pkgSetting.getInstantApp(userId)) {
-                    // upgrade app from instant to full; we don't allow app downgrade
-                    installed = true;
-                }
-                setInstantAppForUser(mInjector, pkgSetting, userId, instantApp, fullApp);
-            }
-
-            if (installed) {
-                if (pkgSetting.getPkg() != null) {
-                    final PermissionManagerServiceInternal.PackageInstalledParams.Builder
-                            permissionParamsBuilder =
-                            new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
-                    if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
-                            != 0) {
-                        permissionParamsBuilder.setAllowlistedRestrictedPermissions(
-                                pkgSetting.getPkg().getRequestedPermissions());
-                    }
-                    mPermissionManager.onPackageInstalled(pkgSetting.getPkg(),
-                            Process.INVALID_UID /* previousAppId */,
-                            permissionParamsBuilder.build(), userId);
-                }
-
-                if (pkgSetting.getPkg() != null) {
-                    synchronized (mInstallLock) {
-                        // We don't need to freeze for a brand new install
-                        mAppDataHelper.prepareAppDataAfterInstallLIF(pkgSetting.getPkg());
-                    }
-                }
-                sendPackageAddedForUser(packageName, pkgSetting, userId, DataLoaderType.NONE);
-                synchronized (mLock) {
-                    updateSequenceNumberLP(pkgSetting, new int[]{ userId });
-                }
-                // start async restore with no post-install since we finish install here
-                PackageInstalledInfo res = new PackageInstalledInfo(
-                        PackageManager.INSTALL_SUCCEEDED);
-                res.mPkg = pkgSetting.getPkg();
-                res.mNewUsers = new int[]{ userId };
-
-                PostInstallData postInstallData =
-                        new PostInstallData(null, res, () -> {
-                            restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
-                                    userId);
-                            if (intentSender != null) {
-                                onRestoreComplete(res.mReturnCode, mContext, intentSender);
-                            }
-                        });
-                restoreAndPostInstall(userId, res, postInstallData);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-
-        return PackageManager.INSTALL_SUCCEEDED;
-    }
-
-    static void onRestoreComplete(int returnCode, Context context, IntentSender target) {
-        Intent fillIn = new Intent();
-        fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
-                PackageManager.installStatusToPublicStatus(returnCode));
-        try {
-            target.sendIntent(context, 0, fillIn, null, null);
-        } catch (SendIntentException ignored) {
-        }
+        final InstallPackageHelper installPackageHelper = new InstallPackageHelper(
+                this, mAppDataHelper);
+        return installPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+                installReason, whiteListedPermissions, null);
     }
 
     static void setInstantAppForUser(PackageManagerServiceInjector injector,
@@ -8222,7 +6229,7 @@
         final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId);
         final long callingId = Binder.clearCallingIdentity();
         try {
-            final String activeLauncherPackageName = mDefaultAppProvider.getDefaultHome(userId);
+            final String activeLauncherPackageName = getActiveLauncherPackageName(userId);
             final String dialerPackageName = mDefaultAppProvider.getDefaultDialer(userId);
             for (int i = 0; i < packageNames.length; i++) {
                 canSuspend[i] = false;
@@ -8349,7 +6356,8 @@
 
     @Override
     public void finishPackageInstall(int token, boolean didLaunch) {
-        enforceSystemOrRoot("Only the system is allowed to finish installs");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system is allowed to finish installs");
 
         if (DEBUG_INSTALL) {
             Slog.v(TAG, "BM finishing package install for " + token);
@@ -8409,24 +6417,12 @@
             ArrayList<IntentFilter> result = new ArrayList<>();
             for (int n=0; n<count; n++) {
                 ParsedActivity activity = pkg.getActivities().get(n);
-                if (activity.getIntents() != null && activity.getIntents().size() > 0) {
-                    result.addAll(activity.getIntents());
+                List<ParsedIntentInfo> intentInfos = activity.getIntents();
+                for (int index = 0; index < intentInfos.size(); index++) {
+                    result.add(new IntentFilter(intentInfos.get(index).getIntentFilter()));
                 }
             }
-            return new ParceledListSlice<IntentFilter>(result) {
-                @Override
-                protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
-                    parcelable.writeToParcel(dest, callFlags);
-                }
-
-                @Override
-                protected void writeParcelableCreator(IntentFilter parcelable, Parcel dest) {
-                    // All Parcel#writeParcelableCreator does is serialize the class name to
-                    // access via reflection to grab its CREATOR. This does that manually, pointing
-                    // to the parent IntentFilter so that all of the subclass fields are ignored.
-                    dest.writeString(IntentFilter.class.getName());
-                }
-            };
+            return new ParceledListSlice<IntentFilter>(result);
         }
     }
 
@@ -8561,144 +6557,6 @@
         }
     }
 
-    /** @param data Post-install is performed only if this is non-null. */
-    void restoreAndPostInstall(
-            int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
-        if (DEBUG_INSTALL) {
-            Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.mPkg);
-        }
-
-        // A restore should be requested at this point if (a) the install
-        // succeeded, (b) the operation is not an update.
-        final boolean update = res.mRemovedInfo != null
-                && res.mRemovedInfo.mRemovedPackage != null;
-        boolean doRestore = !update && res.mPkg != null;
-
-        // Set up the post-install work request bookkeeping.  This will be used
-        // and cleaned up by the post-install event handling regardless of whether
-        // there's a restore pass performed.  Token values are >= 1.
-        int token;
-        if (mNextInstallToken < 0) mNextInstallToken = 1;
-        token = mNextInstallToken++;
-        if (data != null) {
-            mRunningInstalls.put(token, data);
-        } else if (DEBUG_INSTALL) {
-            Log.v(TAG, "No post-install required for " + token);
-        }
-
-        if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
-
-        if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
-            // Pass responsibility to the Backup Manager.  It will perform a
-            // restore if appropriate, then pass responsibility back to the
-            // Package Manager to run the post-install observer callbacks
-            // and broadcasts.
-            if (res.mFreezer != null) {
-                res.mFreezer.close();
-            }
-            doRestore = performBackupManagerRestore(userId, token, res);
-        }
-
-        // If this is an update to a package that might be potentially downgraded, then we
-        // need to check with the rollback manager whether there's any userdata that might
-        // need to be snapshotted or restored for the package.
-        //
-        // TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
-        if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
-            doRestore = performRollbackManagerRestore(userId, token, res, data);
-        }
-
-        if (!doRestore) {
-            // No restore possible, or the Backup Manager was mysteriously not
-            // available -- just fire the post-install work request directly.
-            if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
-
-            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);
-
-            Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
-            mHandler.sendMessage(msg);
-        }
-    }
-
-    /**
-     * Perform Backup Manager restore for a given {@link PackageInstalledInfo}.
-     * Returns whether the restore successfully completed.
-     */
-    private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
-        IBackupManager bm = IBackupManager.Stub.asInterface(
-                ServiceManager.getService(Context.BACKUP_SERVICE));
-        if (bm != null) {
-            // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
-            // in the BackupManager. USER_ALL is used in compatibility tests.
-            if (userId == UserHandle.USER_ALL) {
-                userId = UserHandle.USER_SYSTEM;
-            }
-            if (DEBUG_INSTALL) {
-                Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
-            }
-            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
-            try {
-                if (bm.isUserReadyForBackup(userId)) {
-                    bm.restoreAtInstallForUser(
-                            userId, res.mPkg.getPackageName(), token);
-                } else {
-                    Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
-                            + "didn't take place.");
-                    return false;
-                }
-            } catch (RemoteException e) {
-                // can't happen; the backup manager is local
-            } catch (Exception e) {
-                Slog.e(TAG, "Exception trying to enqueue restore", e);
-                return false;
-            }
-        } else {
-            Slog.e(TAG, "Backup Manager not found!");
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Perform Rollback Manager restore for a given {@link PackageInstalledInfo}.
-     * Returns whether the restore successfully completed.
-     */
-    private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
-            PostInstallData data) {
-        RollbackManagerInternal rm = mInjector.getLocalService(RollbackManagerInternal.class);
-
-        final String packageName = res.mPkg.getPackageName();
-        final int[] allUsers = mUserManager.getUserIds();
-        final int[] installedUsers;
-
-        final PackageSetting ps;
-        int appId = -1;
-        long ceDataInode = -1;
-        synchronized (mLock) {
-            ps = mSettings.getPackageLPr(packageName);
-            if (ps != null) {
-                appId = ps.getAppId();
-                ceDataInode = ps.getCeDataInode(userId);
-            }
-
-            // NOTE: We ignore the user specified in the InstallParam because we know this is
-            // an update, and hence need to restore data for all installed users.
-            installedUsers = ps.queryInstalledUsers(allUsers, true);
-        }
-
-        boolean doSnapshotOrRestore = data != null && data.args != null
-                && ((data.args.mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
-                || (data.args.mInstallFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
-
-        if (ps != null && doSnapshotOrRestore) {
-            final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
-            rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
-                    appId, ceDataInode, seInfo, token);
-            return true;
-        }
-        return false;
-    }
-
     /**
      * Callback from PackageSettings whenever an app is first transitioned out of the
      * 'stopped' state.  Normally we just issue the broadcast, but we can't do that if
@@ -8747,343 +6605,7 @@
         });
     }
 
-    /**
-     * Create args that describe an existing installed package. Typically used
-     * when cleaning up old installs, or used as a move source.
-     */
-    InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) {
-        return new FileInstallArgs(codePath, instructionSets, this);
-    }
-
-    @GuardedBy("mLock")
-    Map<String, ReconciledPackage> reconcilePackagesLocked(
-            final ReconcileRequest request, KeySetManagerService ksms,
-            PackageManagerServiceInjector injector)
-            throws ReconcileFailure {
-        final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
-
-        final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
-
-        // make a copy of the existing set of packages so we can combine them with incoming packages
-        final ArrayMap<String, AndroidPackage> combinedPackages =
-                new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
-
-        combinedPackages.putAll(request.mAllPackages);
-
-        final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
-                new ArrayMap<>();
-
-        for (String installPackageName : scannedPackages.keySet()) {
-            final ScanResult scanResult = scannedPackages.get(installPackageName);
-
-            // add / replace existing with incoming packages
-            combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
-                    scanResult.mRequest.mParsedPackage);
-
-            // in the first pass, we'll build up the set of incoming shared libraries
-            final List<SharedLibraryInfo> allowedSharedLibInfos =
-                    getAllowedSharedLibInfos(scanResult, request.mSharedLibrarySource);
-            final SharedLibraryInfo staticLib = scanResult.mStaticSharedLibraryInfo;
-            if (allowedSharedLibInfos != null) {
-                for (SharedLibraryInfo info : allowedSharedLibInfos) {
-                    if (!addSharedLibraryToPackageVersionMap(incomingSharedLibraries, info)) {
-                        throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
-                                + " is being installed twice in this set!");
-                    }
-                }
-            }
-
-            // the following may be null if we're just reconciling on boot (and not during install)
-            final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
-            final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
-            final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
-            final boolean isInstall = installArgs != null;
-            if (isInstall && (res == null || prepareResult == null)) {
-                throw new ReconcileFailure("Reconcile arguments are not balanced for "
-                        + installPackageName + "!");
-            }
-
-            final DeletePackageAction deletePackageAction;
-            // we only want to try to delete for non system apps
-            if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
-                final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
-                final int deleteFlags = PackageManager.DELETE_KEEP_DATA
-                        | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
-                deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
-                        prepareResult.mOriginalPs, prepareResult.mDisabledPs,
-                        deleteFlags, null /* all users */);
-                if (deletePackageAction == null) {
-                    throw new ReconcileFailure(
-                            PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
-                            "May not delete " + installPackageName + " to replace");
-                }
-            } else {
-                deletePackageAction = null;
-            }
-
-            final int scanFlags = scanResult.mRequest.mScanFlags;
-            final int parseFlags = scanResult.mRequest.mParseFlags;
-            final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
-
-            final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
-            final PackageSetting lastStaticSharedLibSetting =
-                    request.mLastStaticSharedLibSettings.get(installPackageName);
-            final PackageSetting signatureCheckPs =
-                    (prepareResult != null && lastStaticSharedLibSetting != null)
-                            ? lastStaticSharedLibSetting
-                            : scanResult.mPkgSetting;
-            boolean removeAppKeySetData = false;
-            boolean sharedUserSignaturesChanged = false;
-            SigningDetails signingDetails = null;
-            if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
-                if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
-                    // We just determined the app is signed correctly, so bring
-                    // over the latest parsed certs.
-                } else {
-                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
-                        throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
-                                "Package " + parsedPackage.getPackageName()
-                                        + " upgrade keys do not match the previously installed"
-                                        + " version");
-                    } else {
-                        String msg = "System package " + parsedPackage.getPackageName()
-                                + " signature changed; retaining data.";
-                        reportSettingsProblem(Log.WARN, msg);
-                    }
-                }
-                signingDetails = parsedPackage.getSigningDetails();
-            } else {
-                try {
-                    final VersionInfo versionInfo = request.mVersionInfos.get(installPackageName);
-                    final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
-                    final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
-                    final boolean isRollback = installArgs != null
-                            && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
-                    final boolean compatMatch = verifySignatures(signatureCheckPs,
-                            disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
-                            compareRecover, isRollback);
-                    // The new KeySets will be re-added later in the scanning process.
-                    if (compatMatch) {
-                        removeAppKeySetData = true;
-                    }
-                    // We just determined the app is signed correctly, so bring
-                    // over the latest parsed certs.
-                    signingDetails = parsedPackage.getSigningDetails();
-
-                    // if this is is a sharedUser, check to see if the new package is signed by a
-                    // newer
-                    // signing certificate than the existing one, and if so, copy over the new
-                    // details
-                    if (signatureCheckPs.getSharedUser() != null) {
-                        // Attempt to merge the existing lineage for the shared SigningDetails with
-                        // the lineage of the new package; if the shared SigningDetails are not
-                        // returned this indicates the new package added new signers to the lineage
-                        // and/or changed the capabilities of existing signers in the lineage.
-                        SigningDetails sharedSigningDetails =
-                                signatureCheckPs.getSharedUser().signatures.mSigningDetails;
-                        SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
-                                signingDetails);
-                        if (mergedDetails != sharedSigningDetails) {
-                            signatureCheckPs.getSharedUser().signatures.mSigningDetails =
-                                    mergedDetails;
-                        }
-                        if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
-                            signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
-                        }
-                    }
-                } catch (PackageManagerException e) {
-                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
-                        throw new ReconcileFailure(e);
-                    }
-                    signingDetails = parsedPackage.getSigningDetails();
-
-                    // If the system app is part of a shared user we allow that shared user to
-                    // change
-                    // signatures as well as part of an OTA. We still need to verify that the
-                    // signatures
-                    // are consistent within the shared user for a given boot, so only allow
-                    // updating
-                    // the signatures on the first package scanned for the shared user (i.e. if the
-                    // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
-                    if (signatureCheckPs.getSharedUser() != null) {
-                        final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
-                                .signatures.mSigningDetails.getSignatures();
-                        if (signatureCheckPs.getSharedUser().signaturesChanged != null
-                                && compareSignatures(sharedUserSignatures,
-                                parsedPackage.getSigningDetails().getSignatures())
-                                        != PackageManager.SIGNATURE_MATCH) {
-                            if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
-                                // Mismatched signatures is an error and silently skipping system
-                                // packages will likely break the device in unforeseen ways.
-                                // However, we allow the device to boot anyway because, prior to Q,
-                                // vendors were not expecting the platform to crash in this
-                                // situation.
-                                // This WILL be a hard failure on any new API levels after Q.
-                                throw new ReconcileFailure(
-                                        INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
-                                        "Signature mismatch for shared user: "
-                                                + scanResult.mPkgSetting.getSharedUser());
-                            } else {
-                                // Treat mismatched signatures on system packages using a shared
-                                // UID as
-                                // fatal for the system overall, rather than just failing to install
-                                // whichever package happened to be scanned later.
-                                throw new IllegalStateException(
-                                        "Signature mismatch on system package "
-                                                + parsedPackage.getPackageName()
-                                                + " for shared user "
-                                                + scanResult.mPkgSetting.getSharedUser());
-                            }
-                        }
-
-                        sharedUserSignaturesChanged = true;
-                        signatureCheckPs.getSharedUser().signatures.mSigningDetails =
-                                parsedPackage.getSigningDetails();
-                        signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
-                    }
-                    // File a report about this.
-                    String msg = "System package " + parsedPackage.getPackageName()
-                            + " signature changed; retaining data.";
-                    reportSettingsProblem(Log.WARN, msg);
-                } catch (IllegalArgumentException e) {
-                    // should never happen: certs matched when checking, but not when comparing
-                    // old to new for sharedUser
-                    throw new RuntimeException(
-                            "Signing certificates comparison made on incomparable signing details"
-                                    + " but somehow passed verifySignatures!", e);
-                }
-            }
-
-            result.put(installPackageName,
-                    new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
-                            res, request.mPreparedPackages.get(installPackageName), scanResult,
-                            deletePackageAction, allowedSharedLibInfos, signingDetails,
-                            sharedUserSignaturesChanged, removeAppKeySetData));
-        }
-
-        for (String installPackageName : scannedPackages.keySet()) {
-            // Check all shared libraries and map to their actual file path.
-            // We only do this here for apps not on a system dir, because those
-            // are the only ones that can fail an install due to this.  We
-            // will take care of the system apps by updating all of their
-            // library paths after the scan is done. Also during the initial
-            // scan don't update any libs as we do this wholesale after all
-            // apps are scanned to avoid dependency based scanning.
-            final ScanResult scanResult = scannedPackages.get(installPackageName);
-            if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
-                    || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
-                    != 0) {
-                continue;
-            }
-            try {
-                result.get(installPackageName).mCollectedSharedLibraryInfos =
-                        collectSharedLibraryInfos(scanResult.mRequest.mParsedPackage,
-                                combinedPackages, request.mSharedLibrarySource,
-                                incomingSharedLibraries, injector.getCompatibility());
-
-            } catch (PackageManagerException e) {
-                throw new ReconcileFailure(e.error, e.getMessage());
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Compare the newly scanned package with current system state to see which of its declared
-     * shared libraries should be allowed to be added to the system.
-     */
-    private static List<SharedLibraryInfo> getAllowedSharedLibInfos(
-            ScanResult scanResult,
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
-        // Let's used the parsed package as scanResult.pkgSetting may be null
-        final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
-        if (scanResult.mStaticSharedLibraryInfo == null
-                && scanResult.mDynamicSharedLibraryInfos == null) {
-            return null;
-        }
-
-        // Any app can add new static shared libraries
-        if (scanResult.mStaticSharedLibraryInfo != null) {
-            return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
-        }
-        final boolean hasDynamicLibraries = parsedPackage.isSystem()
-                        && scanResult.mDynamicSharedLibraryInfos != null;
-        if (!hasDynamicLibraries) {
-            return null;
-        }
-        final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
-                .isUpdatedSystemApp();
-        // We may not yet have disabled the updated package yet, so be sure to grab the
-        // current setting if that's the case.
-        final PackageSetting updatedSystemPs = isUpdatedSystemApp
-                ? scanResult.mRequest.mDisabledPkgSetting == null
-                        ? scanResult.mRequest.mOldPkgSetting
-                        : scanResult.mRequest.mDisabledPkgSetting
-                : null;
-        if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
-                || updatedSystemPs.getPkg().getLibraryNames() == null)) {
-            Slog.w(TAG, "Package " + parsedPackage.getPackageName()
-                    + " declares libraries that are not declared on the system image; skipping");
-            return null;
-        }
-        final ArrayList<SharedLibraryInfo> infos =
-                new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
-        for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
-            final String name = info.getName();
-            if (isUpdatedSystemApp) {
-                // New library entries can only be added through the
-                // system image.  This is important to get rid of a lot
-                // of nasty edge cases: for example if we allowed a non-
-                // system update of the app to add a library, then uninstalling
-                // the update would make the library go away, and assumptions
-                // we made such as through app install filtering would now
-                // have allowed apps on the device which aren't compatible
-                // with it.  Better to just have the restriction here, be
-                // conservative, and create many fewer cases that can negatively
-                // impact the user experience.
-                if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
-                    Slog.w(TAG, "Package " + parsedPackage.getPackageName()
-                            + " declares library " + name
-                            + " that is not declared on system image; skipping");
-                    continue;
-                }
-            }
-            if (sharedLibExists(
-                    name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
-                Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
-                        + name + " that already exists; skipping");
-                continue;
-            }
-            infos.add(info);
-        }
-        return infos;
-    }
-
-    /**
-     * Returns false if the adding shared library already exists in the map and so could not be
-     * added.
-     */
-    private static boolean addSharedLibraryToPackageVersionMap(
-            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
-            SharedLibraryInfo library) {
-        final String name = library.getName();
-        if (target.containsKey(name)) {
-            if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
-                // We've already added this non-version-specific library to the map.
-                return false;
-            } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
-                // We've already added this version of a version-specific library to the map.
-                return false;
-            }
-        } else {
-            target.put(name, new WatchedLongSparseArray<>());
-        }
-        target.get(name).put(library.getLongVersion(), library);
-        return true;
-    }
-
-    @Nullable PackageSetting getPackageSettingForUser(String packageName, int callingUid,
+    private @Nullable PackageSetting getPackageSettingForUser(String packageName, int callingUid,
             int userId) {
         final PackageSetting ps;
         synchronized (mLock) {
@@ -9146,34 +6668,8 @@
     @Override
     public void deleteExistingPackageAsUser(VersionedPackage versionedPackage,
             final IPackageDeleteObserver2 observer, final int userId) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.DELETE_PACKAGES, null);
-        Preconditions.checkNotNull(versionedPackage);
-        Preconditions.checkNotNull(observer);
-        final String packageName = versionedPackage.getPackageName();
-        final long versionCode = versionedPackage.getLongVersionCode();
-
-        int installedForUsersCount = 0;
-        synchronized (mLock) {
-            // Normalize package name to handle renamed packages and static libs
-            final String internalPkgName = resolveInternalPackageNameLPr(packageName, versionCode);
-            final PackageSetting ps = mSettings.getPackageLPr(internalPkgName);
-            if (ps != null) {
-                int[] installedUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
-                installedForUsersCount = installedUsers.length;
-            }
-        }
-
-        if (installedForUsersCount > 1) {
-            mDeletePackageHelper.deletePackageVersionedInternal(
-                    versionedPackage, observer, userId, 0, true);
-        } else {
-            try {
-                observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_INTERNAL_ERROR,
-                        null);
-            } catch (RemoteException re) {
-            }
-        }
+        mDeletePackageHelper.deleteExistingPackageAsUser(
+                versionedPackage, observer, userId);
     }
 
     @Override
@@ -9257,22 +6753,6 @@
         return mDevicePolicyManager;
     }
 
-    boolean shouldKeepUninstalledPackageLPr(String packageName) {
-        return mKeepUninstalledPackages != null && mKeepUninstalledPackages.contains(packageName);
-    }
-
-    private static @Nullable ScanPartition resolveApexToScanPartition(
-            ApexManager.ActiveApexInfo apexInfo) {
-        for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
-            ScanPartition sp = SYSTEM_PARTITIONS.get(i);
-            if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
-                    sp.getFolder().getAbsolutePath())) {
-                return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
-            }
-        }
-        return null;
-    }
-
     @Override
     public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall,
             int userId) {
@@ -9308,7 +6788,8 @@
 
     @Override
     public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) {
-        enforceSystemOrRoot("setRequiredForSystemUser can only be run by the system or root");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "setRequiredForSystemUser can only be run by the system or root");
         synchronized (mLock) {
             PackageSetting ps = mSettings.getPackageLPr(packageName);
             if (ps == null) {
@@ -9327,7 +6808,8 @@
 
     @Override
     public void clearApplicationProfileData(String packageName) {
-        enforceSystemOrRoot("Only the system can clear all profile data");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system can clear all profile data");
 
         final AndroidPackage pkg;
         synchronized (mLock) {
@@ -9449,10 +6931,6 @@
         return true;
     }
 
-    private void resetNetworkPolicies(int userId) {
-        mInjector.getLocalService(NetworkPolicyManagerInternal.class).resetUserState(userId);
-    }
-
     /**
      * Remove entries from the keystore daemon. Will only remove it if the
      * {@code appId} is valid.
@@ -9552,7 +7030,7 @@
     }
 
     @GuardedBy("mLock")
-    private int getUidTargetSdkVersionLockedLPr(int uid) {
+    int getUidTargetSdkVersionLockedLPr(int uid) {
         final int appId = UserHandle.getAppId(uid);
         final Object obj = mSettings.getSettingLPr(appId);
         if (obj instanceof SharedUserSetting) {
@@ -9588,57 +7066,11 @@
     @Override
     public void addPreferredActivity(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity, int userId, boolean removeExisting) {
-        addPreferredActivity(new WatchedIntentFilter(filter), match, set, activity, true, userId,
+        mPreferredActivityHelper.addPreferredActivity(
+                new WatchedIntentFilter(filter), match, set, activity, true, userId,
                 "Adding preferred", removeExisting);
     }
 
-    /**
-     * Variant that takes a {@link WatchedIntentFilter}
-     */
-    public void addPreferredActivity(WatchedIntentFilter filter, int match,
-            ComponentName[] set, ComponentName activity, boolean always, int userId,
-            String opname, boolean removeExisting) {
-        // writer
-        int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
-                false /* checkShell */, "add preferred activity");
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
-                != PackageManager.PERMISSION_GRANTED) {
-            synchronized (mLock) {
-                if (getUidTargetSdkVersionLockedLPr(callingUid)
-                        < Build.VERSION_CODES.FROYO) {
-                    Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
-                            + callingUid);
-                    return;
-                }
-            }
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
-        }
-        if (filter.countActions() == 0) {
-            Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
-            return;
-        }
-        if (DEBUG_PREFERRED) {
-            Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user "
-                    + userId + ":");
-            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
-        }
-        synchronized (mLock) {
-            final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
-            final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
-            if (removeExisting && existing != null) {
-                Settings.removeFilters(pir, filter, existing);
-            }
-            pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
-            scheduleWritePackageRestrictionsLocked(userId);
-        }
-        if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(userId))) {
-            postPreferredActivityChangedBroadcast(userId);
-        }
-    }
-
     void postPreferredActivityChangedBroadcast(int userId) {
         mHandler.post(() -> mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId));
     }
@@ -9646,139 +7078,15 @@
     @Override
     public void replacePreferredActivity(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity, int userId) {
-        replacePreferredActivity(new WatchedIntentFilter(filter), match,
+        mPreferredActivityHelper.replacePreferredActivity(new WatchedIntentFilter(filter), match,
                                  set, activity, userId);
     }
 
-    /**
-     * Variant that takes a {@link WatchedIntentFilter}
-     */
-    public void replacePreferredActivity(WatchedIntentFilter filter, int match,
-            ComponentName[] set, ComponentName activity, int userId) {
-        if (filter.countActions() != 1) {
-            throw new IllegalArgumentException(
-                    "replacePreferredActivity expects filter to have only 1 action.");
-        }
-        if (filter.countDataAuthorities() != 0
-                || filter.countDataPaths() != 0
-                || filter.countDataSchemes() > 1
-                || filter.countDataTypes() != 0) {
-            throw new IllegalArgumentException(
-                    "replacePreferredActivity expects filter to have no data authorities, " +
-                    "paths, or types; and at most one scheme.");
-        }
-
-        final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
-                false /* checkShell */, "replace preferred activity");
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
-                != PackageManager.PERMISSION_GRANTED) {
-            synchronized (mLock) {
-                if (getUidTargetSdkVersionLockedLPr(callingUid)
-                        < Build.VERSION_CODES.FROYO) {
-                    Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
-                            + Binder.getCallingUid());
-                    return;
-                }
-            }
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
-        }
-
-        synchronized (mLock) {
-            final PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
-            if (pir != null) {
-                // Get all of the existing entries that exactly match this filter.
-                final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
-                if (existing != null && existing.size() == 1) {
-                    final PreferredActivity cur = existing.get(0);
-                    if (DEBUG_PREFERRED) {
-                        Slog.i(TAG, "Checking replace of preferred:");
-                        filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
-                        if (!cur.mPref.mAlways) {
-                            Slog.i(TAG, "  -- CUR; not mAlways!");
-                        } else {
-                            Slog.i(TAG, "  -- CUR: mMatch=" + cur.mPref.mMatch);
-                            Slog.i(TAG, "  -- CUR: mSet="
-                                    + Arrays.toString(cur.mPref.mSetComponents));
-                            Slog.i(TAG, "  -- CUR: mComponent=" + cur.mPref.mShortComponent);
-                            Slog.i(TAG, "  -- NEW: mMatch="
-                                    + (match&IntentFilter.MATCH_CATEGORY_MASK));
-                            Slog.i(TAG, "  -- CUR: mSet=" + Arrays.toString(set));
-                            Slog.i(TAG, "  -- CUR: mComponent=" + activity.flattenToShortString());
-                        }
-                    }
-                    if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity)
-                            && cur.mPref.mMatch == (match&IntentFilter.MATCH_CATEGORY_MASK)
-                            && cur.mPref.sameSet(set)) {
-                        // Setting the preferred activity to what it happens to be already
-                        if (DEBUG_PREFERRED) {
-                            Slog.i(TAG, "Replacing with same preferred activity "
-                                    + cur.mPref.mShortComponent + " for user "
-                                    + userId + ":");
-                            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
-                        }
-                        return;
-                    }
-                }
-                if (existing != null) {
-                    Settings.removeFilters(pir, filter, existing);
-                }
-            }
-        }
-        addPreferredActivity(filter, match, set, activity, true, userId,
-                "Replacing preferred", false);
-    }
-
     @Override
     public void clearPackagePreferredActivities(String packageName) {
-        final int callingUid = Binder.getCallingUid();
-        if (getInstantAppPackageName(callingUid) != null) {
-            return;
-        }
-        // writer
-        synchronized (mLock) {
-            AndroidPackage pkg = mPackages.get(packageName);
-            if (pkg == null || !isCallerSameApp(packageName, callingUid)) {
-                if (mContext.checkCallingOrSelfPermission(
-                        android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
-                        != PackageManager.PERMISSION_GRANTED) {
-                    if (getUidTargetSdkVersionLockedLPr(callingUid)
-                            < Build.VERSION_CODES.FROYO) {
-                        Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid "
-                                + callingUid);
-                        return;
-                    }
-                    mContext.enforceCallingOrSelfPermission(
-                            android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
-                }
-            }
-            final PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (ps != null
-                    && shouldFilterApplicationLocked(
-                            ps, callingUid, UserHandle.getUserId(callingUid))) {
-                return;
-            }
-        }
-        int callingUserId = UserHandle.getCallingUserId();
-        clearPackagePreferredActivities(packageName, callingUserId);
+        mPreferredActivityHelper.clearPackagePreferredActivities(packageName);
     }
 
-    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
-    void clearPackagePreferredActivities(String packageName, int userId) {
-        final SparseBooleanArray changedUsers = new SparseBooleanArray();
-        synchronized (mLock) {
-            clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
-        }
-        if (changedUsers.size() > 0) {
-            updateDefaultHomeNotLocked(changedUsers);
-            postPreferredActivityChangedBroadcast(userId);
-            synchronized (mLock) {
-                scheduleWritePackageRestrictionsLocked(userId);
-            }
-        }
-    }
 
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
     @GuardedBy("mLock")
@@ -9795,185 +7103,31 @@
 
         // Persistent preferred activity might have came into effect due to this
         // install.
-        updateDefaultHomeNotLocked(userId);
+        mPreferredActivityHelper.updateDefaultHomeNotLocked(userId);
     }
 
     @Override
     public void resetApplicationPreferences(int userId) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
-        final long identity = Binder.clearCallingIdentity();
-        // writer
-        try {
-            final SparseBooleanArray changedUsers = new SparseBooleanArray();
-            synchronized (mLock) {
-                clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
-            }
-            if (changedUsers.size() > 0) {
-                postPreferredActivityChangedBroadcast(userId);
-            }
-            synchronized (mLock) {
-                mSettings.applyDefaultPreferredAppsLPw(userId);
-                mDomainVerificationManager.clearUser(userId);
-                final int numPackages = mPackages.size();
-                for (int i = 0; i < numPackages; i++) {
-                    final AndroidPackage pkg = mPackages.valueAt(i);
-                    mPermissionManager.resetRuntimePermissions(pkg, userId);
-                }
-            }
-            updateDefaultHomeNotLocked(userId);
-            resetNetworkPolicies(userId);
-            synchronized (mLock) {
-                scheduleWritePackageRestrictionsLocked(userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        mPreferredActivityHelper.resetApplicationPreferences(userId);
     }
 
     @Override
     public int getPreferredActivities(List<IntentFilter> outFilters,
             List<ComponentName> outActivities, String packageName) {
-        List<WatchedIntentFilter> temp =
-                WatchedIntentFilter.toWatchedIntentFilterList(outFilters);
-        final int result = getPreferredActivitiesInternal(
-                temp, outActivities, packageName);
-        outFilters.clear();
-        for (int i = 0; i < temp.size(); i++) {
-            outFilters.add(temp.get(i).getIntentFilter());
-        }
-        return result;
-    }
-
-    /**
-     * Variant that takes a {@link WatchedIntentFilter}
-     */
-    public int getPreferredActivitiesInternal(List<WatchedIntentFilter> outFilters,
-            List<ComponentName> outActivities, String packageName) {
-        final int callingUid = Binder.getCallingUid();
-        if (getInstantAppPackageName(callingUid) != null) {
-            return 0;
-        }
-        int num = 0;
-        final int userId = UserHandle.getCallingUserId();
-        // reader
-        synchronized (mLock) {
-            PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
-            if (pir != null) {
-                final Iterator<PreferredActivity> it = pir.filterIterator();
-                while (it.hasNext()) {
-                    final PreferredActivity pa = it.next();
-                    final String prefPackageName = pa.mPref.mComponent.getPackageName();
-                    if (packageName == null
-                            || (prefPackageName.equals(packageName) && pa.mPref.mAlways)) {
-                        if (shouldFilterApplicationLocked(
-                                mSettings.getPackageLPr(prefPackageName), callingUid, userId)) {
-                            continue;
-                        }
-                        if (outFilters != null) {
-                            outFilters.add(new WatchedIntentFilter(pa.getIntentFilter()));
-                        }
-                        if (outActivities != null) {
-                            outActivities.add(pa.mPref.mComponent);
-                        }
-                    }
-                }
-            }
-        }
-
-        return num;
+        return mPreferredActivityHelper.getPreferredActivities(outFilters, outActivities,
+                packageName);
     }
 
     @Override
     public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
             int userId) {
-        addPersistentPreferredActivity(new WatchedIntentFilter(filter), activity, userId);
-    }
-
-    /**
-     * Variant that takes a {@link WatchedIntentFilter}
-     */
-    public void addPersistentPreferredActivity(WatchedIntentFilter filter, ComponentName activity,
-            int userId) {
-        int callingUid = Binder.getCallingUid();
-        if (callingUid != Process.SYSTEM_UID) {
-            throw new SecurityException(
-                    "addPersistentPreferredActivity can only be run by the system");
-        }
-        if (filter.countActions() == 0) {
-            Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
-            return;
-        }
-        if (DEBUG_PREFERRED) {
-            Slog.i(TAG, "Adding persistent preferred activity " + activity
-                    + " for user " + userId + ":");
-            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
-        }
-        synchronized (mLock) {
-            mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
-                    new PersistentPreferredActivity(filter, activity, true));
-            scheduleWritePackageRestrictionsLocked(userId);
-        }
-        if (isHomeFilter(filter)) {
-            updateDefaultHomeNotLocked(userId);
-        }
-        postPreferredActivityChangedBroadcast(userId);
+        mPreferredActivityHelper.addPersistentPreferredActivity(new WatchedIntentFilter(filter),
+                activity, userId);
     }
 
     @Override
     public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
-        int callingUid = Binder.getCallingUid();
-        if (callingUid != Process.SYSTEM_UID) {
-            throw new SecurityException(
-                    "clearPackagePersistentPreferredActivities can only be run by the system");
-        }
-        boolean changed = false;
-        synchronized (mLock) {
-            changed = mSettings.clearPackagePersistentPreferredActivities(packageName, userId);
-        }
-        if (changed) {
-            updateDefaultHomeNotLocked(userId);
-            postPreferredActivityChangedBroadcast(userId);
-            synchronized (mLock) {
-                scheduleWritePackageRestrictionsLocked(userId);
-            }
-        }
-    }
-
-    /**
-     * Common machinery for picking apart a restored XML blob and passing
-     * it to a caller-supplied functor to be applied to the running system.
-     */
-    private void restoreFromXml(TypedXmlPullParser parser, int userId,
-            String expectedStartTag, BlobXmlRestorer functor)
-            throws IOException, XmlPullParserException {
-        int type;
-        while ((type = parser.next()) != XmlPullParser.START_TAG
-                && type != XmlPullParser.END_DOCUMENT) {
-        }
-        if (type != XmlPullParser.START_TAG) {
-            // oops didn't find a start tag?!
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Didn't find start tag during restore");
-            }
-            return;
-        }
-        // this is supposed to be TAG_PREFERRED_BACKUP
-        if (!expectedStartTag.equals(parser.getName())) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Found unexpected tag " + parser.getName());
-            }
-            return;
-        }
-
-        // skip interfering stuff, then we're aligned with the backing implementation
-        while ((type = parser.next()) == XmlPullParser.TEXT) { }
-        functor.apply(parser, userId);
-    }
-
-    private interface BlobXmlRestorer {
-        void apply(TypedXmlPullParser parser, int userId)
-                throws IOException, XmlPullParserException;
+        mPreferredActivityHelper.clearPackagePersistentPreferredActivities(packageName, userId);
     }
 
     /**
@@ -9983,55 +7137,12 @@
      */
     @Override
     public byte[] getPreferredActivityBackup(int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call getPreferredActivityBackup()");
-        }
-
-        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
-        try {
-            final TypedXmlSerializer serializer = Xml.newFastSerializer();
-            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TAG_PREFERRED_BACKUP);
-
-            synchronized (mLock) {
-                mSettings.writePreferredActivitiesLPr(serializer, userId, true);
-            }
-
-            serializer.endTag(null, TAG_PREFERRED_BACKUP);
-            serializer.endDocument();
-            serializer.flush();
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Unable to write preferred activities for backup", e);
-            }
-            return null;
-        }
-
-        return dataStream.toByteArray();
+        return mPreferredActivityHelper.getPreferredActivityBackup(userId);
     }
 
     @Override
     public void restorePreferredActivities(byte[] backup, int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call restorePreferredActivities()");
-        }
-
-        try {
-            final TypedXmlPullParser parser = Xml.newFastPullParser();
-            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
-            restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
-                    (readParser, readUserId) -> {
-                        synchronized (mLock) {
-                            mSettings.readPreferredActivitiesLPw(readParser, readUserId);
-                        }
-                        updateDefaultHomeNotLocked(readUserId);
-                    });
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
-            }
-        }
+        mPreferredActivityHelper.restorePreferredActivities(backup, userId);
     }
 
     /**
@@ -10041,59 +7152,12 @@
      */
     @Override
     public byte[] getDefaultAppsBackup(int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call getDefaultAppsBackup()");
-        }
-
-        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
-        try {
-            final TypedXmlSerializer serializer = Xml.newFastSerializer();
-            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TAG_DEFAULT_APPS);
-
-            synchronized (mLock) {
-                mSettings.writeDefaultAppsLPr(serializer, userId);
-            }
-
-            serializer.endTag(null, TAG_DEFAULT_APPS);
-            serializer.endDocument();
-            serializer.flush();
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Unable to write default apps for backup", e);
-            }
-            return null;
-        }
-
-        return dataStream.toByteArray();
+        return mPreferredActivityHelper.getDefaultAppsBackup(userId);
     }
 
     @Override
     public void restoreDefaultApps(byte[] backup, int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call restoreDefaultApps()");
-        }
-
-        try {
-            final TypedXmlPullParser parser = Xml.newFastPullParser();
-            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
-            restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
-                    (parser1, userId1) -> {
-                        final String defaultBrowser;
-                        synchronized (mLock) {
-                            mSettings.readDefaultAppsLPw(parser1, userId1);
-                            defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1);
-                        }
-                        if (defaultBrowser != null) {
-                            mDefaultAppProvider.setDefaultBrowser(defaultBrowser, false, userId1);
-                        }
-                    });
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Exception restoring default apps: " + e.getMessage());
-            }
-        }
+        mPreferredActivityHelper.restoreDefaultApps(backup, userId);
     }
 
     @Override
@@ -10248,114 +7312,19 @@
         return mComputer.getDefaultHomeActivity(userId);
     }
 
-    private Intent getHomeIntent() {
+    Intent getHomeIntent() {
         return mComputer.getHomeIntent();
     }
 
-    private WatchedIntentFilter getHomeFilter() {
-        WatchedIntentFilter filter = new WatchedIntentFilter(Intent.ACTION_MAIN);
-        filter.addCategory(Intent.CATEGORY_HOME);
-        filter.addCategory(Intent.CATEGORY_DEFAULT);
-        return filter;
-    }
-
-    private boolean isHomeFilter(@NonNull WatchedIntentFilter filter) {
-        return filter.hasAction(Intent.ACTION_MAIN) && filter.hasCategory(Intent.CATEGORY_HOME)
-                && filter.hasCategory(CATEGORY_DEFAULT);
-    }
-
     ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
             int userId) {
         return mComputer.getHomeActivitiesAsUser(allHomeCandidates,
                 userId);
     }
 
-    /** <b>must not hold {@link #mLock}</b> */
-    void updateDefaultHomeNotLocked(SparseBooleanArray userIds) {
-        if (Thread.holdsLock(mLock)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
-                    + " is holding mLock", new Throwable());
-        }
-        for (int i = userIds.size() - 1; i >= 0; --i) {
-            final int userId = userIds.keyAt(i);
-            updateDefaultHomeNotLocked(userId);
-        }
-    }
-
-    /**
-     * <b>must not hold {@link #mLock}</b>
-     *
-     * @return Whether the ACTION_PREFERRED_ACTIVITY_CHANGED broadcast has been scheduled.
-     */
-    private boolean updateDefaultHomeNotLocked(int userId) {
-        if (Thread.holdsLock(mLock)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
-                    + " is holding mLock", new Throwable());
-        }
-        if (!mSystemReady) {
-            // We might get called before system is ready because of package changes etc, but
-            // finding preferred activity depends on settings provider, so we ignore the update
-            // before that.
-            return false;
-        }
-        final Intent intent = getHomeIntent();
-        final List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
-                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
-        final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
-                intent, null, 0, resolveInfos, true, false, false, userId);
-        final String packageName = preferredResolveInfo != null
-                && preferredResolveInfo.activityInfo != null
-                ? preferredResolveInfo.activityInfo.packageName : null;
-        final String currentPackageName = mDefaultAppProvider.getDefaultHome(userId);
-        if (TextUtils.equals(currentPackageName, packageName)) {
-            return false;
-        }
-        final String[] callingPackages = getPackagesForUid(Binder.getCallingUid());
-        if (callingPackages != null && ArrayUtils.contains(callingPackages,
-                mRequiredPermissionControllerPackage)) {
-            // PermissionController manages default home directly.
-            return false;
-        }
-
-        if (packageName == null) {
-            // Keep the default home package in RoleManager.
-            return false;
-        }
-        return mDefaultAppProvider.setDefaultHome(packageName, userId, mContext.getMainExecutor(),
-                successful -> {
-                    if (successful) {
-                        postPreferredActivityChangedBroadcast(userId);
-                    }
-                });
-    }
-
     @Override
     public void setHomeActivity(ComponentName comp, int userId) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return;
-        }
-        ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
-        getHomeActivitiesAsUser(homeActivities, userId);
-
-        boolean found = false;
-
-        final int size = homeActivities.size();
-        final ComponentName[] set = new ComponentName[size];
-        for (int i = 0; i < size; i++) {
-            final ResolveInfo candidate = homeActivities.get(i);
-            final ActivityInfo info = candidate.activityInfo;
-            final ComponentName activityName = new ComponentName(info.packageName, info.name);
-            set[i] = activityName;
-            if (!found && activityName.equals(comp)) {
-                found = true;
-            }
-        }
-        if (!found) {
-            throw new IllegalArgumentException("Component " + comp + " cannot be home on user "
-                    + userId);
-        }
-        replacePreferredActivity(getHomeFilter(), IntentFilter.MATCH_CATEGORY_EMPTY,
-                set, comp, userId);
+        mPreferredActivityHelper.setHomeActivity(comp, userId);
     }
 
     private @Nullable String getSetupWizardPackageNameImpl() {
@@ -10838,8 +7807,7 @@
             if (isSystemStub
                     && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                     || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
-                if (!mInitAndSystemPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting,
-                        mDefParseFlags, mDirsToScanAsSystem)) {
+                if (!mInitAndSystemPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
                     Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
                             + "commpressed package " + setting.getPackageName());
                     updateAllowed[i] = false;
@@ -11295,7 +8263,8 @@
 
     @Override
     public void enterSafeMode() {
-        enforceSystemOrRoot("Only the system can request entering safe mode");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system can request entering safe mode");
 
         if (!mSystemReady) {
             mSafeMode = true;
@@ -11304,7 +8273,8 @@
 
     @Override
     public void systemReady() {
-        enforceSystemOrRoot("Only the system can claim the system is ready");
+        PackageManagerServiceUtils.enforceSystemOrRoot(
+                "Only the system can claim the system is ready");
 
         final ContentResolver resolver = mContext.getContentResolver();
         if (mReleaseOnSystemReady != null) {
@@ -11463,6 +8433,9 @@
         mBackgroundDexOptService.systemReady();
     }
 
+    /**
+     * Used by SystemServer
+     */
     public void waitForAppDataPrepared() {
         if (mPrepareAppDataFuture == null) {
             return;
@@ -11495,632 +8468,22 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+        new DumpHelper(this).doDump(fd, pw, args);
+    }
 
-        DumpState dumpState = new DumpState();
-        ArraySet<String> permissionNames = null;
-
-        int opti = 0;
-        while (opti < args.length) {
-            String opt = args[opti];
-            if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
-                break;
-            }
-            opti++;
-
-            if ("-a".equals(opt)) {
-                // Right now we only know how to print all.
-            } else if ("-h".equals(opt)) {
-                pw.println("Package manager dump options:");
-                pw.println("  [-h] [-f] [--checkin] [--all-components] [cmd] ...");
-                pw.println("    --checkin: dump for a checkin");
-                pw.println("    -f: print details of intent filters");
-                pw.println("    -h: print this help");
-                pw.println("    --all-components: include all component names in package dump");
-                pw.println("  cmd may be one of:");
-                pw.println("    apex: list active APEXes and APEX session state");
-                pw.println("    l[ibraries]: list known shared libraries");
-                pw.println("    f[eatures]: list device features");
-                pw.println("    k[eysets]: print known keysets");
-                pw.println("    r[esolvers] [activity|service|receiver|content]: dump intent resolvers");
-                pw.println("    perm[issions]: dump permissions");
-                pw.println("    permission [name ...]: dump declaration and use of given permission");
-                pw.println("    pref[erred]: print preferred package settings");
-                pw.println("    preferred-xml [--full]: print preferred package settings as xml");
-                pw.println("    prov[iders]: dump content providers");
-                pw.println("    p[ackages]: dump installed packages");
-                pw.println("    q[ueries]: dump app queryability calculations");
-                pw.println("    s[hared-users]: dump shared user IDs");
-                pw.println("    m[essages]: print collected runtime messages");
-                pw.println("    v[erifiers]: print package verifier info");
-                pw.println("    d[omain-preferred-apps]: print domains preferred apps");
-                pw.println("    i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
-                pw.println("    t[imeouts]: print read timeouts for known digesters");
-                pw.println("    version: print database version info");
-                pw.println("    write: write current settings now");
-                pw.println("    installs: details about install sessions");
-                pw.println("    check-permission <permission> <package> [<user>]: does pkg hold perm?");
-                pw.println("    dexopt: dump dexopt state");
-                pw.println("    compiler-stats: dump compiler statistics");
-                pw.println("    service-permissions: dump permissions required by services");
-                pw.println("    snapshot: dump snapshot statistics");
-                pw.println("    protected-broadcasts: print list of protected broadcast actions");
-                pw.println("    known-packages: dump known packages");
-                pw.println("    <package.name>: info about given package");
-                return;
-            } else if ("--checkin".equals(opt)) {
-                dumpState.setCheckIn(true);
-            } else if ("--all-components".equals(opt)) {
-                dumpState.setOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS);
-            } else if ("-f".equals(opt)) {
-                dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
-            } else if ("--proto".equals(opt)) {
-                dumpProto(fd);
-                return;
-            } else {
-                pw.println("Unknown argument: " + opt + "; use -h for help");
-            }
-        }
-
-        // Is the caller requesting to dump a particular piece of data?
-        if (opti < args.length) {
-            String cmd = args[opti];
-            opti++;
-            // Is this a package name?
-            if ("android".equals(cmd) || cmd.contains(".")) {
-                dumpState.setTargetPackageName(cmd);
-                // When dumping a single package, we always dump all of its
-                // filter information since the amount of data will be reasonable.
-                dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
-            } else if ("check-permission".equals(cmd)) {
-                if (opti >= args.length) {
-                    pw.println("Error: check-permission missing permission argument");
-                    return;
-                }
-                String perm = args[opti];
-                opti++;
-                if (opti >= args.length) {
-                    pw.println("Error: check-permission missing package argument");
-                    return;
-                }
-
-                String pkg = args[opti];
-                opti++;
-                int user = UserHandle.getUserId(Binder.getCallingUid());
-                if (opti < args.length) {
-                    try {
-                        user = Integer.parseInt(args[opti]);
-                    } catch (NumberFormatException e) {
-                        pw.println("Error: check-permission user argument is not a number: "
-                                + args[opti]);
-                        return;
-                    }
-                }
-
-                // Normalize package name to handle renamed packages and static libs
-                pkg = resolveInternalPackageNameLPr(pkg, PackageManager.VERSION_CODE_HIGHEST);
-
-                pw.println(checkPermission(perm, pkg, user));
-                return;
-            } else if ("l".equals(cmd) || "libraries".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_LIBS);
-            } else if ("f".equals(cmd) || "features".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_FEATURES);
-            } else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
-                if (opti >= args.length) {
-                    dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS
-                            | DumpState.DUMP_SERVICE_RESOLVERS
-                            | DumpState.DUMP_RECEIVER_RESOLVERS
-                            | DumpState.DUMP_CONTENT_RESOLVERS);
-                } else {
-                    while (opti < args.length) {
-                        String name = args[opti];
-                        if ("a".equals(name) || "activity".equals(name)) {
-                            dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS);
-                        } else if ("s".equals(name) || "service".equals(name)) {
-                            dumpState.setDump(DumpState.DUMP_SERVICE_RESOLVERS);
-                        } else if ("r".equals(name) || "receiver".equals(name)) {
-                            dumpState.setDump(DumpState.DUMP_RECEIVER_RESOLVERS);
-                        } else if ("c".equals(name) || "content".equals(name)) {
-                            dumpState.setDump(DumpState.DUMP_CONTENT_RESOLVERS);
-                        } else {
-                            pw.println("Error: unknown resolver table type: " + name);
-                            return;
-                        }
-                        opti++;
-                    }
-                }
-            } else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_PERMISSIONS);
-            } else if ("permission".equals(cmd)) {
-                if (opti >= args.length) {
-                    pw.println("Error: permission requires permission name");
-                    return;
-                }
-                permissionNames = new ArraySet<>();
-                while (opti < args.length) {
-                    permissionNames.add(args[opti]);
-                    opti++;
-                }
-                dumpState.setDump(DumpState.DUMP_PERMISSIONS
-                        | DumpState.DUMP_PACKAGES | DumpState.DUMP_SHARED_USERS);
-            } else if ("pref".equals(cmd) || "preferred".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_PREFERRED);
-            } else if ("preferred-xml".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_PREFERRED_XML);
-                if (opti < args.length && "--full".equals(args[opti])) {
-                    dumpState.setFullPreferred(true);
-                    opti++;
-                }
-            } else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_DOMAIN_PREFERRED);
-            } else if ("p".equals(cmd) || "packages".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_PACKAGES);
-            } else if ("q".equals(cmd) || "queries".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_QUERIES);
-            } else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_SHARED_USERS);
-                if (opti < args.length && "noperm".equals(args[opti])) {
-                    dumpState.setOptionEnabled(DumpState.OPTION_SKIP_PERMISSIONS);
-                }
-            } else if ("prov".equals(cmd) || "providers".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_PROVIDERS);
-            } else if ("m".equals(cmd) || "messages".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_MESSAGES);
-            } else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_VERIFIERS);
-            } else if ("dv".equals(cmd) || "domain-verifier".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_DOMAIN_VERIFIER);
-            } else if ("version".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_VERSION);
-            } else if ("k".equals(cmd) || "keysets".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_KEYSETS);
-            } else if ("installs".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_INSTALLS);
-            } else if ("frozen".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_FROZEN);
-            } else if ("volumes".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_VOLUMES);
-            } else if ("dexopt".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_DEXOPT);
-            } else if ("compiler-stats".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
-            } else if ("changes".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_CHANGES);
-            } else if ("service-permissions".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
-            } else if ("known-packages".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_KNOWN_PACKAGES);
-            } else if ("t".equals(cmd) || "timeouts".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS);
-            } else if ("snapshot".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_SNAPSHOT_STATISTICS);
-                if (opti < args.length) {
-                    if ("--full".equals(args[opti])) {
-                        dumpState.setBrief(false);
-                        opti++;
-                    } else if ("--brief".equals(args[opti])) {
-                        dumpState.setBrief(true);
-                        opti++;
-                    }
-                }
-            } else if ("protected-broadcasts".equals(cmd)) {
-                dumpState.setDump(DumpState.DUMP_PROTECTED_BROADCASTS);
-            } else if ("write".equals(cmd)) {
-                synchronized (mLock) {
-                    writeSettingsLPrTEMP();
-                    pw.println("Settings written.");
-                    return;
+    void dumpSnapshotStats(PrintWriter pw, boolean isBrief) {
+        if (!mSnapshotEnabled) {
+            pw.println("  Snapshots disabled");
+        } else {
+            int hits = 0;
+            int level = sSnapshotCorked.get();
+            synchronized (mSnapshotLock) {
+                if (mSnapshotComputer != null) {
+                    hits = mSnapshotComputer.getUsed();
                 }
             }
-        }
-
-        final String packageName = dumpState.getTargetPackageName();
-        final boolean checkin = dumpState.isCheckIn();
-
-        // Return if the package doesn't exist.
-        if (packageName != null
-                && getPackageSetting(packageName) == null
-                && !mApexManager.isApexPackage(packageName)) {
-            pw.println("Unable to find package: " + packageName);
-            return;
-        }
-
-        if (checkin) {
-            pw.println("vers,1");
-        }
-
-        // reader
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_VERSION)
-                && packageName == null) {
-            dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES)
-                && packageName == null) {
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
-            ipw.println("Known Packages:");
-            ipw.increaseIndent();
-            for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
-                final String knownPackage = PackageManagerInternal.knownPackageToString(i);
-                ipw.print(knownPackage);
-                ipw.println(":");
-                final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
-                        UserHandle.USER_SYSTEM);
-                ipw.increaseIndent();
-                if (ArrayUtils.isEmpty(pkgNames)) {
-                    ipw.println("none");
-                } else {
-                    for (String name : pkgNames) {
-                        ipw.println(name);
-                    }
-                }
-                ipw.decreaseIndent();
-            }
-            ipw.decreaseIndent();
-        }
-
-        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
-                && packageName == null) {
-            final String requiredVerifierPackage = mRequiredVerifierPackage;
-            if (!checkin) {
-                if (dumpState.onTitlePrinted()) {
-                    pw.println();
-                }
-                pw.println("Verifiers:");
-                pw.print("  Required: ");
-                pw.print(requiredVerifierPackage);
-                pw.print(" (uid=");
-                pw.print(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
-                        UserHandle.USER_SYSTEM));
-                pw.println(")");
-            } else if (requiredVerifierPackage != null) {
-                pw.print("vrfy,"); pw.print(requiredVerifierPackage);
-                pw.print(",");
-                pw.println(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
-                        UserHandle.USER_SYSTEM));
-            }
-        }
-
-        if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER)
-                && packageName == null) {
-            final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
-            final ComponentName verifierComponent = proxy.getComponentName();
-            if (verifierComponent != null) {
-                String verifierPackageName = verifierComponent.getPackageName();
-                if (!checkin) {
-                    if (dumpState.onTitlePrinted()) {
-                        pw.println();
-                    }
-                    pw.println("Domain Verifier:");
-                    pw.print("  Using: ");
-                    pw.print(verifierPackageName);
-                    pw.print(" (uid=");
-                    pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
-                            UserHandle.USER_SYSTEM));
-                    pw.println(")");
-                } else if (verifierPackageName != null) {
-                    pw.print("dv,"); pw.print(verifierPackageName);
-                    pw.print(",");
-                    pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
-                            UserHandle.USER_SYSTEM));
-                }
-            } else {
-                pw.println();
-                pw.println("No Domain Verifier available!");
-            }
-        }
-
-        if (dumpState.isDumping(DumpState.DUMP_LIBS)
-                && packageName == null) {
-            dump(DumpState.DUMP_LIBS, fd, pw, dumpState);
-        }
-
-        if (dumpState.isDumping(DumpState.DUMP_FEATURES)
-                && packageName == null) {
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            if (!checkin) {
-                pw.println("Features:");
-            }
-
-            synchronized (mAvailableFeatures) {
-                for (FeatureInfo feat : mAvailableFeatures.values()) {
-                    if (!checkin) {
-                        pw.print("  ");
-                        pw.print(feat.name);
-                        if (feat.version > 0) {
-                            pw.print(" version=");
-                            pw.print(feat.version);
-                        }
-                        pw.println();
-                    } else {
-                        pw.print("feat,");
-                        pw.print(feat.name);
-                        pw.print(",");
-                        pw.println(feat.version);
-                    }
-                }
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
-            synchronized (mLock) {
-                mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
-            }
-        }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
-            synchronized (mLock) {
-                mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
-            }
-        }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
-            synchronized (mLock) {
-                mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
-            }
-        }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
-            synchronized (mLock) {
-                mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
-            dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)
-                && packageName == null) {
-            dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
-            dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
-            synchronized (mLock) {
-                mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
-            synchronized (mLock) {
-                mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
-            synchronized (mLock) {
-                mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
-            }
-        }
-
-        if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
-            // This cannot be moved to ComputerEngine since some variables of the collections
-            // in PackageUserState such as suspendParams, disabledComponents and enabledComponents
-            // do not have a copy.
-            synchronized (mLock) {
-                mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin);
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_QUERIES)) {
-            dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
-        }
-
-        if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
-            // This cannot be moved to ComputerEngine since the set of packages in the
-            // SharedUserSetting do not have a copy.
-            synchronized (mLock) {
-                mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_CHANGES)
-                && packageName == null) {
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            pw.println("Package Changes:");
-            synchronized (mLock) {
-                pw.print("  Sequence number="); pw.println(mChangedPackagesSequenceNumber);
-                final int K = mChangedPackages.size();
-                for (int i = 0; i < K; i++) {
-                    final SparseArray<String> changes = mChangedPackages.valueAt(i);
-                    pw.print("  User "); pw.print(mChangedPackages.keyAt(i)); pw.println(":");
-                    final int N = changes.size();
-                    if (N == 0) {
-                        pw.print("    "); pw.println("No packages changed");
-                    } else {
-                        for (int j = 0; j < N; j++) {
-                            final String pkgName = changes.valueAt(j);
-                            final int sequenceNumber = changes.keyAt(j);
-                            pw.print("    ");
-                            pw.print("seq=");
-                            pw.print(sequenceNumber);
-                            pw.print(", package=");
-                            pw.println(pkgName);
-                        }
-                    }
-                }
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_FROZEN)
-                && packageName == null) {
-            // XXX should handle packageName != null by dumping only install data that
-            // the given package is involved with.
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
-            ipw.println();
-            ipw.println("Frozen packages:");
-            ipw.increaseIndent();
-            synchronized (mLock) {
-                if (mFrozenPackages.size() == 0) {
-                    ipw.println("(none)");
-                } else {
-                    for (int i = 0; i < mFrozenPackages.size(); i++) {
-                        ipw.println(mFrozenPackages.valueAt(i));
-                    }
-                }
-            }
-            ipw.decreaseIndent();
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_VOLUMES)
-                && packageName == null) {
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
-            ipw.println();
-            ipw.println("Loaded volumes:");
-            ipw.increaseIndent();
-            synchronized (mLoadedVolumes) {
-                if (mLoadedVolumes.size() == 0) {
-                    ipw.println("(none)");
-                } else {
-                    for (int i = 0; i < mLoadedVolumes.size(); i++) {
-                        ipw.println(mLoadedVolumes.valueAt(i));
-                    }
-                }
-            }
-            ipw.decreaseIndent();
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
-                && packageName == null) {
-            synchronized (mLock) {
-                mComponentResolver.dumpServicePermissions(pw, dumpState);
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
-            dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
-            dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
-        }
-
-        if (dumpState.isDumping(DumpState.DUMP_MESSAGES)
-                && packageName == null) {
-            if (!checkin) {
-                if (dumpState.onTitlePrinted()) {
-                    pw.println();
-                }
-                synchronized (mLock) {
-                    mSettings.dumpReadMessagesLPr(pw, dumpState);
-                }
-                pw.println();
-                pw.println("Package warning messages:");
-                dumpCriticalInfo(pw, null);
-            } else {
-                dumpCriticalInfo(pw, "msg,");
-            }
-        }
-
-        // PackageInstaller should be called outside of mPackages lock
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_INSTALLS)
-                && packageName == null) {
-            // XXX should handle packageName != null by dumping only install data that
-            // the given package is involved with.
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            mInstallerService.dump(new IndentingPrintWriter(pw, "  ", 120));
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_APEX)
-                && (packageName == null || mApexManager.isApexPackage(packageName))) {
-            mApexManager.dump(pw, packageName);
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
-                && packageName == null) {
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            pw.println("Per UID read timeouts:");
-            pw.println("    Default timeouts flag: " + getDefaultTimeouts());
-            pw.println("    Known digesters list flag: " + getKnownDigestersList());
-
-            PerUidReadTimeouts[] items = getPerUidReadTimeouts();
-            pw.println("    Timeouts (" + items.length + "):");
-            for (PerUidReadTimeouts item : items) {
-                pw.print("        (");
-                pw.print("uid=" + item.uid + ", ");
-                pw.print("minTimeUs=" + item.minTimeUs + ", ");
-                pw.print("minPendingTimeUs=" + item.minPendingTimeUs + ", ");
-                pw.print("maxPendingTimeUs=" + item.maxPendingTimeUs);
-                pw.println(")");
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)
-                && packageName == null) {
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            pw.println("Snapshot statistics");
-            if (!mSnapshotEnabled) {
-                pw.println("  Snapshots disabled");
-            } else {
-                int hits = 0;
-                int level = sSnapshotCorked.get();
-                synchronized (mSnapshotLock) {
-                    if (mSnapshotComputer != null) {
-                        hits = mSnapshotComputer.getUsed();
-                    }
-                }
-                final long now = SystemClock.currentTimeMicro();
-                mSnapshotStatistics.dump(pw, "  ", now, hits, level, dumpState.isBrief());
-            }
-        }
-
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PROTECTED_BROADCASTS)
-                && packageName == null) {
-            if (dumpState.onTitlePrinted()) {
-                pw.println();
-            }
-            pw.println("Protected broadcast actions:");
-            synchronized (mProtectedBroadcasts) {
-                for (int i = 0; i < mProtectedBroadcasts.size(); i++) {
-                    pw.print("  ");
-                    pw.println(mProtectedBroadcasts.valueAt(i));
-                }
-            }
-
+            final long now = SystemClock.currentTimeMicro();
+            mSnapshotStatistics.dump(pw, "  ", now, hits, level, isBrief);
         }
     }
 
@@ -12128,7 +8491,7 @@
      * Dump package manager states to the file according to a given dumping type of
      * {@link DumpState}.
      */
-    private void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+    void dumpComputer(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
         mComputer.dump(type, fd, pw, dumpState);
     }
 
@@ -12153,83 +8516,6 @@
         }
     }
 
-    private void dumpProto(FileDescriptor fd) {
-        final ProtoOutputStream proto = new ProtoOutputStream(fd);
-
-        synchronized (mLock) {
-            final long requiredVerifierPackageToken =
-                    proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
-            proto.write(PackageServiceDumpProto.PackageShortProto.NAME, mRequiredVerifierPackage);
-            proto.write(
-                    PackageServiceDumpProto.PackageShortProto.UID,
-                    getPackageUid(
-                            mRequiredVerifierPackage,
-                            MATCH_DEBUG_TRIAGED_MISSING,
-                            UserHandle.USER_SYSTEM));
-            proto.end(requiredVerifierPackageToken);
-
-            DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
-            ComponentName verifierComponent = proxy.getComponentName();
-            if (verifierComponent != null) {
-                String verifierPackageName = verifierComponent.getPackageName();
-                final long verifierPackageToken =
-                        proto.start(PackageServiceDumpProto.VERIFIER_PACKAGE);
-                proto.write(PackageServiceDumpProto.PackageShortProto.NAME, verifierPackageName);
-                proto.write(
-                        PackageServiceDumpProto.PackageShortProto.UID,
-                        getPackageUid(
-                                verifierPackageName,
-                                MATCH_DEBUG_TRIAGED_MISSING,
-                                UserHandle.USER_SYSTEM));
-                proto.end(verifierPackageToken);
-            }
-
-            dumpSharedLibrariesProto(proto);
-            dumpFeaturesProto(proto);
-            mSettings.dumpPackagesProto(proto);
-            mSettings.dumpSharedUsersProto(proto);
-            dumpCriticalInfo(proto);
-        }
-        proto.flush();
-    }
-
-    private void dumpFeaturesProto(ProtoOutputStream proto) {
-        synchronized (mAvailableFeatures) {
-            final int count = mAvailableFeatures.size();
-            for (int i = 0; i < count; i++) {
-                mAvailableFeatures.valueAt(i).dumpDebug(proto, PackageServiceDumpProto.FEATURES);
-            }
-        }
-    }
-
-    private void dumpSharedLibrariesProto(ProtoOutputStream proto) {
-        final int count = mSharedLibraries.size();
-        for (int i = 0; i < count; i++) {
-            final String libName = mSharedLibraries.keyAt(i);
-            WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
-            if (versionedLib == null) {
-                continue;
-            }
-            final int versionCount = versionedLib.size();
-            for (int j = 0; j < versionCount; j++) {
-                final SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
-                final long sharedLibraryToken =
-                        proto.start(PackageServiceDumpProto.SHARED_LIBRARIES);
-                proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libraryInfo.getName());
-                final boolean isJar = (libraryInfo.getPath() != null);
-                proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar);
-                if (isJar) {
-                    proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH,
-                            libraryInfo.getPath());
-                } else {
-                    proto.write(PackageServiceDumpProto.SharedLibraryProto.APK,
-                            libraryInfo.getPackageName());
-                }
-                proto.end(sharedLibraryToken);
-            }
-        }
-    }
-
     public PackageFreezer freezePackage(String packageName, String killReason) {
         return freezePackage(packageName, UserHandle.USER_ALL, killReason);
     }
@@ -12255,7 +8541,7 @@
     /**
      * Verify that given package is currently frozen.
      */
-    private void checkPackageFrozen(String packageName) {
+    void checkPackageFrozen(String packageName) {
         synchronized (mLock) {
             if (!mFrozenPackages.contains(packageName)) {
                 Slog.wtf(TAG, "Expected " + packageName + " to be frozen!", new Throwable());
@@ -12363,60 +8649,8 @@
             mSettings.removeUserLPw(userId);
             mPendingBroadcasts.remove(userId);
             mInstantAppRegistry.onUserRemovedLPw(userId);
-            removeUnusedPackagesLPw(userManager, userId);
-        }
-    }
-
-    /**
-     * We're removing userId and would like to remove any downloaded packages
-     * that are no longer in use by any other user.
-     * @param userId the user being removed
-     */
-    @GuardedBy("mLock")
-    private void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) {
-        final boolean DEBUG_CLEAN_APKS = false;
-        int [] users = userManager.getUserIds();
-        final int numPackages = mSettings.getPackagesLocked().size();
-        for (int index = 0; index < numPackages; index++) {
-            final PackageSetting ps = mSettings.getPackagesLocked().valueAt(index);
-            if (ps.getPkg() == null) {
-                continue;
-            }
-            final String packageName = ps.getPkg().getPackageName();
-            // Skip over if system app or static shared library
-            if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
-                    || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())) {
-                continue;
-            }
-            if (DEBUG_CLEAN_APKS) {
-                Slog.i(TAG, "Checking package " + packageName);
-            }
-            boolean keep = shouldKeepUninstalledPackageLPr(packageName);
-            if (keep) {
-                if (DEBUG_CLEAN_APKS) {
-                    Slog.i(TAG, "  Keeping package " + packageName + " - requested by DO");
-                }
-            } else {
-                for (int i = 0; i < users.length; i++) {
-                    if (users[i] != userId && ps.getInstalled(users[i])) {
-                        keep = true;
-                        if (DEBUG_CLEAN_APKS) {
-                            Slog.i(TAG, "  Keeping package " + packageName + " for user "
-                                    + users[i]);
-                        }
-                        break;
-                    }
-                }
-            }
-            if (!keep) {
-                if (DEBUG_CLEAN_APKS) {
-                    Slog.i(TAG, "  Removing package " + packageName);
-                }
-                //end run
-                mHandler.post(() -> mDeletePackageHelper.deletePackageX(
-                        packageName, PackageManager.VERSION_CODE_HIGHEST,
-                        userId, 0, true /*removedBySystem*/));
-            }
+            mDeletePackageHelper.removeUnusedPackagesLPw(userManager, userId);
+            mAppsFilter.onUserDeleted(userId);
         }
     }
 
@@ -12438,7 +8672,7 @@
         synchronized (mLock) {
             scheduleWritePackageRestrictionsLocked(userId);
             scheduleWritePackageListLocked(userId);
-            mAppsFilter.onUsersChanged();
+            mAppsFilter.onUserCreated(userId);
         }
     }
 
@@ -12454,7 +8688,7 @@
         }
     }
 
-    boolean readPermissionStateForUser(@UserIdInt int userId) {
+    private boolean readPermissionStateForUser(@UserIdInt int userId) {
         synchronized (mLock) {
             mPermissionManager.writeLegacyPermissionStateTEMP();
             mSettings.readPermissionStateForUserSyncLPr(userId);
@@ -12504,7 +8738,7 @@
         return mArtManagerService;
     }
 
-    private boolean userNeedsBadging(int userId) {
+    boolean userNeedsBadging(int userId) {
         int index = mUserNeedsBadging.indexOfKey(userId);
         if (index < 0) {
             final UserInfo userInfo;
@@ -12633,200 +8867,6 @@
         }
     }
 
-    private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
-        private final IPackageChangeObserver mObserver;
-
-        PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
-            mObserver = observer;
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mPackageChangeObservers) {
-                mPackageChangeObservers.remove(mObserver);
-                Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
-                    + mPackageChangeObservers.size());
-            }
-        }
-    }
-
-    private class PackageManagerNative extends IPackageManagerNative.Stub {
-        @Override
-        public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
-          synchronized (mPackageChangeObservers) {
-            try {
-                observer.asBinder().linkToDeath(
-                    new PackageChangeObserverDeathRecipient(observer), 0);
-            } catch (RemoteException e) {
-              Log.e(TAG, e.getMessage());
-            }
-            mPackageChangeObservers.add(observer);
-            Log.d(TAG, "Size of mPackageChangeObservers after registry is "
-                + mPackageChangeObservers.size());
-          }
-        }
-
-        @Override
-        public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
-          synchronized (mPackageChangeObservers) {
-            mPackageChangeObservers.remove(observer);
-            Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
-                + mPackageChangeObservers.size());
-          }
-        }
-
-        @Override
-        public String[] getAllPackages() {
-            return PackageManagerService.this.getAllPackages().toArray(new String[0]);
-        }
-
-        @Override
-        public String[] getNamesForUids(int[] uids) throws RemoteException {
-            String[] names = null;
-            String[] results = null;
-            try {
-                if (uids == null || uids.length == 0) {
-                    return null;
-                }
-                names = PackageManagerService.this.getNamesForUids(uids);
-                results = (names != null) ? names : new String[uids.length];
-                // massage results so they can be parsed by the native binder
-                for (int i = results.length - 1; i >= 0; --i) {
-                    if (results[i] == null) {
-                        results[i] = "";
-                    }
-                }
-                return results;
-            } catch (Throwable t) {
-                // STOPSHIP(186558987): revert addition of try/catch/log
-                Slog.e(TAG, "uids: " + Arrays.toString(uids));
-                Slog.e(TAG, "names: " + Arrays.toString(names));
-                Slog.e(TAG, "results: " + Arrays.toString(results));
-                Slog.e(TAG, "throwing exception", t);
-                throw t;
-            }
-        }
-
-        // NB: this differentiates between preloads and sideloads
-        @Override
-        public String getInstallerForPackage(String packageName) throws RemoteException {
-            final String installerName = getInstallerPackageName(packageName);
-            if (!TextUtils.isEmpty(installerName)) {
-                return installerName;
-            }
-            // differentiate between preload and sideload
-            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
-            ApplicationInfo appInfo = getApplicationInfo(packageName,
-                                    /*flags*/ 0,
-                                    /*userId*/ callingUser);
-            if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                return "preload";
-            }
-            return "";
-        }
-
-        @Override
-        public long getVersionCodeForPackage(String packageName) throws RemoteException {
-            try {
-                int callingUser = UserHandle.getUserId(Binder.getCallingUid());
-                PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser);
-                if (pInfo != null) {
-                    return pInfo.getLongVersionCode();
-                }
-            } catch (Exception e) {
-            }
-            return 0;
-        }
-
-        @Override
-        public int getTargetSdkVersionForPackage(String packageName) throws RemoteException {
-            int targetSdk = getTargetSdkVersion(packageName);
-            if (targetSdk != -1) {
-                return targetSdk;
-            }
-
-            throw new RemoteException("Couldn't get targetSdkVersion for package " + packageName);
-        }
-
-        @Override
-        public boolean isPackageDebuggable(String packageName) throws RemoteException {
-            int callingUser = UserHandle.getCallingUserId();
-            ApplicationInfo appInfo = getApplicationInfo(packageName, 0, callingUser);
-            if (appInfo != null) {
-                return (0 != (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE));
-            }
-
-            throw new RemoteException("Couldn't get debug flag for package " + packageName);
-        }
-
-        @Override
-        public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
-                throws RemoteException {
-            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
-            boolean[] results = new boolean[packageNames.length];
-            for (int i = results.length - 1; i >= 0; --i) {
-                ApplicationInfo appInfo = getApplicationInfo(packageNames[i], 0, callingUser);
-                results[i] = appInfo != null && appInfo.isAudioPlaybackCaptureAllowed();
-            }
-            return results;
-        }
-
-        @Override
-        public int getLocationFlags(String packageName) throws RemoteException {
-            int callingUser = UserHandle.getUserId(Binder.getCallingUid());
-            ApplicationInfo appInfo = getApplicationInfo(packageName,
-                    /*flags*/ 0,
-                    /*userId*/ callingUser);
-            if (appInfo == null) {
-                throw new RemoteException(
-                        "Couldn't get ApplicationInfo for package " + packageName);
-            }
-            return ((appInfo.isSystemApp() ? IPackageManagerNative.LOCATION_SYSTEM : 0)
-                    | (appInfo.isVendor() ? IPackageManagerNative.LOCATION_VENDOR : 0)
-                    | (appInfo.isProduct() ? IPackageManagerNative.LOCATION_PRODUCT : 0));
-        }
-
-        @Override
-        public String getModuleMetadataPackageName() throws RemoteException {
-            return PackageManagerService.this.mModuleInfoProvider.getPackageName();
-        }
-
-        @Override
-        public boolean hasSha256SigningCertificate(String packageName, byte[] certificate)
-                throws RemoteException {
-            return PackageManagerService.this.hasSigningCertificate(
-                packageName, certificate, CERT_INPUT_SHA256);
-        }
-
-        @Override
-        public boolean hasSystemFeature(String featureName, int version) {
-            return PackageManagerService.this.hasSystemFeature(featureName, version);
-        }
-
-        @Override
-        public void registerStagedApexObserver(IStagedApexObserver observer) {
-            mInstallerService.getStagingManager().registerStagedApexObserver(observer);
-        }
-
-        @Override
-        public void unregisterStagedApexObserver(IStagedApexObserver observer) {
-            mInstallerService.getStagingManager().unregisterStagedApexObserver(observer);
-        }
-
-        @Override
-        public String[] getStagedApexModuleNames() {
-            return mInstallerService.getStagingManager()
-                    .getStagedApexModuleNames().toArray(new String[0]);
-        }
-
-        @Override
-        @Nullable
-        public StagedApexInfo getStagedApexInfo(String moduleName) {
-            return mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
-        }
-
-    }
-
     private AndroidPackage getPackage(String packageName) {
         return mComputer.getPackage(packageName);
     }
@@ -13100,85 +9140,9 @@
             return disabledPkg == null ? null : disabledPkg.getPackageName();
         }
 
-        /**
-         * Only keep package names that refer to {@link AndroidPackage#isSystem system} packages.
-         *
-         * @param pkgNames The packages to filter
-         *
-         * @return The filtered packages
-         */
-        private @NonNull String[] filterOnlySystemPackages(@Nullable String... pkgNames) {
-            if (pkgNames == null) {
-                return ArrayUtils.emptyArray(String.class);
-            }
-
-            ArrayList<String> systemPackageNames = new ArrayList<>(pkgNames.length);
-
-            for (String pkgName: pkgNames) {
-                synchronized (mLock) {
-                    if (pkgName == null) {
-                        continue;
-                    }
-
-                    AndroidPackage pkg = getPackage(pkgName);
-                    if (pkg == null) {
-                        Log.w(TAG, "Could not find package " + pkgName);
-                        continue;
-                    }
-
-                    if (!pkg.isSystem()) {
-                        Log.w(TAG, pkgName + " is not system");
-                        continue;
-                    }
-
-                    systemPackageNames.add(pkgName);
-                }
-            }
-
-            return systemPackageNames.toArray(new String[]{});
-        }
-
         @Override
         public @NonNull String[] getKnownPackageNames(int knownPackage, int userId) {
-            return getKnownPackageNamesInternal(knownPackage, userId);
-        }
-
-        private String[] getKnownPackageNamesInternal(int knownPackage, int userId) {
-            switch (knownPackage) {
-                case PackageManagerInternal.PACKAGE_BROWSER:
-                    return new String[] { mDefaultAppProvider.getDefaultBrowser(userId) };
-                case PackageManagerInternal.PACKAGE_INSTALLER:
-                    return filterOnlySystemPackages(mRequiredInstallerPackage);
-                case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
-                    return filterOnlySystemPackages(mSetupWizardPackage);
-                case PackageManagerInternal.PACKAGE_SYSTEM:
-                    return new String[]{"android"};
-                case PackageManagerInternal.PACKAGE_VERIFIER:
-                    return filterOnlySystemPackages(mRequiredVerifierPackage);
-                case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
-                    return filterOnlySystemPackages(
-                            mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
-                case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
-                    return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
-                case PackageManagerInternal.PACKAGE_CONFIGURATOR:
-                    return filterOnlySystemPackages(mConfiguratorPackage);
-                case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
-                    return filterOnlySystemPackages(mIncidentReportApproverPackage);
-                case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
-                    return filterOnlySystemPackages(mAppPredictionServicePackage);
-                case PackageManagerInternal.PACKAGE_COMPANION:
-                    return filterOnlySystemPackages(COMPANION_PACKAGE_NAME);
-                case PackageManagerInternal.PACKAGE_RETAIL_DEMO:
-                    return TextUtils.isEmpty(mRetailDemoPackage)
-                            ? ArrayUtils.emptyArray(String.class)
-                            : new String[] {mRetailDemoPackage};
-                case PackageManagerInternal.PACKAGE_OVERLAY_CONFIG_SIGNATURE:
-                    return filterOnlySystemPackages(getOverlayConfigSignaturePackageName());
-                case PackageManagerInternal.PACKAGE_RECENTS:
-                    return filterOnlySystemPackages(mRecentsPackage);
-                default:
-                    return ArrayUtils.emptyArray(String.class);
-            }
+            return PackageManagerService.this.getKnownPackageNamesInternal(knownPackage, userId);
         }
 
         @Override
@@ -13360,8 +9324,8 @@
         @Override
         public List<ResolveInfo> queryIntentReceivers(Intent intent,
                 String resolvedType, int flags, int filterCallingUid, int userId) {
-            return PackageManagerService.this.queryIntentReceiversInternal(intent, resolvedType,
-                    flags, userId, filterCallingUid);
+            return PackageManagerService.this.mResolveIntentHelper.queryIntentReceiversInternal(
+                    intent, resolvedType, flags, userId, filterCallingUid);
         }
 
         @Override
@@ -13396,17 +9360,6 @@
                 SparseArray<String> profileOwnerPackages) {
             mProtectedPackages.setDeviceAndProfileOwnerPackages(
                     deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages);
-
-            final ArraySet<Integer> usersWithPoOrDo = new ArraySet<>();
-            if (deviceOwnerPackage != null) {
-                usersWithPoOrDo.add(deviceOwnerUserId);
-            }
-            final int sz = profileOwnerPackages.size();
-            for (int i = 0; i < sz; i++) {
-                if (profileOwnerPackages.valueAt(i) != null) {
-                    usersWithPoOrDo.add(profileOwnerPackages.keyAt(i));
-                }
-            }
         }
 
         @Override
@@ -13654,7 +9607,7 @@
         public ResolveInfo resolveIntent(Intent intent, String resolvedType,
                 int flags, int privateResolveFlags, int userId, boolean resolveForStart,
                 int filterCallingUid) {
-            return resolveIntentInternal(
+            return mResolveIntentHelper.resolveIntentInternal(
                     intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
                     filterCallingUid);
         }
@@ -13662,7 +9615,8 @@
         @Override
         public ResolveInfo resolveService(Intent intent, String resolvedType,
                 int flags, int userId, int callingUid) {
-            return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid);
+            return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
+                    callingUid);
         }
 
         @Override
@@ -14606,7 +10560,8 @@
     private void applyMimeGroupChanges(String packageName, String mimeGroup) {
         if (mComponentResolver.updateMimeGroup(packageName, mimeGroup)) {
             Binder.withCleanCallingIdentity(() ->
-                    clearPackagePreferredActivities(packageName, UserHandle.USER_ALL));
+                    mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
+                            UserHandle.USER_ALL));
         }
 
         mPmInternal.writeSettings(false);
@@ -14657,7 +10612,7 @@
      * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's
      * writeLegacyPermissionsTEMP() beforehand.
      *
-     * TODO(zhanghai): This should be removed once we finish migration of permission storage.
+     * TODO(b/182523293): This should be removed once we finish migration of permission storage.
      */
     void writeSettingsLPrTEMP() {
         mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
@@ -14703,7 +10658,7 @@
         }
     }
 
-    private static String getDefaultTimeouts() {
+    static String getDefaultTimeouts() {
         final long token = Binder.clearCallingIdentity();
         try {
             return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
@@ -14713,7 +10668,7 @@
         }
     }
 
-    private static String getKnownDigestersList() {
+    static String getKnownDigestersList() {
         final long token = Binder.clearCallingIdentity();
         try {
             return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
@@ -14840,54 +10795,15 @@
         }
     }
 
+    boolean shouldKeepUninstalledPackageLPr(String packageName) {
+        return mKeepUninstalledPackages != null && mKeepUninstalledPackages.contains(packageName);
+    }
+
     @Override
     public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
             String featureId, int userId) throws RemoteException {
-        Objects.requireNonNull(packageName);
-        final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
-                false /* checkShell */, "get launch intent sender for package");
-        final int packageUid = getPackageUid(callingPackage, 0 /* flags */, userId);
-        if (!UserHandle.isSameApp(callingUid, packageUid)) {
-            throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
-                    + callingUid + " does not own package: " + callingPackage);
-        }
-
-        // Using the same implementation with the #getLaunchIntentForPackage to get the ResolveInfo.
-        // Pass the resolveForStart as true in queryIntentActivities to skip the app filtering.
-        final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
-        intentToResolve.addCategory(Intent.CATEGORY_INFO);
-        intentToResolve.setPackage(packageName);
-        String resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
-        List<ResolveInfo> ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
-                0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
-                true /* resolveForStart */, false /* allowDynamicSplits */);
-        if (ris == null || ris.size() <= 0) {
-            intentToResolve.removeCategory(Intent.CATEGORY_INFO);
-            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
-            intentToResolve.setPackage(packageName);
-            resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
-            ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
-                    0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
-                    true /* resolveForStart */, false /* allowDynamicSplits */);
-        }
-
-        final Intent intent = new Intent(intentToResolve);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        // For the case of empty result, no component name is assigned into the intent. A
-        // non-launchable IntentSender which contains the failed intent is created. The
-        // SendIntentException is thrown if the IntentSender#sendIntent is invoked.
-        if (ris != null && !ris.isEmpty()) {
-            intent.setClassName(ris.get(0).activityInfo.packageName,
-                    ris.get(0).activityInfo.name);
-        }
-        final IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
-                ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                featureId, null /* token */, null /* resultWho */,
-                1 /* requestCode */, new Intent[] { intent },
-                resolvedType != null ? new String[] { resolvedType } : null,
-                PendingIntent.FLAG_IMMUTABLE, null /* bOptions */, userId);
-        return new IntentSender(target);
+        return mResolveIntentHelper.getLaunchIntentSenderForPackage(packageName, callingPackage,
+                featureId, userId);
     }
 
     @Override
@@ -14921,31 +10837,255 @@
         }
     }
 
-    public boolean getSafeMode() {
+    boolean getSafeMode() {
         return mSafeMode;
     }
 
-    public ComponentName getResolveComponentName() {
+    ComponentName getResolveComponentName() {
         return mResolveComponentName;
     }
 
-    public DefaultAppProvider getDefaultAppProvider() {
+    DefaultAppProvider getDefaultAppProvider() {
         return mDefaultAppProvider;
     }
 
-    public File getCacheDir() {
+    File getCacheDir() {
         return mCacheDir;
     }
 
-    public List<ScanPartition> getDirsToScanAsSystem() {
-        return mDirsToScanAsSystem;
-    }
-
-    public PackageProperty getPackageProperty() {
+    PackageProperty getPackageProperty() {
         return mPackageProperty;
     }
 
-    public WatchedArrayMap<ComponentName, ParsedInstrumentation> getInstrumentation() {
+    WatchedArrayMap<ComponentName, ParsedInstrumentation> getInstrumentation() {
         return mInstrumentation;
     }
+
+    int getSdkVersion() {
+        return mSdkVersion;
+    }
+
+    void addAllPackageProperties(@NonNull AndroidPackage pkg) {
+        mPackageProperty.addAllProperties(pkg);
+    }
+
+    void addInstrumentation(ComponentName name, ParsedInstrumentation instrumentation) {
+        mInstrumentation.put(name, instrumentation);
+    }
+
+    String[] getKnownPackageNamesInternal(int knownPackage, int userId) {
+        switch (knownPackage) {
+            case PackageManagerInternal.PACKAGE_BROWSER:
+                return new String[] { mDefaultAppProvider.getDefaultBrowser(userId) };
+            case PackageManagerInternal.PACKAGE_INSTALLER:
+                return filterOnlySystemPackages(mRequiredInstallerPackage);
+            case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
+                return filterOnlySystemPackages(mSetupWizardPackage);
+            case PackageManagerInternal.PACKAGE_SYSTEM:
+                return new String[]{"android"};
+            case PackageManagerInternal.PACKAGE_VERIFIER:
+                return filterOnlySystemPackages(mRequiredVerifierPackage);
+            case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
+                return filterOnlySystemPackages(
+                        mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
+            case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
+                return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
+            case PackageManagerInternal.PACKAGE_CONFIGURATOR:
+                return filterOnlySystemPackages(mConfiguratorPackage);
+            case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
+                return filterOnlySystemPackages(mIncidentReportApproverPackage);
+            case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
+                return filterOnlySystemPackages(mAppPredictionServicePackage);
+            case PackageManagerInternal.PACKAGE_COMPANION:
+                return filterOnlySystemPackages(COMPANION_PACKAGE_NAME);
+            case PackageManagerInternal.PACKAGE_RETAIL_DEMO:
+                return TextUtils.isEmpty(mRetailDemoPackage)
+                        ? ArrayUtils.emptyArray(String.class)
+                        : new String[] {mRetailDemoPackage};
+            case PackageManagerInternal.PACKAGE_OVERLAY_CONFIG_SIGNATURE:
+                return filterOnlySystemPackages(getOverlayConfigSignaturePackageName());
+            case PackageManagerInternal.PACKAGE_RECENTS:
+                return filterOnlySystemPackages(mRecentsPackage);
+            default:
+                return ArrayUtils.emptyArray(String.class);
+        }
+    }
+
+    /**
+     * Only keep package names that refer to {@link AndroidPackage#isSystem system} packages.
+     *
+     * @param pkgNames The packages to filter
+     *
+     * @return The filtered packages
+     */
+    private @NonNull String[] filterOnlySystemPackages(@Nullable String... pkgNames) {
+        if (pkgNames == null) {
+            return ArrayUtils.emptyArray(String.class);
+        }
+
+        ArrayList<String> systemPackageNames = new ArrayList<>(pkgNames.length);
+
+        for (String pkgName: pkgNames) {
+            synchronized (mLock) {
+                if (pkgName == null) {
+                    continue;
+                }
+
+                AndroidPackage pkg = getPackage(pkgName);
+                if (pkg == null) {
+                    Log.w(TAG, "Could not find package " + pkgName);
+                    continue;
+                }
+
+                if (!pkg.isSystem()) {
+                    Log.w(TAG, pkgName + " is not system");
+                    continue;
+                }
+
+                systemPackageNames.add(pkgName);
+            }
+        }
+
+        return systemPackageNames.toArray(new String[]{});
+    }
+
+    String getActiveLauncherPackageName(int userId) {
+        return mDefaultAppProvider.getDefaultHome(userId);
+    }
+
+    boolean setActiveLauncherPackage(@NonNull String packageName, @UserIdInt int userId,
+            @NonNull Consumer<Boolean> callback) {
+        return mDefaultAppProvider.setDefaultHome(packageName, userId, mContext.getMainExecutor(),
+                callback);
+    }
+
+    void setDefaultBrowser(@Nullable String packageName, boolean async, @UserIdInt int userId) {
+        mDefaultAppProvider.setDefaultBrowser(packageName, async, userId);
+    }
+
+    ResolveInfo getInstantAppInstallerInfo() {
+        return mInstantAppInstallerInfo;
+    }
+
+    PackageUsage getPackageUsage() {
+        return mPackageUsage;
+    }
+
+    String getModuleMetadataPackageName() {
+        return mModuleInfoProvider.getPackageName();
+    }
+
+    File getAppInstallDir() {
+        return mAppInstallDir;
+    }
+
+    boolean isExpectingBetter(String packageName) {
+        return mInitAndSystemPackageHelper.isExpectingBetter(packageName);
+    }
+
+    int getDefParseFlags() {
+        return mDefParseFlags;
+    }
+
+    void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
+        synchronized (mLock) {
+            mResolverReplaced = true;
+
+            // The instance created in PackageManagerService is special cased to be non-user
+            // specific, so initialize all the needed fields here.
+            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0,
+                    PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
+
+            // Set up information for custom user intent resolution activity.
+            mResolveActivity.applicationInfo = appInfo;
+            mResolveActivity.name = mCustomResolverComponentName.getClassName();
+            mResolveActivity.packageName = pkg.getPackageName();
+            mResolveActivity.processName = pkg.getProcessName();
+            mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+            mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
+                    | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+            mResolveActivity.theme = 0;
+            mResolveActivity.exported = true;
+            mResolveActivity.enabled = true;
+            mResolveInfo.activityInfo = mResolveActivity;
+            mResolveInfo.priority = 0;
+            mResolveInfo.preferredOrder = 0;
+            mResolveInfo.match = 0;
+            mResolveComponentName = mCustomResolverComponentName;
+            PackageManagerService.onChanged();
+            Slog.i(TAG, "Replacing default ResolverActivity with custom activity: "
+                    + mResolveComponentName);
+        }
+    }
+
+    void setPlatformPackage(AndroidPackage pkg, PackageSetting pkgSetting) {
+        synchronized (mLock) {
+            // Set up information for our fall-back user intent resolution activity.
+            mPlatformPackage = pkg;
+
+            // The instance stored in PackageManagerService is special cased to be non-user
+            // specific, so initialize all the needed fields here.
+            mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0,
+                    PackageUserState.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
+
+            if (!mResolverReplaced) {
+                mResolveActivity.applicationInfo = mAndroidApplication;
+                mResolveActivity.name = ResolverActivity.class.getName();
+                mResolveActivity.packageName = mAndroidApplication.packageName;
+                mResolveActivity.processName = "system:ui";
+                mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+                mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
+                mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+                mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
+                mResolveActivity.exported = true;
+                mResolveActivity.enabled = true;
+                mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+                mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
+                        | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
+                        | ActivityInfo.CONFIG_SCREEN_LAYOUT
+                        | ActivityInfo.CONFIG_ORIENTATION
+                        | ActivityInfo.CONFIG_KEYBOARD
+                        | ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+                mResolveInfo.activityInfo = mResolveActivity;
+                mResolveInfo.priority = 0;
+                mResolveInfo.preferredOrder = 0;
+                mResolveInfo.match = 0;
+                mResolveComponentName = new ComponentName(
+                        mAndroidApplication.packageName, mResolveActivity.name);
+            }
+            PackageManagerService.onChanged();
+        }
+    }
+
+    ResolveInfo getResolveInfo() {
+        return mResolveInfo;
+    }
+
+    ApplicationInfo getCoreAndroidApplication() {
+        return mAndroidApplication;
+    }
+
+    boolean isSystemReady() {
+        return mSystemReady;
+    }
+
+    AndroidPackage getPlatformPackage() {
+        return mPlatformPackage;
+    }
+
+    boolean isPreNUpgrade() {
+        return mIsPreNUpgrade;
+    }
+
+    boolean isPreNMR1Upgrade() {
+        return mIsPreNMR1Upgrade;
+    }
+
+    InitAndSystemPackageHelper getInitAndSystemPackageHelper() {
+        return mInitAndSystemPackageHelper;
+    }
+
+    boolean isOverlayMutable(String packageName) {
+        return mOverlayConfig.isMutable(packageName);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index c1c32dd..9327c5f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -102,4 +102,12 @@
     public int sdkInt = Build.VERSION.SDK_INT;
     public BackgroundDexOptService backgroundDexOptService;
     public final String incrementalVersion = Build.VERSION.INCREMENTAL;
+    public BroadcastHelper broadcastHelper;
+    public AppDataHelper appDataHelper;
+    public RemovePackageHelper removePackageHelper;
+    public InitAndSystemPackageHelper initAndSystemPackageHelper;
+    public DeletePackageHelper deletePackageHelper;
+    public PreferredActivityHelper preferredActivityHelper;
+    public ResolveIntentHelper resolveIntentHelper;
+    public DexOptHelper dexOptHelper;
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 144b2b0..e124e04 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -23,7 +23,6 @@
 
 import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
-import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
 import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
 import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
 import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX;
@@ -33,7 +32,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.AppGlobals;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.content.Context;
@@ -45,7 +43,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ApkLiteParseUtils;
@@ -53,14 +50,13 @@
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Debug;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.V4Signature;
 import android.os.incremental.V4Signature.HashingInfo;
@@ -86,11 +82,9 @@
 import com.android.server.EventLogTags;
 import com.android.server.IntentResolver;
 import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.utils.WatchedLongSparseArray;
 
 import dalvik.system.VMRuntime;
 
@@ -112,14 +106,10 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
+import java.util.Objects;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.zip.GZIPInputStream;
@@ -130,7 +120,6 @@
  * {@hide}
  */
 public class PackageManagerServiceUtils {
-    private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
     private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 3 * 1000 * 1000; // 3MB
 
     public final static Predicate<PackageSetting> REMOVE_IF_NULL_PKG =
@@ -150,151 +139,18 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
     private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
 
-    private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
-        List<ResolveInfo> ris = null;
-        try {
-            ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
-                    .getList();
-        } catch (RemoteException e) {
-        }
-        ArraySet<String> pkgNames = new ArraySet<String>();
-        if (ris != null) {
-            for (ResolveInfo ri : ris) {
-                pkgNames.add(ri.activityInfo.packageName);
-            }
-        }
-        return pkgNames;
-    }
+    /**
+     * The initial enabled state of the cache before other checks are done.
+     */
+    private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
 
-    // Sort a list of apps by their last usage, most recently used apps first. The order of
-    // packages without usage data is undefined (but they will be sorted after the packages
-    // that do have usage data).
-    public static void sortPackagesByUsageDate(List<PackageSetting> pkgSettings,
-            PackageManagerService packageManagerService) {
-        if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
-            return;
-        }
-
-        Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) ->
-                Long.compare(
-                        pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills(),
-                        pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills())
-        );
-    }
-
-    // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
-    // package will be removed from {@code packages} and added to {@code result} with its
-    // dependencies. If usage data is available, the positive packages will be sorted by usage
-    // data (with {@code sortTemp} as temporary storage).
-    private static void applyPackageFilter(
-            Predicate<PackageSetting> filter,
-            Collection<PackageSetting> result,
-            Collection<PackageSetting> packages,
-            @NonNull List<PackageSetting> sortTemp,
-            PackageManagerService packageManagerService) {
-        for (PackageSetting pkgSetting : packages) {
-            if (filter.test(pkgSetting)) {
-                sortTemp.add(pkgSetting);
-            }
-        }
-
-        sortPackagesByUsageDate(sortTemp, packageManagerService);
-        packages.removeAll(sortTemp);
-
-        for (PackageSetting pkgSetting : sortTemp) {
-            result.add(pkgSetting);
-
-            List<PackageSetting> deps =
-                    packageManagerService.findSharedNonSystemLibraries(pkgSetting);
-            if (!deps.isEmpty()) {
-                deps.removeAll(result);
-                result.addAll(deps);
-                packages.removeAll(deps);
-            }
-        }
-
-        sortTemp.clear();
-    }
-
-    // Sort apps by importance for dexopt ordering. Important apps are given
-    // more priority in case the device runs out of space.
-    public static List<PackageSetting> getPackagesForDexopt(
-            Collection<PackageSetting> packages,
-            PackageManagerService packageManagerService) {
-        return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
-    }
-
-    public static List<PackageSetting> getPackagesForDexopt(
-            Collection<PackageSetting> pkgSettings,
-            PackageManagerService packageManagerService,
-            boolean debug) {
-        List<PackageSetting> result = new LinkedList<>();
-        ArrayList<PackageSetting> remainingPkgSettings = new ArrayList<>(pkgSettings);
-
-        // First, remove all settings without available packages
-        remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG);
-
-        ArrayList<PackageSetting> sortTemp = new ArrayList<>(remainingPkgSettings.size());
-
-        // Give priority to core apps.
-        applyPackageFilter(pkgSetting -> pkgSetting.getPkg().isCoreApp(), result,
-                remainingPkgSettings, sortTemp, packageManagerService);
-
-        // Give priority to system apps that listen for pre boot complete.
-        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
-        final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
-        applyPackageFilter(pkgSetting -> pkgNames.contains(pkgSetting.getPackageName()), result,
-                remainingPkgSettings, sortTemp, packageManagerService);
-
-        // Give priority to apps used by other apps.
-        DexManager dexManager = packageManagerService.getDexManager();
-        applyPackageFilter(pkgSetting ->
-                dexManager.getPackageUseInfoOrDefault(pkgSetting.getPackageName())
-                        .isAnyCodePathUsedByOtherApps(),
-                result, remainingPkgSettings, sortTemp, packageManagerService);
-
-        // Filter out packages that aren't recently used, add all remaining apps.
-        // TODO: add a property to control this?
-        Predicate<PackageSetting> remainingPredicate;
-        if (!remainingPkgSettings.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
-            if (debug) {
-                Log.i(TAG, "Looking at historical package use");
-            }
-            // Get the package that was used last.
-            PackageSetting lastUsed = Collections.max(remainingPkgSettings,
-                    (pkgSetting1, pkgSetting2) -> Long.compare(
-                            pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills(),
-                            pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills()));
-            if (debug) {
-                Log.i(TAG, "Taking package " + lastUsed.getPackageName()
-                        + " as reference in time use");
-            }
-            long estimatedPreviousSystemUseTime = lastUsed.getPkgState()
-                    .getLatestForegroundPackageUseTimeInMills();
-            // Be defensive if for some reason package usage has bogus data.
-            if (estimatedPreviousSystemUseTime != 0) {
-                final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
-                remainingPredicate = pkgSetting -> pkgSetting.getPkgState()
-                        .getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
-            } else {
-                // No meaningful historical info. Take all.
-                remainingPredicate = pkgSetting -> true;
-            }
-            sortPackagesByUsageDate(remainingPkgSettings, packageManagerService);
-        } else {
-            // No historical info. Take all.
-            remainingPredicate = pkgSetting -> true;
-        }
-        applyPackageFilter(remainingPredicate, result, remainingPkgSettings, sortTemp,
-                packageManagerService);
-
-        if (debug) {
-            Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
-            Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
-        }
-
-        return result;
-    }
+    /**
+     * Whether to skip all other checks and force the cache to be enabled.
+     *
+     * Setting this to true will cause the cache to be named "debug" to avoid eviction from
+     * build fingerprint changes.
+     */
+    private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false;
 
     /**
      * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
@@ -343,17 +199,6 @@
         }
     }
 
-    public static String packagesToString(List<PackageSetting> pkgSettings) {
-        StringBuilder sb = new StringBuilder();
-        for (int index = 0; index < pkgSettings.size(); index++) {
-            if (sb.length() > 0) {
-                sb.append(", ");
-            }
-            sb.append(pkgSettings.get(index).getPackageName());
-        }
-        return sb.toString();
-    }
-
     /**
      * Verifies that the given string {@code isa} is a valid supported isa on
      * the running device.
@@ -1169,13 +1014,14 @@
             }
 
             final boolean match = comp.getIntents().stream().anyMatch(
-                    f -> IntentResolver.intentMatchesFilter(f, intent, resolvedType));
+                    f -> IntentResolver.intentMatchesFilter(f.getIntentFilter(), intent,
+                            resolvedType));
             if (!match) {
                 Slog.w(TAG, "Intent does not match component's intent filter: " + intent);
                 Slog.w(TAG, "Access blocked: " + comp.getComponentName());
                 if (DEBUG_INTENT_MATCHING) {
                     Slog.v(TAG, "Component intent filters:");
-                    comp.getIntents().forEach(f -> f.dump(logPrinter, "  "));
+                    comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, "  "));
                     Slog.v(TAG, "-----------------------------");
                 }
                 resolveInfos.remove(i);
@@ -1276,4 +1122,107 @@
         }
         return StorageEnums.UNKNOWN;
     }
+
+    /**
+     * Enforces that only the system UID or root's UID or shell's UID can call
+     * a method exposed via Binder.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller is not system or shell
+     */
+    public static void enforceSystemOrRootOrShell(String message) {
+        final int uid = Binder.getCallingUid();
+        if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_UID) {
+            throw new SecurityException(message);
+        }
+    }
+
+    /**
+     * Enforces that only the system UID or root's UID can call a method exposed
+     * via Binder.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller is not system or root
+     */
+    public static void enforceSystemOrRoot(String message) {
+        final int uid = Binder.getCallingUid();
+        if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
+            throw new SecurityException(message);
+        }
+    }
+
+    public static @Nullable File preparePackageParserCache(boolean forEngBuild,
+            boolean isUserDebugBuild, String incrementalVersion) {
+        if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
+            if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
+                return null;
+            }
+
+            // Disable package parsing on eng builds to allow for faster incremental development.
+            if (forEngBuild) {
+                return null;
+            }
+
+            if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
+                Slog.i(TAG, "Disabling package parser cache due to system property.");
+                return null;
+            }
+        }
+
+        // The base directory for the package parser cache lives under /data/system/.
+        final File cacheBaseDir = Environment.getPackageCacheDirectory();
+        if (!FileUtils.createDir(cacheBaseDir)) {
+            return null;
+        }
+
+        // There are several items that need to be combined together to safely
+        // identify cached items. In particular, changing the value of certain
+        // feature flags should cause us to invalidate any caches.
+        final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
+                : SystemProperties.digestOf("ro.build.fingerprint");
+
+        // Reconcile cache directories, keeping only what we'd actually use.
+        for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
+            if (Objects.equals(cacheName, cacheDir.getName())) {
+                Slog.d(TAG, "Keeping known cache " + cacheDir.getName());
+            } else {
+                Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName());
+                FileUtils.deleteContentsAndDir(cacheDir);
+            }
+        }
+
+        // Return the versioned package cache directory.
+        File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
+
+        if (cacheDir == null) {
+            // Something went wrong. Attempt to delete everything and return.
+            Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
+            FileUtils.deleteContentsAndDir(cacheBaseDir);
+            return null;
+        }
+
+        // The following is a workaround to aid development on non-numbered userdebug
+        // builds or cases where "adb sync" is used on userdebug builds. If we detect that
+        // the system partition is newer.
+        //
+        // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
+        // that starts with "eng." to signify that this is an engineering build and not
+        // destined for release.
+        if (isUserDebugBuild && incrementalVersion.startsWith("eng.")) {
+            Slog.w(TAG, "Wiping cache directory because the system partition changed.");
+
+            // Heuristic: If the /system directory has been modified recently due to an "adb sync"
+            // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
+            // in general and should not be used for production changes. In this specific case,
+            // we know that they will work.
+            File frameworkDir =
+                    new File(Environment.getRootDirectory(), "framework");
+            if (cacheDir.lastModified() < frameworkDir.lastModified()) {
+                FileUtils.deleteContents(cacheBaseDir);
+                cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
+            }
+        }
+
+        return cacheDir;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 74e1050..a60d2c8 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -32,7 +32,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 
-public final class PackageRemovedInfo {
+final class PackageRemovedInfo {
     final PackageSender mPackageSender;
     String mRemovedPackage;
     String mInstallerPackageName;
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
new file mode 100644
index 0000000..68e880b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -0,0 +1,736 @@
+/*
+ * 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.server.pm;
+
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
+import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LogPrinter;
+import android.util.PrintStreamPrinter;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+final class PreferredActivityHelper {
+    // XML tags for backup/restore of various bits of state
+    private static final String TAG_PREFERRED_BACKUP = "pa";
+    private static final String TAG_DEFAULT_APPS = "da";
+
+    private final PackageManagerService mPm;
+
+    // TODO(b/198166813): remove PMS dependency
+    PreferredActivityHelper(PackageManagerService pm) {
+        mPm = pm;
+    }
+
+    private ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType,
+            int flags, List<ResolveInfo> query, boolean always, boolean removeMatches,
+            boolean debug, int userId) {
+        return findPreferredActivityNotLocked(
+                intent, resolvedType, flags, query, always, removeMatches, debug, userId,
+                UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
+    }
+
+    // TODO: handle preferred activities missing while user has amnesia
+    /** <b>must not hold {@link PackageManagerService.mLock}</b> */
+    public ResolveInfo findPreferredActivityNotLocked(
+            Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean always,
+            boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
+        if (Thread.holdsLock(mPm.mLock)) {
+            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+                    + " is holding mLock", new Throwable());
+        }
+        if (!mPm.mUserManager.exists(userId)) return null;
+
+        PackageManagerService.FindPreferredActivityBodyResult body =
+                mPm.findPreferredActivityInternal(
+                intent, resolvedType, flags, query, always,
+                removeMatches, debug, userId, queryMayBeFiltered);
+        if (body.mChanged) {
+            if (DEBUG_PREFERRED) {
+                Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions");
+            }
+            synchronized (mPm.mLock) {
+                mPm.scheduleWritePackageRestrictionsLocked(userId);
+            }
+        }
+        if ((DEBUG_PREFERRED || debug) && body.mPreferredResolveInfo == null) {
+            Slog.v(TAG, "No preferred activity to return");
+        }
+        return body.mPreferredResolveInfo;
+    }
+
+    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    public void clearPackagePreferredActivities(String packageName, int userId) {
+        final SparseBooleanArray changedUsers = new SparseBooleanArray();
+        synchronized (mPm.mLock) {
+            mPm.clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
+        }
+        if (changedUsers.size() > 0) {
+            updateDefaultHomeNotLocked(changedUsers);
+            mPm.postPreferredActivityChangedBroadcast(userId);
+            synchronized (mPm.mLock) {
+                mPm.scheduleWritePackageRestrictionsLocked(userId);
+            }
+        }
+    }
+
+    /**
+     * <b>must not hold {@link PackageManagerService.mLock}</b>
+     *
+     * @return Whether the ACTION_PREFERRED_ACTIVITY_CHANGED broadcast has been scheduled.
+     */
+    public boolean updateDefaultHomeNotLocked(int userId) {
+        if (Thread.holdsLock(mPm.mLock)) {
+            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+                    + " is holding mLock", new Throwable());
+        }
+        if (!mPm.isSystemReady()) {
+            // We might get called before system is ready because of package changes etc, but
+            // finding preferred activity depends on settings provider, so we ignore the update
+            // before that.
+            return false;
+        }
+        final Intent intent = mPm.getHomeIntent();
+        final List<ResolveInfo> resolveInfos = mPm.queryIntentActivitiesInternal(intent, null,
+                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
+        final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
+                intent, null, 0, resolveInfos, true, false, false, userId);
+        final String packageName = preferredResolveInfo != null
+                && preferredResolveInfo.activityInfo != null
+                ? preferredResolveInfo.activityInfo.packageName : null;
+        final String currentPackageName = mPm.getActiveLauncherPackageName(userId);
+        if (TextUtils.equals(currentPackageName, packageName)) {
+            return false;
+        }
+        final String[] callingPackages = mPm.getPackagesForUid(Binder.getCallingUid());
+        if (callingPackages != null && ArrayUtils.contains(callingPackages,
+                mPm.mRequiredPermissionControllerPackage)) {
+            // PermissionController manages default home directly.
+            return false;
+        }
+
+        if (packageName == null) {
+            // Keep the default home package in RoleManager.
+            return false;
+        }
+        return mPm.setActiveLauncherPackage(packageName, userId,
+                successful -> {
+                    if (successful) {
+                        mPm.postPreferredActivityChangedBroadcast(userId);
+                    }
+                });
+    }
+
+    /**
+     * Variant that takes a {@link WatchedIntentFilter}
+     */
+    public void addPreferredActivity(WatchedIntentFilter filter, int match,
+            ComponentName[] set, ComponentName activity, boolean always, int userId,
+            String opname, boolean removeExisting) {
+        // writer
+        int callingUid = Binder.getCallingUid();
+        mPm.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "add preferred activity");
+        if (mPm.mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
+                != PackageManager.PERMISSION_GRANTED) {
+            synchronized (mPm.mLock) {
+                if (mPm.getUidTargetSdkVersionLockedLPr(callingUid)
+                        < Build.VERSION_CODES.FROYO) {
+                    Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
+                            + callingUid);
+                    return;
+                }
+            }
+            mPm.mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+        }
+        if (filter.countActions() == 0) {
+            Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
+            return;
+        }
+        if (DEBUG_PREFERRED) {
+            Slog.i(TAG, opname + " activity " + activity.flattenToShortString() + " for user "
+                    + userId + ":");
+            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
+        }
+        synchronized (mPm.mLock) {
+            final PreferredIntentResolver pir = mPm.mSettings.editPreferredActivitiesLPw(userId);
+            final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
+            if (removeExisting && existing != null) {
+                Settings.removeFilters(pir, filter, existing);
+            }
+            pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
+            mPm.scheduleWritePackageRestrictionsLocked(userId);
+        }
+        if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(userId))) {
+            mPm.postPreferredActivityChangedBroadcast(userId);
+        }
+    }
+
+    /**
+     * Variant that takes a {@link WatchedIntentFilter}
+     */
+    public void replacePreferredActivity(WatchedIntentFilter filter, int match,
+            ComponentName[] set, ComponentName activity, int userId) {
+        if (filter.countActions() != 1) {
+            throw new IllegalArgumentException(
+                    "replacePreferredActivity expects filter to have only 1 action.");
+        }
+        if (filter.countDataAuthorities() != 0
+                || filter.countDataPaths() != 0
+                || filter.countDataSchemes() > 1
+                || filter.countDataTypes() != 0) {
+            throw new IllegalArgumentException(
+                    "replacePreferredActivity expects filter to have no data authorities, "
+                            + "paths, or types; and at most one scheme.");
+        }
+
+        final int callingUid = Binder.getCallingUid();
+        mPm.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+                false /* checkShell */, "replace preferred activity");
+        if (mPm.mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
+                != PackageManager.PERMISSION_GRANTED) {
+            synchronized (mPm.mLock) {
+                if (mPm.getUidTargetSdkVersionLockedLPr(callingUid)
+                        < Build.VERSION_CODES.FROYO) {
+                    Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
+                            + Binder.getCallingUid());
+                    return;
+                }
+            }
+            mPm.mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+        }
+
+        synchronized (mPm.mLock) {
+            final PreferredIntentResolver pir = mPm.mSettings.getPreferredActivities(userId);
+            if (pir != null) {
+                // Get all of the existing entries that exactly match this filter.
+                final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
+                if (existing != null && existing.size() == 1) {
+                    final PreferredActivity cur = existing.get(0);
+                    if (DEBUG_PREFERRED) {
+                        Slog.i(TAG, "Checking replace of preferred:");
+                        filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
+                        if (!cur.mPref.mAlways) {
+                            Slog.i(TAG, "  -- CUR; not mAlways!");
+                        } else {
+                            Slog.i(TAG, "  -- CUR: mMatch=" + cur.mPref.mMatch);
+                            Slog.i(TAG, "  -- CUR: mSet="
+                                    + Arrays.toString(cur.mPref.mSetComponents));
+                            Slog.i(TAG, "  -- CUR: mComponent=" + cur.mPref.mShortComponent);
+                            Slog.i(TAG, "  -- NEW: mMatch="
+                                    + (match & IntentFilter.MATCH_CATEGORY_MASK));
+                            Slog.i(TAG, "  -- CUR: mSet=" + Arrays.toString(set));
+                            Slog.i(TAG, "  -- CUR: mComponent=" + activity.flattenToShortString());
+                        }
+                    }
+                    if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity)
+                            && cur.mPref.mMatch == (match & IntentFilter.MATCH_CATEGORY_MASK)
+                            && cur.mPref.sameSet(set)) {
+                        // Setting the preferred activity to what it happens to be already
+                        if (DEBUG_PREFERRED) {
+                            Slog.i(TAG, "Replacing with same preferred activity "
+                                    + cur.mPref.mShortComponent + " for user "
+                                    + userId + ":");
+                            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
+                        }
+                        return;
+                    }
+                }
+                if (existing != null) {
+                    Settings.removeFilters(pir, filter, existing);
+                }
+            }
+        }
+        addPreferredActivity(filter, match, set, activity, true, userId,
+                "Replacing preferred", false);
+    }
+
+    public void clearPackagePreferredActivities(String packageName) {
+        final int callingUid = Binder.getCallingUid();
+        if (mPm.getInstantAppPackageName(callingUid) != null) {
+            return;
+        }
+        // writer
+        synchronized (mPm.mLock) {
+            AndroidPackage pkg = mPm.mPackages.get(packageName);
+            if (pkg == null || !mPm.isCallerSameApp(packageName, callingUid)) {
+                if (mPm.mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    if (mPm.getUidTargetSdkVersionLockedLPr(callingUid)
+                            < Build.VERSION_CODES.FROYO) {
+                        Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid "
+                                + callingUid);
+                        return;
+                    }
+                    mPm.mContext.enforceCallingOrSelfPermission(
+                            android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+                }
+            }
+            final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+            if (ps != null
+                    && mPm.shouldFilterApplicationLocked(
+                    ps, callingUid, UserHandle.getUserId(callingUid))) {
+                return;
+            }
+        }
+        int callingUserId = UserHandle.getCallingUserId();
+        clearPackagePreferredActivities(packageName, callingUserId);
+    }
+
+    /** <b>must not hold {@link #PackageManagerService.mLock}</b> */
+    void updateDefaultHomeNotLocked(SparseBooleanArray userIds) {
+        if (Thread.holdsLock(mPm.mLock)) {
+            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+                    + " is holding mLock", new Throwable());
+        }
+        for (int i = userIds.size() - 1; i >= 0; --i) {
+            final int userId = userIds.keyAt(i);
+            updateDefaultHomeNotLocked(userId);
+        }
+    }
+
+    public void setHomeActivity(ComponentName comp, int userId) {
+        if (mPm.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+            return;
+        }
+        ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
+        mPm.getHomeActivitiesAsUser(homeActivities, userId);
+
+        boolean found = false;
+
+        final int size = homeActivities.size();
+        final ComponentName[] set = new ComponentName[size];
+        for (int i = 0; i < size; i++) {
+            final ResolveInfo candidate = homeActivities.get(i);
+            final ActivityInfo info = candidate.activityInfo;
+            final ComponentName activityName = new ComponentName(info.packageName, info.name);
+            set[i] = activityName;
+            if (!found && activityName.equals(comp)) {
+                found = true;
+            }
+        }
+        if (!found) {
+            throw new IllegalArgumentException("Component " + comp + " cannot be home on user "
+                    + userId);
+        }
+        replacePreferredActivity(getHomeFilter(), IntentFilter.MATCH_CATEGORY_EMPTY,
+                set, comp, userId);
+    }
+
+    private WatchedIntentFilter getHomeFilter() {
+        WatchedIntentFilter filter = new WatchedIntentFilter(Intent.ACTION_MAIN);
+        filter.addCategory(Intent.CATEGORY_HOME);
+        filter.addCategory(Intent.CATEGORY_DEFAULT);
+        return filter;
+    }
+
+    /**
+     * Variant that takes a {@link WatchedIntentFilter}
+     */
+    public void addPersistentPreferredActivity(WatchedIntentFilter filter, ComponentName activity,
+            int userId) {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SYSTEM_UID) {
+            throw new SecurityException(
+                    "addPersistentPreferredActivity can only be run by the system");
+        }
+        if (filter.countActions() == 0) {
+            Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
+            return;
+        }
+        if (DEBUG_PREFERRED) {
+            Slog.i(TAG, "Adding persistent preferred activity " + activity
+                    + " for user " + userId + ":");
+            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
+        }
+        synchronized (mPm.mLock) {
+            mPm.mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
+                    new PersistentPreferredActivity(filter, activity, true));
+            mPm.scheduleWritePackageRestrictionsLocked(userId);
+        }
+        if (isHomeFilter(filter)) {
+            updateDefaultHomeNotLocked(userId);
+        }
+        mPm.postPreferredActivityChangedBroadcast(userId);
+    }
+
+    public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SYSTEM_UID) {
+            throw new SecurityException(
+                    "clearPackagePersistentPreferredActivities can only be run by the system");
+        }
+        boolean changed = false;
+        synchronized (mPm.mLock) {
+            changed = mPm.mSettings.clearPackagePersistentPreferredActivities(packageName, userId);
+        }
+        if (changed) {
+            updateDefaultHomeNotLocked(userId);
+            mPm.postPreferredActivityChangedBroadcast(userId);
+            synchronized (mPm.mLock) {
+                mPm.scheduleWritePackageRestrictionsLocked(userId);
+            }
+        }
+    }
+
+    private boolean isHomeFilter(@NonNull WatchedIntentFilter filter) {
+        return filter.hasAction(Intent.ACTION_MAIN) && filter.hasCategory(Intent.CATEGORY_HOME)
+                && filter.hasCategory(CATEGORY_DEFAULT);
+    }
+
+    /**
+     * Common machinery for picking apart a restored XML blob and passing
+     * it to a caller-supplied functor to be applied to the running system.
+     */
+    private void restoreFromXml(TypedXmlPullParser parser, int userId,
+            String expectedStartTag, BlobXmlRestorer functor)
+            throws IOException, XmlPullParserException {
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+        }
+        if (type != XmlPullParser.START_TAG) {
+            // oops didn't find a start tag?!
+            if (DEBUG_BACKUP) {
+                Slog.e(TAG, "Didn't find start tag during restore");
+            }
+            return;
+        }
+        // this is supposed to be TAG_PREFERRED_BACKUP
+        if (!expectedStartTag.equals(parser.getName())) {
+            if (DEBUG_BACKUP) {
+                Slog.e(TAG, "Found unexpected tag " + parser.getName());
+            }
+            return;
+        }
+
+        // skip interfering stuff, then we're aligned with the backing implementation
+        while ((type = parser.next()) == XmlPullParser.TEXT) { }
+        functor.apply(parser, userId);
+    }
+
+    private interface BlobXmlRestorer {
+        void apply(TypedXmlPullParser parser, int userId)
+                throws IOException, XmlPullParserException;
+    }
+
+    public byte[] getPreferredActivityBackup(int userId) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the system may call getPreferredActivityBackup()");
+        }
+
+        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
+        try {
+            final TypedXmlSerializer serializer = Xml.newFastSerializer();
+            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TAG_PREFERRED_BACKUP);
+
+            synchronized (mPm.mLock) {
+                mPm.mSettings.writePreferredActivitiesLPr(serializer, userId, true);
+            }
+
+            serializer.endTag(null, TAG_PREFERRED_BACKUP);
+            serializer.endDocument();
+            serializer.flush();
+        } catch (Exception e) {
+            if (DEBUG_BACKUP) {
+                Slog.e(TAG, "Unable to write preferred activities for backup", e);
+            }
+            return null;
+        }
+
+        return dataStream.toByteArray();
+    }
+
+    public void restorePreferredActivities(byte[] backup, int userId) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the system may call restorePreferredActivities()");
+        }
+
+        try {
+            final TypedXmlPullParser parser = Xml.newFastPullParser();
+            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
+            restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
+                    (readParser, readUserId) -> {
+                        synchronized (mPm.mLock) {
+                            mPm.mSettings.readPreferredActivitiesLPw(readParser, readUserId);
+                        }
+                        updateDefaultHomeNotLocked(readUserId);
+                    });
+        } catch (Exception e) {
+            if (DEBUG_BACKUP) {
+                Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Non-Binder method, support for the backup/restore mechanism: write the
+     * default browser (etc) settings in its canonical XML format.  Returns the default
+     * browser XML representation as a byte array, or null if there is none.
+     */
+    public byte[] getDefaultAppsBackup(int userId) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the system may call getDefaultAppsBackup()");
+        }
+
+        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
+        try {
+            final TypedXmlSerializer serializer = Xml.newFastSerializer();
+            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TAG_DEFAULT_APPS);
+
+            synchronized (mPm.mLock) {
+                mPm.mSettings.writeDefaultAppsLPr(serializer, userId);
+            }
+
+            serializer.endTag(null, TAG_DEFAULT_APPS);
+            serializer.endDocument();
+            serializer.flush();
+        } catch (Exception e) {
+            if (DEBUG_BACKUP) {
+                Slog.e(TAG, "Unable to write default apps for backup", e);
+            }
+            return null;
+        }
+
+        return dataStream.toByteArray();
+    }
+
+    public void restoreDefaultApps(byte[] backup, int userId) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the system may call restoreDefaultApps()");
+        }
+
+        try {
+            final TypedXmlPullParser parser = Xml.newFastPullParser();
+            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
+            restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
+                    (parser1, userId1) -> {
+                        final String defaultBrowser;
+                        synchronized (mPm.mLock) {
+                            mPm.mSettings.readDefaultAppsLPw(parser1, userId1);
+                            defaultBrowser = mPm.mSettings.removeDefaultBrowserPackageNameLPw(
+                                    userId1);
+                        }
+                        if (defaultBrowser != null) {
+                            mPm.setDefaultBrowser(defaultBrowser, false, userId1);
+                        }
+                    });
+        } catch (Exception e) {
+            if (DEBUG_BACKUP) {
+                Slog.e(TAG, "Exception restoring default apps: " + e.getMessage());
+            }
+        }
+    }
+
+    public void resetApplicationPreferences(int userId) {
+        mPm.mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+        final long identity = Binder.clearCallingIdentity();
+        // writer
+        try {
+            final SparseBooleanArray changedUsers = new SparseBooleanArray();
+            synchronized (mPm.mLock) {
+                mPm.clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
+            }
+            if (changedUsers.size() > 0) {
+                mPm.postPreferredActivityChangedBroadcast(userId);
+            }
+            synchronized (mPm.mLock) {
+                mPm.mSettings.applyDefaultPreferredAppsLPw(userId);
+                mPm.mDomainVerificationManager.clearUser(userId);
+                final int numPackages = mPm.mPackages.size();
+                for (int i = 0; i < numPackages; i++) {
+                    final AndroidPackage pkg = mPm.mPackages.valueAt(i);
+                    mPm.mPermissionManager.resetRuntimePermissions(pkg, userId);
+                }
+            }
+            updateDefaultHomeNotLocked(userId);
+            resetNetworkPolicies(userId);
+            synchronized (mPm.mLock) {
+                mPm.scheduleWritePackageRestrictionsLocked(userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void resetNetworkPolicies(int userId) {
+        mPm.mInjector.getLocalService(NetworkPolicyManagerInternal.class).resetUserState(userId);
+    }
+
+    public int getPreferredActivities(List<IntentFilter> outFilters,
+            List<ComponentName> outActivities, String packageName) {
+        List<WatchedIntentFilter> temp =
+                WatchedIntentFilter.toWatchedIntentFilterList(outFilters);
+        final int result = getPreferredActivitiesInternal(
+                temp, outActivities, packageName);
+        outFilters.clear();
+        for (int i = 0; i < temp.size(); i++) {
+            outFilters.add(temp.get(i).getIntentFilter());
+        }
+        return result;
+    }
+
+    /**
+     * Variant that takes a {@link WatchedIntentFilter}
+     */
+    private int getPreferredActivitiesInternal(List<WatchedIntentFilter> outFilters,
+            List<ComponentName> outActivities, String packageName) {
+        final int callingUid = Binder.getCallingUid();
+        if (mPm.getInstantAppPackageName(callingUid) != null) {
+            return 0;
+        }
+        int num = 0;
+        final int userId = UserHandle.getCallingUserId();
+        // reader
+        synchronized (mPm.mLock) {
+            PreferredIntentResolver pir = mPm.mSettings.getPreferredActivities(userId);
+            if (pir != null) {
+                final Iterator<PreferredActivity> it = pir.filterIterator();
+                while (it.hasNext()) {
+                    final PreferredActivity pa = it.next();
+                    final String prefPackageName = pa.mPref.mComponent.getPackageName();
+                    if (packageName == null
+                            || (prefPackageName.equals(packageName) && pa.mPref.mAlways)) {
+                        if (mPm.shouldFilterApplicationLocked(
+                                mPm.mSettings.getPackageLPr(prefPackageName), callingUid, userId)) {
+                            continue;
+                        }
+                        if (outFilters != null) {
+                            outFilters.add(new WatchedIntentFilter(pa.getIntentFilter()));
+                        }
+                        if (outActivities != null) {
+                            outActivities.add(pa.mPref.mComponent);
+                        }
+                    }
+                }
+            }
+        }
+
+        return num;
+    }
+
+    public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) {
+        if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
+            throw new SecurityException(
+                    "findPersistentPreferredActivity can only be run by the system");
+        }
+        if (!mPm.mUserManager.exists(userId)) {
+            return null;
+        }
+        final int callingUid = Binder.getCallingUid();
+        intent = PackageManagerServiceUtils.updateIntentForResolve(intent);
+        final String resolvedType = intent.resolveTypeIfNeeded(mPm.mContext.getContentResolver());
+        final int flags = mPm.updateFlagsForResolve(
+                0, userId, callingUid, false /*includeInstantApps*/,
+                mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+                        0));
+        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
+                flags, userId);
+        synchronized (mPm.mLock) {
+            return mPm.findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
+                    userId);
+        }
+    }
+
+    /**
+     * Variant that takes a {@link WatchedIntentFilter}
+     */
+    public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
+            WatchedIntentFilter filter, int match, ComponentName activity) {
+        if (mPm.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+            return;
+        }
+        final int userId = UserHandle.getCallingUserId();
+        if (DEBUG_PREFERRED) {
+            Log.v(TAG, "setLastChosenActivity intent=" + intent
+                    + " resolvedType=" + resolvedType
+                    + " flags=" + flags
+                    + " filter=" + filter
+                    + " match=" + match
+                    + " activity=" + activity);
+            filter.dump(new PrintStreamPrinter(System.out), "    ");
+        }
+        intent.setComponent(null);
+        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
+                flags, userId);
+        // Find any earlier preferred or last chosen entries and nuke them
+        findPreferredActivityNotLocked(
+                intent, resolvedType, flags, query, false, true, false, userId);
+        // Add the new activity as the last chosen for this filter
+        addPreferredActivity(filter, match, null, activity, false, userId,
+                "Setting last chosen", false);
+    }
+
+    public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
+        if (mPm.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+            return null;
+        }
+        final int userId = UserHandle.getCallingUserId();
+        if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
+        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
+                flags, userId);
+        return findPreferredActivityNotLocked(
+                intent, resolvedType, flags, query, false, false, false, userId);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index bcf2f92..bb3ffb4 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -29,7 +29,10 @@
 
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.VersionedPackage;
 import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.incremental.IncrementalManager;
 import android.util.Log;
@@ -42,6 +45,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.utils.WatchedLongSparseArray;
 
 import java.io.File;
 import java.util.Collections;
@@ -168,7 +172,7 @@
             final int libraryNamesSize = pkg.getLibraryNames().size();
             for (i = 0; i < libraryNamesSize; i++) {
                 String name = pkg.getLibraryNames().get(i);
-                if (mPm.removeSharedLibraryLPw(name, 0)) {
+                if (removeSharedLibraryLPw(name, 0)) {
                     if (DEBUG_REMOVE && chatty) {
                         if (r == null) {
                             r = new StringBuilder(256);
@@ -185,7 +189,7 @@
 
         // Any package can hold static shared libraries.
         if (pkg.getStaticSharedLibName() != null) {
-            if (mPm.removeSharedLibraryLPw(pkg.getStaticSharedLibName(),
+            if (removeSharedLibraryLPw(pkg.getStaticSharedLibName(),
                     pkg.getStaticSharedLibVersion())) {
                 if (DEBUG_REMOVE && chatty) {
                     if (r == null) {
@@ -203,6 +207,44 @@
         }
     }
 
+    private boolean removeSharedLibraryLPw(String name, long version) {
+        WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(name);
+        if (versionedLib == null) {
+            return false;
+        }
+        final int libIdx = versionedLib.indexOfKey(version);
+        if (libIdx < 0) {
+            return false;
+        }
+        SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
+
+        // Remove the shared library overlays from its dependent packages.
+        for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
+            final List<VersionedPackage> dependents = mPm.getPackagesUsingSharedLibraryLPr(
+                    libraryInfo, 0, Process.SYSTEM_UID, currentUserId);
+            if (dependents == null) {
+                continue;
+            }
+            for (VersionedPackage dependentPackage : dependents) {
+                final PackageSetting ps = mPm.mSettings.getPackageLPr(
+                        dependentPackage.getPackageName());
+                if (ps != null) {
+                    ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
+                }
+            }
+        }
+
+        versionedLib.remove(version);
+        if (versionedLib.size() <= 0) {
+            mPm.mSharedLibraries.remove(name);
+            if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
+                mPm.mStaticLibsByDeclaringPackage.remove(libraryInfo.getDeclaringPackage()
+                        .getPackageName());
+            }
+        }
+        return true;
+    }
+
     /*
      * This method deletes the package from internal data structures. If the DELETE_KEEP_DATA
      * flag is not set, the data directory is removed as well.
@@ -274,7 +316,9 @@
                 mPm.mSettings.removeRenamedPackageLPw(deletedPs.getRealName());
             }
             if (changedUsers.size() > 0) {
-                mPm.updateDefaultHomeNotLocked(changedUsers);
+                final PreferredActivityHelper preferredActivityHelper =
+                        new PreferredActivityHelper(mPm);
+                preferredActivityHelper.updateDefaultHomeNotLocked(changedUsers);
                 mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
             }
         }
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
new file mode 100644
index 0000000..f5010e4
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -0,0 +1,709 @@
+/*
+ * 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.server.pm;
+
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
+import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.AuxiliaryResolveInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.app.ResolverActivity;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+final class ResolveIntentHelper {
+    private final PackageManagerService mPm;
+    private final PreferredActivityHelper mPreferredActivityHelper;
+
+    // TODO(b/198166813): remove PMS dependency
+    ResolveIntentHelper(PackageManagerService pm, PreferredActivityHelper preferredActivityHelper) {
+        mPm = pm;
+        mPreferredActivityHelper = preferredActivityHelper;
+    }
+
+    /**
+     * Normally instant apps can only be resolved when they're visible to the caller.
+     * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
+     * since we need to allow the system to start any installed application.
+     */
+    public ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags,
+            @PackageManagerInternal.PrivateResolveFlags int privateResolveFlags, int userId,
+            boolean resolveForStart, int filterCallingUid) {
+        try {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
+
+            if (!mPm.mUserManager.exists(userId)) return null;
+            final int callingUid = Binder.getCallingUid();
+            flags = mPm.updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+                    mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+                            resolvedType, flags));
+            mPm.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                    false /*checkShell*/, "resolve intent");
+
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
+            final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
+                    flags, privateResolveFlags, filterCallingUid, userId, resolveForStart,
+                    true /*allowDynamicSplits*/);
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+            final boolean queryMayBeFiltered =
+                    UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
+                            && !resolveForStart;
+
+            final ResolveInfo bestChoice =
+                    chooseBestActivity(
+                            intent, resolvedType, flags, privateResolveFlags, query, userId,
+                            queryMayBeFiltered);
+            final boolean nonBrowserOnly =
+                    (privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
+            if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
+                return null;
+            }
+            return bestChoice;
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
+            int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
+            boolean queryMayBeFiltered) {
+        if (query != null) {
+            final int n = query.size();
+            if (n == 1) {
+                return query.get(0);
+            } else if (n > 1) {
+                final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
+                // If there is more than one activity with the same priority,
+                // then let the user decide between them.
+                ResolveInfo r0 = query.get(0);
+                ResolveInfo r1 = query.get(1);
+                if (DEBUG_INTENT_MATCHING || debug) {
+                    Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs "
+                            + r1.activityInfo.name + "=" + r1.priority);
+                }
+                // If the first activity has a higher priority, or a different
+                // default, then it is always desirable to pick it.
+                if (r0.priority != r1.priority
+                        || r0.preferredOrder != r1.preferredOrder
+                        || r0.isDefault != r1.isDefault) {
+                    return query.get(0);
+                }
+                // If we have saved a preference for a preferred activity for
+                // this Intent, use that.
+                ResolveInfo ri = mPreferredActivityHelper.findPreferredActivityNotLocked(intent,
+                        resolvedType, flags, query, true, false, debug, userId,
+                        queryMayBeFiltered);
+                if (ri != null) {
+                    return ri;
+                }
+                int browserCount = 0;
+                for (int i = 0; i < n; i++) {
+                    ri = query.get(i);
+                    if (ri.handleAllWebDataURI) {
+                        browserCount++;
+                    }
+                    // If we have an ephemeral app, use it
+                    if (ri.activityInfo.applicationInfo.isInstantApp()) {
+                        final String packageName = ri.activityInfo.packageName;
+                        final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+                        if (ps != null && PackageManagerServiceUtils.hasAnyDomainApproval(
+                                mPm.mDomainVerificationManager, ps, intent, flags, userId)) {
+                            return ri;
+                        }
+                    }
+                }
+                if ((privateResolveFlags
+                        & PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
+                    return null;
+                }
+                ri = new ResolveInfo(mPm.getResolveInfo());
+                // if all resolve options are browsers, mark the resolver's info as if it were
+                // also a browser.
+                ri.handleAllWebDataURI = browserCount == n;
+                ri.activityInfo = new ActivityInfo(ri.activityInfo);
+                ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
+                // If all of the options come from the same package, show the application's
+                // label and icon instead of the generic resolver's.
+                // Some calls like Intent.resolveActivityInfo query the ResolveInfo from here
+                // and then throw away the ResolveInfo itself, meaning that the caller loses
+                // the resolvePackageName. Therefore the activityInfo.labelRes above provides
+                // a fallback for this case; we only set the target package's resources on
+                // the ResolveInfo, not the ActivityInfo.
+                final String intentPackage = intent.getPackage();
+                if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
+                    final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
+                    ri.resolvePackageName = intentPackage;
+                    if (mPm.userNeedsBadging(userId)) {
+                        ri.noResourceId = true;
+                    } else {
+                        ri.icon = appi.icon;
+                    }
+                    ri.iconResourceId = appi.icon;
+                    ri.labelRes = appi.labelRes;
+                }
+                ri.activityInfo.applicationInfo = new ApplicationInfo(
+                        ri.activityInfo.applicationInfo);
+                if (userId != 0) {
+                    ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
+                            UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
+                }
+                // Make sure that the resolver is displayable in car mode
+                if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();
+                ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);
+                return ri;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Return true if the given list is not empty and all of its contents have
+     * an activityInfo with the given package name.
+     */
+    private boolean allHavePackage(List<ResolveInfo> list, String packageName) {
+        if (ArrayUtils.isEmpty(list)) {
+            return false;
+        }
+        for (int i = 0, n = list.size(); i < n; i++) {
+            final ResolveInfo ri = list.get(i);
+            final ActivityInfo ai = ri != null ? ri.activityInfo : null;
+            if (ai == null || !packageName.equals(ai.packageName)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+            String featureId, int userId) throws RemoteException {
+        Objects.requireNonNull(packageName);
+        final int callingUid = Binder.getCallingUid();
+        mPm.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+                false /* checkShell */, "get launch intent sender for package");
+        final int packageUid = mPm.getPackageUid(callingPackage, 0 /* flags */, userId);
+        if (!UserHandle.isSameApp(callingUid, packageUid)) {
+            throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
+                    + callingUid + " does not own package: " + callingPackage);
+        }
+
+        // Using the same implementation with the #getLaunchIntentForPackage to get the ResolveInfo.
+        // Pass the resolveForStart as true in queryIntentActivities to skip the app filtering.
+        final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+        intentToResolve.addCategory(Intent.CATEGORY_INFO);
+        intentToResolve.setPackage(packageName);
+        String resolvedType = intentToResolve.resolveTypeIfNeeded(
+                mPm.mContext.getContentResolver());
+        List<ResolveInfo> ris = mPm.queryIntentActivitiesInternal(intentToResolve, resolvedType,
+                0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+                true /* resolveForStart */, false /* allowDynamicSplits */);
+        if (ris == null || ris.size() <= 0) {
+            intentToResolve.removeCategory(Intent.CATEGORY_INFO);
+            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+            intentToResolve.setPackage(packageName);
+            resolvedType = intentToResolve.resolveTypeIfNeeded(mPm.mContext.getContentResolver());
+            ris = mPm.queryIntentActivitiesInternal(intentToResolve, resolvedType,
+                    0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+                    true /* resolveForStart */, false /* allowDynamicSplits */);
+        }
+
+        final Intent intent = new Intent(intentToResolve);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // For the case of empty result, no component name is assigned into the intent. A
+        // non-launchable IntentSender which contains the failed intent is created. The
+        // SendIntentException is thrown if the IntentSender#sendIntent is invoked.
+        if (ris != null && !ris.isEmpty()) {
+            intent.setClassName(ris.get(0).activityInfo.packageName,
+                    ris.get(0).activityInfo.name);
+        }
+        final IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
+                ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                featureId, null /* token */, null /* resultWho */,
+                1 /* requestCode */, new Intent[]{intent},
+                resolvedType != null ? new String[]{resolvedType} : null,
+                PendingIntent.FLAG_IMMUTABLE, null /* bOptions */, userId);
+        return new IntentSender(target);
+    }
+
+    // In this method, we have to know the actual calling UID, but in some cases Binder's
+    // call identity is removed, so the UID has to be passed in explicitly.
+    public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
+            String resolvedType, int flags, int userId, int filterCallingUid) {
+        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+        mPm.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
+                false /*checkShell*/, "query intent receivers");
+        final String instantAppPkgName = mPm.getInstantAppPackageName(filterCallingUid);
+        flags = mPm.updateFlagsForResolve(
+                flags, userId, filterCallingUid, false /*includeInstantApps*/,
+                mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+                        flags));
+        Intent originalIntent = null;
+        ComponentName comp = intent.getComponent();
+        if (comp == null) {
+            if (intent.getSelector() != null) {
+                originalIntent = intent;
+                intent = intent.getSelector();
+                comp = intent.getComponent();
+            }
+        }
+        List<ResolveInfo> list = Collections.emptyList();
+        if (comp != null) {
+            final ActivityInfo ai = mPm.getReceiverInfo(comp, flags, userId);
+            if (ai != null) {
+                // When specifying an explicit component, we prevent the activity from being
+                // used when either 1) the calling package is normal and the activity is within
+                // an instant application or 2) the calling package is ephemeral and the
+                // activity is not visible to instant applications.
+                final boolean matchInstantApp =
+                        (flags & PackageManager.MATCH_INSTANT) != 0;
+                final boolean matchVisibleToInstantAppOnly =
+                        (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+                final boolean matchExplicitlyVisibleOnly =
+                        (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
+                final boolean isCallerInstantApp =
+                        instantAppPkgName != null;
+                final boolean isTargetSameInstantApp =
+                        comp.getPackageName().equals(instantAppPkgName);
+                final boolean isTargetInstantApp =
+                        (ai.applicationInfo.privateFlags
+                                & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
+                final boolean isTargetVisibleToInstantApp =
+                        (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+                final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp
+                        && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
+                final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp
+                        || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
+                final boolean blockResolution =
+                        !isTargetSameInstantApp
+                                && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
+                                || (matchVisibleToInstantAppOnly && isCallerInstantApp
+                                && isTargetHiddenFromInstantApp));
+                if (!blockResolution) {
+                    ResolveInfo ri = new ResolveInfo();
+                    ri.activityInfo = ai;
+                    list = new ArrayList<>(1);
+                    list.add(ri);
+                    PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
+                            mPm.mInjector.getCompatibility(), mPm.mComponentResolver,
+                            list, true, intent, resolvedType, filterCallingUid);
+                }
+            }
+        } else {
+            // reader
+            synchronized (mPm.mLock) {
+                String pkgName = intent.getPackage();
+                if (pkgName == null) {
+                    final List<ResolveInfo> result = mPm.mComponentResolver.queryReceivers(
+                            intent, resolvedType, flags, userId);
+                    if (result != null) {
+                        list = result;
+                    }
+                }
+                final AndroidPackage pkg = mPm.mPackages.get(pkgName);
+                if (pkg != null) {
+                    final List<ResolveInfo> result = mPm.mComponentResolver.queryReceivers(
+                            intent, resolvedType, flags, pkg.getReceivers(), userId);
+                    if (result != null) {
+                        list = result;
+                    }
+                }
+            }
+        }
+
+        if (originalIntent != null) {
+            // We also have to ensure all components match the original intent
+            PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
+                    mPm.mInjector.getCompatibility(), mPm.mComponentResolver,
+                    list, true, originalIntent, resolvedType, filterCallingUid);
+        }
+
+        return mPm.applyPostResolutionFilter(
+                list, instantAppPkgName, false, filterCallingUid, false, userId, intent);
+    }
+
+
+    public ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
+            int userId, int callingUid) {
+        if (!mPm.mUserManager.exists(userId)) return null;
+        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
+        List<ResolveInfo> query = mPm.queryIntentServicesInternal(
+                intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
+        if (query != null) {
+            if (query.size() >= 1) {
+                // If there is more than one service with the same priority,
+                // just arbitrarily pick the first one.
+                return query.get(0);
+            }
+        }
+        return null;
+    }
+
+    public @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
+            Intent intent, String resolvedType, int flags, int userId) {
+        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+        final int callingUid = Binder.getCallingUid();
+        final String instantAppPkgName = mPm.getInstantAppPackageName(callingUid);
+        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
+        ComponentName comp = intent.getComponent();
+        if (comp == null) {
+            if (intent.getSelector() != null) {
+                intent = intent.getSelector();
+                comp = intent.getComponent();
+            }
+        }
+        if (comp != null) {
+            final List<ResolveInfo> list = new ArrayList<>(1);
+            final ProviderInfo pi = mPm.getProviderInfo(comp, flags, userId);
+            if (pi != null) {
+                // When specifying an explicit component, we prevent the provider from being
+                // used when either 1) the provider is in an instant application and the
+                // caller is not the same instant application or 2) the calling package is an
+                // instant application and the provider is not visible to instant applications.
+                final boolean matchInstantApp =
+                        (flags & PackageManager.MATCH_INSTANT) != 0;
+                final boolean matchVisibleToInstantAppOnly =
+                        (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+                final boolean isCallerInstantApp =
+                        instantAppPkgName != null;
+                final boolean isTargetSameInstantApp =
+                        comp.getPackageName().equals(instantAppPkgName);
+                final boolean isTargetInstantApp =
+                        (pi.applicationInfo.privateFlags
+                                & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
+                final boolean isTargetHiddenFromInstantApp =
+                        (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
+                final boolean blockResolution =
+                        !isTargetSameInstantApp
+                                && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
+                                || (matchVisibleToInstantAppOnly && isCallerInstantApp
+                                && isTargetHiddenFromInstantApp));
+                final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
+                        && mPm.shouldFilterApplicationLocked(
+                        mPm.getPackageSettingInternal(pi.applicationInfo.packageName,
+                                Process.SYSTEM_UID), callingUid, userId);
+                if (!blockResolution && !blockNormalResolution) {
+                    final ResolveInfo ri = new ResolveInfo();
+                    ri.providerInfo = pi;
+                    list.add(ri);
+                }
+            }
+            return list;
+        }
+
+        // reader
+        synchronized (mPm.mLock) {
+            String pkgName = intent.getPackage();
+            if (pkgName == null) {
+                final List<ResolveInfo> resolveInfos = mPm.mComponentResolver.queryProviders(intent,
+                        resolvedType, flags, userId);
+                if (resolveInfos == null) {
+                    return Collections.emptyList();
+                }
+                return applyPostContentProviderResolutionFilter(
+                        resolveInfos, instantAppPkgName, userId, callingUid);
+            }
+            final AndroidPackage pkg = mPm.mPackages.get(pkgName);
+            if (pkg != null) {
+                final List<ResolveInfo> resolveInfos = mPm.mComponentResolver.queryProviders(intent,
+                        resolvedType, flags,
+                        pkg.getProviders(), userId);
+                if (resolveInfos == null) {
+                    return Collections.emptyList();
+                }
+                return applyPostContentProviderResolutionFilter(
+                        resolveInfos, instantAppPkgName, userId, callingUid);
+            }
+            return Collections.emptyList();
+        }
+    }
+
+    private List<ResolveInfo> applyPostContentProviderResolutionFilter(
+            List<ResolveInfo> resolveInfos, String instantAppPkgName,
+            @UserIdInt int userId, int callingUid) {
+        for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+            final ResolveInfo info = resolveInfos.get(i);
+
+            if (instantAppPkgName == null) {
+                SettingBase callingSetting =
+                        mPm.mSettings.getSettingLPr(UserHandle.getAppId(callingUid));
+                PackageSetting resolvedSetting =
+                        mPm.getPackageSettingInternal(info.providerInfo.packageName, 0);
+                if (!mPm.mAppsFilter.shouldFilterApplication(
+                        callingUid, callingSetting, resolvedSetting, userId)) {
+                    continue;
+                }
+            }
+
+            final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp();
+            // allow providers that are defined in the provided package
+            if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) {
+                if (info.providerInfo.splitName != null
+                        && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames,
+                        info.providerInfo.splitName)) {
+                    if (mPm.mInstantAppInstallerActivity == null) {
+                        if (DEBUG_INSTANT) {
+                            Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
+                        }
+                        resolveInfos.remove(i);
+                        continue;
+                    }
+                    // requested provider is defined in a split that hasn't been installed yet.
+                    // add the installer to the resolve list
+                    if (DEBUG_INSTANT) {
+                        Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+                    }
+                    final ResolveInfo installerInfo = new ResolveInfo(
+                            mPm.getInstantAppInstallerInfo());
+                    installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
+                            null /*failureActivity*/,
+                            info.providerInfo.packageName,
+                            info.providerInfo.applicationInfo.longVersionCode,
+                            info.providerInfo.splitName);
+                    // add a non-generic filter
+                    installerInfo.filter = new IntentFilter();
+                    // load resources from the correct package
+                    installerInfo.resolvePackageName = info.getComponentInfo().packageName;
+                    resolveInfos.set(i, installerInfo);
+                }
+                continue;
+            }
+            // allow providers that have been explicitly exposed to instant applications
+            if (!isEphemeralApp && (
+                    (info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
+                continue;
+            }
+            resolveInfos.remove(i);
+        }
+        return resolveInfos;
+    }
+
+    public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
+            Intent[] specifics, String[] specificTypes, Intent intent,
+            String resolvedType, int flags, int userId) {
+        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+        final int callingUid = Binder.getCallingUid();
+        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
+                        flags));
+        mPm.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+                false /*checkShell*/, "query intent activity options");
+        final String resultsAction = intent.getAction();
+
+        final List<ResolveInfo> results = mPm.queryIntentActivitiesInternal(intent, resolvedType,
+                flags | PackageManager.GET_RESOLVED_FILTER, userId);
+
+        if (DEBUG_INTENT_MATCHING) {
+            Log.v(TAG, "Query " + intent + ": " + results);
+        }
+
+        int specificsPos = 0;
+        int N;
+
+        // todo: note that the algorithm used here is O(N^2).  This
+        // isn't a problem in our current environment, but if we start running
+        // into situations where we have more than 5 or 10 matches then this
+        // should probably be changed to something smarter...
+
+        // First we go through and resolve each of the specific items
+        // that were supplied, taking care of removing any corresponding
+        // duplicate items in the generic resolve list.
+        if (specifics != null) {
+            for (int i = 0; i < specifics.length; i++) {
+                final Intent sintent = specifics[i];
+                if (sintent == null) {
+                    continue;
+                }
+
+                if (DEBUG_INTENT_MATCHING) {
+                    Log.v(TAG, "Specific #" + i + ": " + sintent);
+                }
+
+                String action = sintent.getAction();
+                if (resultsAction != null && resultsAction.equals(action)) {
+                    // If this action was explicitly requested, then don't
+                    // remove things that have it.
+                    action = null;
+                }
+
+                ResolveInfo ri = null;
+                ActivityInfo ai = null;
+
+                ComponentName comp = sintent.getComponent();
+                if (comp == null) {
+                    ri = mPm.resolveIntent(
+                            sintent,
+                            specificTypes != null ? specificTypes[i] : null,
+                            flags, userId);
+                    if (ri == null) {
+                        continue;
+                    }
+                    if (ri == mPm.getResolveInfo()) {
+                        // ACK!  Must do something better with this.
+                    }
+                    ai = ri.activityInfo;
+                    comp = new ComponentName(ai.applicationInfo.packageName,
+                            ai.name);
+                } else {
+                    ai = mPm.getActivityInfo(comp, flags, userId);
+                    if (ai == null) {
+                        continue;
+                    }
+                }
+
+                // Look for any generic query activities that are duplicates
+                // of this specific one, and remove them from the results.
+                if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Specific #" + i + ": " + ai);
+                N = results.size();
+                int j;
+                for (j = specificsPos; j < N; j++) {
+                    ResolveInfo sri = results.get(j);
+                    if ((sri.activityInfo.name.equals(comp.getClassName())
+                            && sri.activityInfo.applicationInfo.packageName.equals(
+                            comp.getPackageName()))
+                            || (action != null && sri.filter.matchAction(action))) {
+                        results.remove(j);
+                        if (DEBUG_INTENT_MATCHING) {
+                            Log.v(
+                                    TAG, "Removing duplicate item from " + j
+                                            + " due to specific " + specificsPos);
+                        }
+                        if (ri == null) {
+                            ri = sri;
+                        }
+                        j--;
+                        N--;
+                    }
+                }
+
+                // Add this specific item to its proper place.
+                if (ri == null) {
+                    ri = new ResolveInfo();
+                    ri.activityInfo = ai;
+                }
+                results.add(specificsPos, ri);
+                ri.specificIndex = i;
+                specificsPos++;
+            }
+        }
+
+        // Now we go through the remaining generic results and remove any
+        // duplicate actions that are found here.
+        N = results.size();
+        for (int i = specificsPos; i < N - 1; i++) {
+            final ResolveInfo rii = results.get(i);
+            if (rii.filter == null) {
+                continue;
+            }
+
+            // Iterate over all of the actions of this result's intent
+            // filter...  typically this should be just one.
+            final Iterator<String> it = rii.filter.actionsIterator();
+            if (it == null) {
+                continue;
+            }
+            while (it.hasNext()) {
+                final String action = it.next();
+                if (resultsAction != null && resultsAction.equals(action)) {
+                    // If this action was explicitly requested, then don't
+                    // remove things that have it.
+                    continue;
+                }
+                for (int j = i + 1; j < N; j++) {
+                    final ResolveInfo rij = results.get(j);
+                    if (rij.filter != null && rij.filter.hasAction(action)) {
+                        results.remove(j);
+                        if (DEBUG_INTENT_MATCHING) {
+                            Log.v(
+                                    TAG, "Removing duplicate item from " + j
+                                            + " due to action " + action + " at " + i);
+                        }
+                        j--;
+                        N--;
+                    }
+                }
+            }
+
+            // If the caller didn't request filter information, drop it now
+            // so we don't have to marshall/unmarshall it.
+            if ((flags & PackageManager.GET_RESOLVED_FILTER) == 0) {
+                rii.filter = null;
+            }
+        }
+
+        // Filter out the caller activity if so requested.
+        if (caller != null) {
+            N = results.size();
+            for (int i = 0; i < N; i++) {
+                ActivityInfo ainfo = results.get(i).activityInfo;
+                if (caller.getPackageName().equals(ainfo.applicationInfo.packageName)
+                        && caller.getClassName().equals(ainfo.name)) {
+                    results.remove(i);
+                    break;
+                }
+            }
+        }
+
+        // If the caller didn't request filter information,
+        // drop them now so we don't have to
+        // marshall/unmarshall it.
+        if ((flags & PackageManager.GET_RESOLVED_FILTER) == 0) {
+            N = results.size();
+            for (int i = 0; i < N; i++) {
+                results.get(i).filter = null;
+            }
+        }
+
+        if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Result: " + results);
+        return results;
+    }
+
+}
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index ef06c58..8018011 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -65,6 +65,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ComponentMutateUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.component.ParsedProcess;
@@ -115,12 +116,22 @@
 /**
  * Helper class that handles package scanning logic
  */
-public final class ScanPackageHelper {
+final class ScanPackageHelper {
     final PackageManagerService mPm;
+    final InstallPackageHelper mInstallPackageHelper;
+    final PackageManagerServiceInjector mInjector;
 
     // TODO(b/198166813): remove PMS dependency
     public ScanPackageHelper(PackageManagerService pm) {
         mPm = pm;
+        mInjector = pm.mInjector;
+        mInstallPackageHelper = new InstallPackageHelper(mPm, mInjector);
+    }
+
+    ScanPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
+        mPm = pm;
+        mInjector = injector;
+        mInstallPackageHelper = new InstallPackageHelper(mPm, injector);
     }
 
     /**
@@ -197,7 +208,7 @@
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
         final ParsedPackage parsedPackage;
-        try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) {
+        try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
             parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -208,8 +219,7 @@
             PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
         }
 
-        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
-                mPm.mPlatformPackage, mPm.mIsUpgrade, mPm.mIsPreNMR1Upgrade);
+        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
     }
 
     // TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
@@ -248,7 +258,7 @@
             } else {
                 isUpdatedSystemApp = disabledPkgSetting != null;
             }
-            applyPolicy(parsedPackage, scanFlags, mPm.mPlatformPackage, isUpdatedSystemApp);
+            applyPolicy(parsedPackage, scanFlags, mPm.getPlatformPackage(), isUpdatedSystemApp);
             assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
 
             SharedUserSetting sharedUserSetting = null;
@@ -264,14 +274,14 @@
                     }
                 }
             }
-            String platformPackageName = mPm.mPlatformPackage == null
-                    ? null : mPm.mPlatformPackage.getPackageName();
+            String platformPackageName = mPm.getPlatformPackage() == null
+                    ? null : mPm.getPlatformPackage().getPackageName();
             final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
                     pkgSetting == null ? null : pkgSetting.getPkg(), pkgSetting, disabledPkgSetting,
                     originalPkgSetting, realPkgName, parseFlags, scanFlags,
                     Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
                     cpuAbiOverride);
-            return scanPackageOnlyLI(request, mPm.mInjector, mPm.mFactoryTest, currentTime);
+            return scanPackageOnlyLI(request, mInjector, mPm.mFactoryTest, currentTime);
         }
     }
 
@@ -756,8 +766,7 @@
     public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
             @ParsingPackageUtils.ParseFlags int parseFlags,
             @PackageManagerService.ScanFlags int scanFlags, long currentTime,
-            @Nullable UserHandle user, AndroidPackage platformPackage, boolean isUpgrade,
-            boolean isPreNMR1Upgrade) throws PackageManagerException {
+            @Nullable UserHandle user) throws PackageManagerException {
         final boolean scanSystemPartition =
                 (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
         final String renamedPkgName;
@@ -765,8 +774,11 @@
         final boolean isSystemPkgUpdated;
         final boolean pkgAlreadyExists;
         PackageSetting pkgSetting;
+        AndroidPackage platformPackage;
+        final boolean isUpgrade = mPm.isDeviceUpgrading();
 
         synchronized (mPm.mLock) {
+            platformPackage = mPm.getPlatformPackage();
             renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
                     AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
             final String realPkgName = getRealPackageName(parsedPackage,
@@ -815,8 +827,8 @@
                 if (isSystemPkgUpdated) {
                     // we're updating the disabled package, so, scan it as the package setting
                     boolean isPlatformPackage = platformPackage != null
-                            && Objects.equals(platformPackage.getPackageName(),
-                            parsedPackage.getPackageName());
+                            && platformPackage.getPackageName().equals(
+                                    parsedPackage.getPackageName());
                     final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
                             null, disabledPkgSetting /* pkgSetting */,
                             null /* disabledPkgSetting */, null /* originalPkgSetting */,
@@ -824,7 +836,7 @@
                     applyPolicy(parsedPackage, scanFlags,
                             platformPackage, true);
                     final ScanResult scanResult =
-                            scanPackageOnlyLI(request, mPm.mInjector,
+                            scanPackageOnlyLI(request, mInjector,
                                     mPm.mFactoryTest, -1L);
                     if (scanResult.mExistingSettingCopied
                             && scanResult.mRequest.mPkgSetting != null) {
@@ -858,9 +870,9 @@
                             + "; " + pkgSetting.getPathString()
                             + " --> " + parsedPackage.getPath());
 
-            final InstallArgs args = mPm.createInstallArgsForExisting(
+            final InstallArgs args = new FileInstallArgs(
                     pkgSetting.getPathString(), getAppDexInstructionSets(
-                            pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()));
+                            pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
             args.cleanUpResourcesLI();
             synchronized (mPm.mLock) {
                 mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
@@ -900,8 +912,7 @@
         // TODO(b/136132412): skip for Incremental installation
         final boolean skipVerify = scanSystemPartition
                 || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
-        collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify,
-                isPreNMR1Upgrade);
+        collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
 
         // Reset profile if the application version is changed
         maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
@@ -944,9 +955,10 @@
                                 + parsedPackage.getLongVersionCode()
                                 + "; " + pkgSetting.getPathString() + " --> "
                                 + parsedPackage.getPath());
-                InstallArgs args = mPm.createInstallArgsForExisting(
+                InstallArgs args = new FileInstallArgs(
                         pkgSetting.getPathString(), getAppDexInstructionSets(
-                                pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()));
+                                pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
+                        mPm);
                 synchronized (mPm.mInstallLock) {
                     args.cleanUpResourcesLI();
                 }
@@ -973,7 +985,7 @@
                 try {
                     final String pkgName = scanResult.mPkgSetting.getPackageName();
                     final Map<String, ReconciledPackage> reconcileResult =
-                            mPm.reconcilePackagesLocked(
+                            mInstallPackageHelper.reconcilePackagesLocked(
                                     new ReconcileRequest(
                                             Collections.singletonMap(pkgName, scanResult),
                                             mPm.mSharedLibraries,
@@ -985,9 +997,9 @@
                                             Collections.singletonMap(pkgName,
                                                     mPm.getSharedLibLatestVersionSetting(
                                                             scanResult))),
-                                    mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
+                                    mPm.mSettings.getKeySetManagerService(), mInjector);
                     appIdCreated = optimisticallyRegisterAppId(scanResult);
-                    mPm.commitReconciledScanResultLocked(
+                    mInstallPackageHelper.commitReconciledScanResultLocked(
                             reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
                 } catch (PackageManagerException e) {
                     if (appIdCreated) {
@@ -1063,11 +1075,10 @@
     }
 
     private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
-            boolean forceCollect, boolean skipVerify, boolean mIsPreNMR1Upgrade)
-            throws PackageManagerException {
+            boolean forceCollect, boolean skipVerify) throws PackageManagerException {
         // When upgrading from pre-N MR1, verify the package time stamp using the package
         // directory and not the APK file.
-        final long lastModifiedTime = mIsPreNMR1Upgrade
+        final long lastModifiedTime = mPm.isPreNMR1Upgrade()
                 ? new File(parsedPackage.getPath()).lastModified()
                 : getLastModifiedTime(parsedPackage);
         final Settings.VersionInfo settingsVersionForPackage =
@@ -1075,9 +1086,9 @@
         if (ps != null && !forceCollect
                 && ps.getPathString().equals(parsedPackage.getPath())
                 && ps.getLastModifiedTime() == lastModifiedTime
-                && !PackageManagerService.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
-                && !PackageManagerService.isRecoverSignatureUpdateNeeded(
-                settingsVersionForPackage)) {
+                && !InstallPackageHelper.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+                && !InstallPackageHelper.isRecoverSignatureUpdateNeeded(
+                        settingsVersionForPackage)) {
             if (ps.getSigningDetails().getSignatures() != null
                     && ps.getSigningDetails().getSignatures().length != 0
                     && ps.getSigningDetails().getSignatureSchemeVersion()
@@ -1238,7 +1249,7 @@
         synchronized (mPm.mLock) {
             // The special "android" package can only be defined once
             if (pkg.getPackageName().equals("android")) {
-                if (mPm.mAndroidApplication != null) {
+                if (mPm.getCoreAndroidApplication() != null) {
                     Slog.w(TAG, "*************************************************");
                     Slog.w(TAG, "Core android package being redefined.  Skipping.");
                     Slog.w(TAG, " codePath=" + pkg.getPath());
@@ -1389,7 +1400,7 @@
             // to the user-installed location. If we don't allow this change, any newer,
             // user-installed version of the application will be ignored.
             if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
-                if (mPm.mExpectingBetter.containsKey(pkg.getPackageName())) {
+                if (mPm.isExpectingBetter(pkg.getPackageName())) {
                     Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
                             + pkg.getPackageName());
                 } else {
@@ -1470,9 +1481,7 @@
                     if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
                         // This must be an update to a system overlay. Immutable overlays cannot be
                         // upgraded.
-                        Objects.requireNonNull(mPm.mOverlayConfig,
-                                "Parsing non-system dir before overlay configs are initialized");
-                        if (!mPm.mOverlayConfig.isMutable(pkg.getPackageName())) {
+                        if (!mPm.isOverlayMutable(pkg.getPackageName())) {
                             throw new PackageManagerException("Overlay "
                                     + pkg.getPackageName()
                                     + " is static and cannot be upgraded.");
@@ -1786,7 +1795,7 @@
             final ParsedActivity component = pkg.getActivities().get(i);
             final Boolean enabled = componentsEnabledStates.get(component.getName());
             if (enabled != null) {
-                component.setEnabled(enabled);
+                ComponentMutateUtils.setEnabled(component, enabled);
             }
         }
 
@@ -1794,7 +1803,7 @@
             final ParsedActivity component = pkg.getReceivers().get(i);
             final Boolean enabled = componentsEnabledStates.get(component.getName());
             if (enabled != null) {
-                component.setEnabled(enabled);
+                ComponentMutateUtils.setEnabled(component, enabled);
             }
         }
 
@@ -1802,7 +1811,7 @@
             final ParsedProvider component = pkg.getProviders().get(i);
             final Boolean enabled = componentsEnabledStates.get(component.getName());
             if (enabled != null) {
-                component.setEnabled(enabled);
+                ComponentMutateUtils.setEnabled(component, enabled);
             }
         }
 
@@ -1810,7 +1819,7 @@
             final ParsedService component = pkg.getServices().get(i);
             final Boolean enabled = componentsEnabledStates.get(component.getName());
             if (enabled != null) {
-                component.setEnabled(enabled);
+                ComponentMutateUtils.setEnabled(component, enabled);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0902e4a..f89d9eb 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3002,17 +3002,6 @@
             }
         }
 
-        // If the build is setup to drop runtime permissions
-        // on update drop the files before loading them.
-        if (PackageManagerService.CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE) {
-            final VersionInfo internal = getInternalVersion();
-            if (!Build.FINGERPRINT.equals(internal.fingerprint)) {
-                for (UserInfo user : users) {
-                    mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(user.id);
-                }
-            }
-        }
-
         final int N = mPendingPackages.size();
 
         for (int i = 0; i < N; i++) {
@@ -3092,7 +3081,7 @@
                 for (int i=0; i<intents.size(); i++) {
                     Pair<String, ParsedIntentInfo> pair = intents.get(i);
                     applyDefaultPreferredActivityLPw(pmInternal,
-                            new WatchedIntentFilter(pair.second),
+                            pair.second.getIntentFilter(),
                             new ComponentName(ps.getPackageName(), pair.first), userId);
                 }
             }
@@ -3178,7 +3167,7 @@
     }
 
     private void applyDefaultPreferredActivityLPw(PackageManagerInternal pmInternal,
-            WatchedIntentFilter tmpPa, ComponentName cn, int userId) {
+            IntentFilter tmpPa, ComponentName cn, int userId) {
         // The initial preferences only specify the target activity
         // component and intent-filter, not the set of matches.  So we
         // now need to query for the matches to build the correct
@@ -3422,7 +3411,7 @@
                 PreferredActivity tmpPa = new PreferredActivity(parser);
                 if (tmpPa.mPref.getParseError() == null) {
                     applyDefaultPreferredActivityLPw(
-                            pmInternal, tmpPa, tmpPa.mPref.mComponent, userId);
+                            pmInternal, tmpPa.getIntentFilter(), tmpPa.mPref.mComponent, userId);
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Error in package manager settings: <preferred-activity> "
diff --git a/services/core/java/com/android/server/pm/SharedLibraryHelper.java b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
new file mode 100644
index 0000000..2582316
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
@@ -0,0 +1,356 @@
+/*
+ * 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.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.os.Build;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import libcore.util.HexEncoding;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+final class SharedLibraryHelper {
+    private static final boolean DEBUG_SHARED_LIBRARIES = false;
+
+    /**
+     * Apps targeting Android S and above need to declare dependencies to the public native
+     * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+     * in its {@code AndroidManifest.xml}.
+     *
+     * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+     * the package manager rejects to install the app. The dependency can be specified as optional
+     * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+     * dependency doesn't stop the installation.
+     * <p>Once installed, an app is provided with only the native shared libraries that are
+     * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+     * in the app manifest will fail even if it actually exists on the device.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
+
+    /**
+     * Compare the newly scanned package with current system state to see which of its declared
+     * shared libraries should be allowed to be added to the system.
+     */
+    public static List<SharedLibraryInfo> getAllowedSharedLibInfos(
+            ScanResult scanResult,
+            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
+        // Let's used the parsed package as scanResult.pkgSetting may be null
+        final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+        if (scanResult.mStaticSharedLibraryInfo == null
+                && scanResult.mDynamicSharedLibraryInfos == null) {
+            return null;
+        }
+
+        // Any app can add new static shared libraries
+        if (scanResult.mStaticSharedLibraryInfo != null) {
+            return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
+        }
+        final boolean hasDynamicLibraries = parsedPackage.isSystem()
+                && scanResult.mDynamicSharedLibraryInfos != null;
+        if (!hasDynamicLibraries) {
+            return null;
+        }
+        final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
+                .isUpdatedSystemApp();
+        // We may not yet have disabled the updated package yet, so be sure to grab the
+        // current setting if that's the case.
+        final PackageSetting updatedSystemPs = isUpdatedSystemApp
+                ? scanResult.mRequest.mDisabledPkgSetting == null
+                ? scanResult.mRequest.mOldPkgSetting
+                : scanResult.mRequest.mDisabledPkgSetting
+                : null;
+        if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
+                || updatedSystemPs.getPkg().getLibraryNames() == null)) {
+            Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+                    + " declares libraries that are not declared on the system image; skipping");
+            return null;
+        }
+        final ArrayList<SharedLibraryInfo> infos =
+                new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
+        for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
+            final String name = info.getName();
+            if (isUpdatedSystemApp) {
+                // New library entries can only be added through the
+                // system image.  This is important to get rid of a lot
+                // of nasty edge cases: for example if we allowed a non-
+                // system update of the app to add a library, then uninstalling
+                // the update would make the library go away, and assumptions
+                // we made such as through app install filtering would now
+                // have allowed apps on the device which aren't compatible
+                // with it.  Better to just have the restriction here, be
+                // conservative, and create many fewer cases that can negatively
+                // impact the user experience.
+                if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
+                    Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+                            + " declares library " + name
+                            + " that is not declared on system image; skipping");
+                    continue;
+                }
+            }
+            if (sharedLibExists(
+                    name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
+                Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
+                        + name + " that already exists; skipping");
+                continue;
+            }
+            infos.add(info);
+        }
+        return infos;
+    }
+
+    public static boolean sharedLibExists(final String name, final long version,
+            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) {
+        WatchedLongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
+        return versionedLib != null && versionedLib.indexOfKey(version) >= 0;
+    }
+
+    /**
+     * Returns false if the adding shared library already exists in the map and so could not be
+     * added.
+     */
+    public static boolean addSharedLibraryToPackageVersionMap(
+            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
+            SharedLibraryInfo library) {
+        final String name = library.getName();
+        if (target.containsKey(name)) {
+            if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
+                // We've already added this non-version-specific library to the map.
+                return false;
+            } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
+                // We've already added this version of a version-specific library to the map.
+                return false;
+            }
+        } else {
+            target.put(name, new WatchedLongSparseArray<>());
+        }
+        target.get(name).put(library.getLongVersion(), library);
+        return true;
+    }
+
+    public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
+            Map<String, AndroidPackage> availablePackages,
+            @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries,
+            PlatformCompat platformCompat) throws PackageManagerException {
+        if (pkg == null) {
+            return null;
+        }
+        // The collection used here must maintain the order of addition (so
+        // that libraries are searched in the correct order) and must have no
+        // duplicates.
+        ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
+        if (!pkg.getUsesLibraries().isEmpty()) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
+                    pkg.getPackageName(), true, pkg.getTargetSdkVersion(), null,
+                    availablePackages, existingLibraries, newLibraries);
+        }
+        if (!pkg.getUsesStaticLibraries().isEmpty()) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
+                    pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
+                    pkg.getPackageName(), true, pkg.getTargetSdkVersion(), usesLibraryInfos,
+                    availablePackages, existingLibraries, newLibraries);
+        }
+        if (!pkg.getUsesOptionalLibraries().isEmpty()) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(),
+                    null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+                    usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+        }
+        if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
+                pkg.getPackageName(), pkg.getTargetSdkVersion())) {
+            if (!pkg.getUsesNativeLibraries().isEmpty()) {
+                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+                        null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
+                        usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+            }
+            if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+                        null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+                        usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+            }
+        }
+        return usesLibraryInfos;
+    }
+
+    public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
+            @NonNull List<String> requestedLibraries,
+            @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
+            @NonNull String packageName, boolean required, int targetSdk,
+            @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
+            @NonNull final Map<String, AndroidPackage> availablePackages,
+            @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
+            throws PackageManagerException {
+        final int libCount = requestedLibraries.size();
+        for (int i = 0; i < libCount; i++) {
+            final String libName = requestedLibraries.get(i);
+            final long libVersion = requiredVersions != null ? requiredVersions[i]
+                    : SharedLibraryInfo.VERSION_UNDEFINED;
+            final SharedLibraryInfo libraryInfo =
+                    getSharedLibraryInfo(libName, libVersion, existingLibraries, newLibraries);
+            if (libraryInfo == null) {
+                if (required) {
+                    throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                            "Package " + packageName + " requires unavailable shared library "
+                                    + libName + "; failing!");
+                } else if (DEBUG_SHARED_LIBRARIES) {
+                    Slog.i(TAG, "Package " + packageName
+                            + " desires unavailable shared library "
+                            + libName + "; ignoring!");
+                }
+            } else {
+                if (requiredVersions != null && requiredCertDigests != null) {
+                    if (libraryInfo.getLongVersion() != requiredVersions[i]) {
+                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                "Package " + packageName + " requires unavailable static shared"
+                                        + " library " + libName + " version "
+                                        + libraryInfo.getLongVersion() + "; failing!");
+                    }
+                    AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
+                    SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
+                    if (libPkg == null) {
+                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                "Package " + packageName + " requires unavailable static shared"
+                                        + " library; failing!");
+                    }
+                    final String[] expectedCertDigests = requiredCertDigests[i];
+                    if (expectedCertDigests.length > 1) {
+                        // For apps targeting O MR1 we require explicit enumeration of all certs.
+                        final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
+                                ? PackageUtils.computeSignaturesSha256Digests(
+                                libPkg.getSignatures())
+                                : PackageUtils.computeSignaturesSha256Digests(
+                                        new Signature[]{libPkg.getSignatures()[0]});
+
+                        // Take a shortcut if sizes don't match. Note that if an app doesn't
+                        // target O we don't parse the "additional-certificate" tags similarly
+                        // how we only consider all certs only for apps targeting O (see above).
+                        // Therefore, the size check is safe to make.
+                        if (expectedCertDigests.length != libCertDigests.length) {
+                            throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                    "Package " + packageName + " requires differently signed"
+                                            + " static shared library; failing!");
+                        }
+
+                        // Use a predictable order as signature order may vary
+                        Arrays.sort(libCertDigests);
+                        Arrays.sort(expectedCertDigests);
+
+                        final int certCount = libCertDigests.length;
+                        for (int j = 0; j < certCount; j++) {
+                            if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
+                                throw new PackageManagerException(
+                                        INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                        "Package " + packageName + " requires differently signed"
+                                                + " static shared library; failing!");
+                            }
+                        }
+                    } else {
+                        // lib signing cert could have rotated beyond the one expected, check to see
+                        // if the new one has been blessed by the old
+                        byte[] digestBytes = HexEncoding.decode(
+                                expectedCertDigests[0], false /* allowSingleChar */);
+                        if (!libPkg.hasSha256Certificate(digestBytes)) {
+                            throw new PackageManagerException(
+                                    INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                    "Package " + packageName + " requires differently signed"
+                                            + " static shared library; failing!");
+                        }
+                    }
+                }
+                if (outUsedLibraries == null) {
+                    outUsedLibraries = new ArrayList<>();
+                }
+                outUsedLibraries.add(libraryInfo);
+            }
+        }
+        return outUsedLibraries;
+    }
+
+    @Nullable
+    public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
+            Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+            @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
+        if (newLibraries != null) {
+            final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
+            SharedLibraryInfo info = null;
+            if (versionedLib != null) {
+                info = versionedLib.get(version);
+            }
+            if (info != null) {
+                return info;
+            }
+        }
+        final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
+        if (versionedLib == null) {
+            return null;
+        }
+        return versionedLib.get(version);
+    }
+
+    public static List<SharedLibraryInfo> findSharedLibraries(PackageSetting pkgSetting) {
+        if (!pkgSetting.getPkgState().getUsesLibraryInfos().isEmpty()) {
+            ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
+            Set<String> collectedNames = new HashSet<>();
+            for (SharedLibraryInfo info : pkgSetting.getPkgState().getUsesLibraryInfos()) {
+                findSharedLibrariesRecursive(info, retValue, collectedNames);
+            }
+            return retValue;
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
+            ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
+        if (!collectedNames.contains(info.getName())) {
+            collectedNames.add(info.getName());
+            collected.add(info);
+
+            if (info.getDependencies() != null) {
+                for (SharedLibraryInfo dep : info.getDependencies()) {
+                    findSharedLibrariesRecursive(dep, collected, collectedNames);
+                }
+            }
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 3055747..3387737 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.parsing.component.ComponentMutateUtils;
 import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProcessImpl;
 import android.service.pm.PackageServiceDumpProto;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -126,15 +128,14 @@
 
     void addProcesses(Map<String, ParsedProcess> newProcs) {
         if (newProcs != null) {
-            final int numProcs = newProcs.size();
             for (String key : newProcs.keySet()) {
                 ParsedProcess newProc = newProcs.get(key);
                 ParsedProcess proc = processes.get(newProc.getName());
                 if (proc == null) {
-                    proc = new ParsedProcess(newProc);
+                    proc = new ParsedProcessImpl(newProc);
                     processes.put(newProc.getName(), proc);
                 } else {
-                    proc.addStateFrom(newProc);
+                    ComponentMutateUtils.addStateFrom(proc, newProc);
                 }
             }
             onChanged();
@@ -280,8 +281,7 @@
             this.processes.clear();
             this.processes.ensureCapacity(numProcs);
             for (int i = 0; i < numProcs; i++) {
-                ParsedProcess proc =
-                        new ParsedProcess(sharedUser.processes.valueAt(i));
+                ParsedProcess proc = new ParsedProcessImpl(sharedUser.processes.valueAt(i));
                 this.processes.put(proc.getName(), proc);
             }
         } else {
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index a82cb9e..b49c8da 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -132,7 +132,7 @@
         final AppDataHelper appDataHelper = new AppDataHelper(mPm);
         final ArrayList<PackageFreezer> freezers = new ArrayList<>();
         final ArrayList<AndroidPackage> loaded = new ArrayList<>();
-        final int parseFlags = mPm.mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
+        final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
 
         final Settings.VersionInfo ver;
         final List<PackageSetting> packages;
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index 3d2ffe1..c5569cd 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -65,7 +65,7 @@
 import android.os.UserManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
-import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
 
@@ -78,7 +78,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
+import java.util.Set;
 
 final class VerificationParams extends HandlerParams {
     /**
@@ -688,7 +688,7 @@
 
     private void sendVerificationCompleteNotification() {
         if (mParentVerificationParams != null) {
-            mParentVerificationParams.trySendVerificationCompleteNotification(this, mRet);
+            mParentVerificationParams.trySendVerificationCompleteNotification(this);
         } else {
             try {
                 mObserver.onPackageInstalled(null, mRet, mErrorMessage,
@@ -717,7 +717,7 @@
     static final class MultiPackageVerificationParams extends HandlerParams {
         private final IPackageInstallObserver2 mObserver;
         private final List<VerificationParams> mChildParams;
-        private final Map<VerificationParams, Integer> mVerificationState;
+        private final Set<VerificationParams> mVerificationState;
 
         MultiPackageVerificationParams(VerificationParams parent, List<VerificationParams> children,
                 PackageManagerService pm) throws PackageManagerException {
@@ -731,7 +731,7 @@
                 final VerificationParams childParams = children.get(i);
                 childParams.mParentVerificationParams = this;
             }
-            mVerificationState = new ArrayMap<>(mChildParams.size());
+            mVerificationState = new ArraySet<>(mChildParams.size());
             mObserver = parent.mObserver;
         }
 
@@ -749,18 +749,16 @@
             }
         }
 
-        void trySendVerificationCompleteNotification(VerificationParams child, int currentStatus) {
-            mVerificationState.put(child, currentStatus);
+        void trySendVerificationCompleteNotification(VerificationParams child) {
+            mVerificationState.add(child);
             if (mVerificationState.size() != mChildParams.size()) {
                 return;
             }
             int completeStatus = PackageManager.INSTALL_SUCCEEDED;
             String errorMsg = null;
-            for (VerificationParams params : mVerificationState.keySet()) {
+            for (VerificationParams params : mVerificationState) {
                 int status = params.mRet;
-                if (status == PackageManager.INSTALL_UNKNOWN) {
-                    return;
-                } else if (status != PackageManager.INSTALL_SUCCEEDED) {
+                if (status != PackageManager.INSTALL_SUCCEEDED) {
                     completeStatus = status;
                     errorMsg = params.mErrorMessage;
                     break;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 7598423..6846ac5 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -26,6 +26,7 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageImpl;
+import android.content.pm.parsing.component.ComponentMutateUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedProvider;
 import android.content.pm.parsing.component.ParsedService;
@@ -314,37 +315,37 @@
 
         int permissionsSize = permissions.size();
         for (int index = 0; index < permissionsSize; index++) {
-            permissions.get(index).setPackageName(this.packageName);
+            ComponentMutateUtils.setPackageName(permissions.get(index), this.packageName);
         }
 
         int permissionGroupsSize = permissionGroups.size();
         for (int index = 0; index < permissionGroupsSize; index++) {
-            permissionGroups.get(index).setPackageName(this.packageName);
+            ComponentMutateUtils.setPackageName(permissionGroups.get(index), this.packageName);
         }
 
         int activitiesSize = activities.size();
         for (int index = 0; index < activitiesSize; index++) {
-            activities.get(index).setPackageName(this.packageName);
+            ComponentMutateUtils.setPackageName(activities.get(index), this.packageName);
         }
 
         int receiversSize = receivers.size();
         for (int index = 0; index < receiversSize; index++) {
-            receivers.get(index).setPackageName(this.packageName);
+            ComponentMutateUtils.setPackageName(receivers.get(index), this.packageName);
         }
 
         int providersSize = providers.size();
         for (int index = 0; index < providersSize; index++) {
-            providers.get(index).setPackageName(this.packageName);
+            ComponentMutateUtils.setPackageName(providers.get(index), this.packageName);
         }
 
         int servicesSize = services.size();
         for (int index = 0; index < servicesSize; index++) {
-            services.get(index).setPackageName(this.packageName);
+            ComponentMutateUtils.setPackageName(services.get(index), this.packageName);
         }
 
         int instrumentationsSize = instrumentations.size();
         for (int index = 0; index < instrumentationsSize; index++) {
-            instrumentations.get(index).setPackageName(this.packageName);
+            ComponentMutateUtils.setPackageName(instrumentations.get(index), this.packageName);
         }
 
         return this;
@@ -354,22 +355,26 @@
     public PackageImpl setAllComponentsDirectBootAware(boolean allComponentsDirectBootAware) {
         int activitiesSize = activities.size();
         for (int index = 0; index < activitiesSize; index++) {
-            activities.get(index).setDirectBootAware(allComponentsDirectBootAware);
+            ComponentMutateUtils.setDirectBootAware(activities.get(index),
+                    allComponentsDirectBootAware);
         }
 
         int receiversSize = receivers.size();
         for (int index = 0; index < receiversSize; index++) {
-            receivers.get(index).setDirectBootAware(allComponentsDirectBootAware);
+            ComponentMutateUtils.setDirectBootAware(receivers.get(index),
+                    allComponentsDirectBootAware);
         }
 
         int providersSize = providers.size();
         for (int index = 0; index < providersSize; index++) {
-            providers.get(index).setDirectBootAware(allComponentsDirectBootAware);
+            ComponentMutateUtils.setDirectBootAware(providers.get(index),
+                    allComponentsDirectBootAware);
         }
 
         int servicesSize = services.size();
         for (int index = 0; index < servicesSize; index++) {
-            services.get(index).setDirectBootAware(allComponentsDirectBootAware);
+            ComponentMutateUtils.setDirectBootAware(services.get(index),
+                    allComponentsDirectBootAware);
         }
 
         return this;
@@ -434,7 +439,7 @@
         int size = permissionGroups.size();
         for (int index = size - 1; index >= 0; --index) {
             // TODO(b/135203078): Builder/immutability
-            permissionGroups.get(index).setPriority(0);
+            ComponentMutateUtils.setPriority(permissionGroups.get(index), 0);
         }
         return this;
     }
@@ -446,7 +451,7 @@
         for (int index = 0; index < receiversSize; index++) {
             ParsedActivity receiver = receivers.get(index);
             if ((receiver.getFlags() & ActivityInfo.FLAG_SINGLE_USER) != 0) {
-                receiver.setExported(false);
+                ComponentMutateUtils.setExported(receiver, false);
             }
         }
 
@@ -455,7 +460,7 @@
         for (int index = 0; index < servicesSize; index++) {
             ParsedService service = services.get(index);
             if ((service.getFlags() & ActivityInfo.FLAG_SINGLE_USER) != 0) {
-                service.setExported(false);
+                ComponentMutateUtils.setExported(service, false);
             }
         }
 
@@ -464,7 +469,7 @@
         for (int index = 0; index < providersSize; index++) {
             ParsedProvider provider = providers.get(index);
             if ((provider.getFlags() & ActivityInfo.FLAG_SINGLE_USER) != 0) {
-                provider.setExported(false);
+                ComponentMutateUtils.setExported(provider, false);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d54c571..e62988dd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -91,8 +91,10 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.SigningDetails;
+import android.content.pm.parsing.component.ComponentMutateUtils;
 import android.content.pm.parsing.component.ParsedPermission;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedPermissionUtils;
 import android.content.pm.permission.CompatibilityPermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
@@ -2287,7 +2289,7 @@
                 newPermissionNum++) {
             final ParsedPermission newPermission =
                     newPackage.getPermissions().get(newPermissionNum);
-            final int newProtection = newPermission.getProtection();
+            final int newProtection = ParsedPermissionUtils.getProtection(newPermission);
 
             if ((newProtection & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
                 final String permissionName = newPermission.getName();
@@ -2403,7 +2405,7 @@
             ParsedPermission p = pkg.getPermissions().get(i);
 
             // Assume by default that we did not install this permission into the system.
-            p.setFlags(p.getFlags() & ~PermissionInfo.FLAG_INSTALLED);
+            ComponentMutateUtils.setExactFlags(p, p.getFlags() & ~PermissionInfo.FLAG_INSTALLED);
 
             final PermissionInfo permissionInfo;
             final Permission oldPermission;
@@ -2413,7 +2415,8 @@
                 // permissions for one app being granted to someone just because they happen
                 // to be in a group defined by another app (before this had no implications).
                 if (pkg.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) {
-                    p.setParsedPermissionGroup(mRegistry.getPermissionGroup(p.getGroup()));
+                    ComponentMutateUtils.setParsedPermissionGroup(p,
+                            mRegistry.getPermissionGroup(p.getGroup()));
                     // Warn for a permission in an unknown group.
                     if (DEBUG_PERMISSIONS
                             && p.getGroup() != null && p.getParsedPermissionGroup() == null) {
@@ -2441,7 +2444,8 @@
                     mRegistry.addPermission(permission);
                 }
                 if (permission.isInstalled()) {
-                    p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
+                    ComponentMutateUtils.setExactFlags(p,
+                            p.getFlags() | PermissionInfo.FLAG_INSTALLED);
                 }
                 if (permission.isDefinitionChanged()) {
                     definitionChangedPermissions.add(p.getName());
@@ -2516,7 +2520,7 @@
                         r.append(p.getName());
                     }
                 }
-                if (p.isAppOp()) {
+                if (ParsedPermissionUtils.isAppOp(p)) {
                     // TODO(zhanghai): Should we just remove the entry for this permission directly?
                     mRegistry.removeAppOpPermissionPackage(p.getName(), pkg.getPackageName());
                 }
@@ -3451,7 +3455,7 @@
         for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
             final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
             if (info.getName().equals(perm)
-                    && pkg.getTargetSdkVersion() < info.sdkVersion) {
+                    && pkg.getTargetSdkVersion() < info.getSdkVersion()) {
                 allowed = true;
                 Log.i(TAG, "Auto-granting " + perm + " to old pkg "
                         + pkg.getPackageName());
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index a5ba82f..d47f510 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -187,7 +187,7 @@
                 for (int intentIndex = 0; intentIndex < intentsSize && !needsAutoVerify;
                         intentIndex++) {
                     ParsedIntentInfo intent = intents.get(intentIndex);
-                    needsAutoVerify = intent.needsVerification();
+                    needsAutoVerify = intent.getIntentFilter().needsVerification();
                 }
             }
 
@@ -205,10 +205,11 @@
             int intentsSize = intents.size();
             for (int intentIndex = 0; intentIndex < intentsSize && underMaxSize; intentIndex++) {
                 ParsedIntentInfo intent = intents.get(intentIndex);
-                if (intent.handlesWebUris(false)) {
-                    int authorityCount = intent.countDataAuthorities();
+                IntentFilter intentFilter = intent.getIntentFilter();
+                if (intentFilter.handlesWebUris(false)) {
+                    int authorityCount = intentFilter.countDataAuthorities();
                     for (int index = 0; index < authorityCount; index++) {
-                        String host = intent.getDataAuthority(index).getHost();
+                        String host = intentFilter.getDataAuthority(index).getHost();
                         if (isValidHost(host) == valid) {
                             totalSize += byteSizeOf(host);
                             underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
@@ -248,12 +249,13 @@
             int intentsSize = intents.size();
             for (int intentIndex = 0; intentIndex < intentsSize && underMaxSize; intentIndex++) {
                 ParsedIntentInfo intent = intents.get(intentIndex);
-                if (checkAutoVerify && !intent.getAutoVerify()) {
+                IntentFilter intentFilter = intent.getIntentFilter();
+                if (checkAutoVerify && !intentFilter.getAutoVerify()) {
                     continue;
                 }
 
-                if (!intent.hasCategory(Intent.CATEGORY_DEFAULT)
-                        || !intent.handlesWebUris(checkAutoVerify)) {
+                if (!intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)
+                        || !intentFilter.handlesWebUris(checkAutoVerify)) {
                     continue;
                 }
 
@@ -271,9 +273,9 @@
                 //  app will probably fail. This can be re-configured to work properly by the
                 //  app developer by declaring a separate intent-filter. This may not be worth
                 //  fixing.
-                int authorityCount = intent.countDataAuthorities();
+                int authorityCount = intentFilter.countDataAuthorities();
                 for (int index = 0; index < authorityCount && underMaxSize; index++) {
-                    String host = intent.getDataAuthority(index).getHost();
+                    String host = intentFilter.getDataAuthority(index).getHost();
                     if (isValidHost(host) == valid) {
                         totalSize += byteSizeOf(host);
                         underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 193b27d..61076ce 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -93,8 +93,10 @@
 import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;
 import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
 
+import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
@@ -105,6 +107,7 @@
 import android.app.UiModeManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -113,6 +116,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -191,6 +195,7 @@
 
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.accessibility.util.AccessibilityUtils;
 import com.android.internal.app.AssistUtils;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.logging.MetricsLogger;
@@ -230,6 +235,7 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * WindowManagerPolicy implementation for the Android phone UI.  This
@@ -308,6 +314,22 @@
 
     static final int PENDING_KEY_NULL = -1;
 
+    // Must match: config_shortPressOnStemPrimaryBehavior in config.xml
+    static final int SHORT_PRESS_PRIMARY_NOTHING = 0;
+    static final int SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS = 1;
+
+    // Must match: config_longPressOnStemPrimaryBehavior in config.xml
+    static final int LONG_PRESS_PRIMARY_NOTHING = 0;
+    static final int LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT = 1;
+
+    // Must match: config_doublePressOnStemPrimaryBehavior in config.xml
+    static final int DOUBLE_PRESS_PRIMARY_NOTHING = 0;
+    static final int DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP = 1;
+
+    // Must match: config_triplePressOnStemPrimaryBehavior in config.xml
+    static final int TRIPLE_PRESS_PRIMARY_NOTHING = 0;
+    static final int TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY = 1;
+
     static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
     static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
     static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
@@ -315,6 +337,8 @@
     static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
     static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
 
+    private static final String TALKBACK_LABEL = "TalkBack";
+
     private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -400,7 +424,6 @@
     private AccessibilityShortcutController mAccessibilityShortcutController;
 
     boolean mSafeMode;
-    private WindowState mKeyguardCandidate = null;
 
     // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
     // This is for car dock and this is updated from resource.
@@ -487,6 +510,10 @@
     int mShortPressOnSleepBehavior;
     int mShortPressOnWindowBehavior;
     int mPowerVolUpBehavior;
+    int mShortPressOnStemPrimaryBehavior;
+    int mDoublePressOnStemPrimaryBehavior;
+    int mTriplePressOnStemPrimaryBehavior;
+    int mLongPressOnStemPrimaryBehavior;
     boolean mHasSoftInput = false;
     boolean mHapticTextHandleEnabled;
     boolean mUseTvRouting;
@@ -1192,6 +1219,170 @@
         return mLongPressOnPowerBehavior;
     }
 
+    private void stemPrimaryPress(int count) {
+        if (DEBUG_INPUT) {
+            Slog.d(TAG, "stemPrimaryPress: " + count);
+        }
+        if (count == 3) {
+            stemPrimaryTriplePressAction(mTriplePressOnStemPrimaryBehavior);
+        } else if (count == 2) {
+            stemPrimaryDoublePressAction(mDoublePressOnStemPrimaryBehavior);
+        } else if (count == 1) {
+            stemPrimarySinglePressAction(mShortPressOnStemPrimaryBehavior);
+        }
+    }
+
+    private void stemPrimarySinglePressAction(int behavior) {
+        switch (behavior) {
+            case SHORT_PRESS_PRIMARY_NOTHING:
+                break;
+            case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
+                if (DEBUG_INPUT) {
+                    Slog.d(TAG, "Executing stem primary short press action behavior.");
+                }
+                final boolean keyguardActive =
+                        mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
+                if (!keyguardActive) {
+                    Intent intent = new Intent(Intent.ACTION_ALL_APPS);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                }
+                break;
+        }
+    }
+
+    private void stemPrimaryDoublePressAction(int behavior) {
+        switch (behavior) {
+            case DOUBLE_PRESS_PRIMARY_NOTHING:
+                break;
+            case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
+                if (DEBUG_INPUT) {
+                    Slog.d(TAG, "Executing stem primary double press action behavior.");
+                }
+                final boolean keyguardActive = mKeyguardDelegate == null
+                        ? false
+                        : mKeyguardDelegate.isShowing();
+                if (!keyguardActive) {
+                    switchRecentTask();
+                }
+                break;
+        }
+    }
+
+    private void stemPrimaryTriplePressAction(int behavior) {
+        switch (behavior) {
+            case TRIPLE_PRESS_PRIMARY_NOTHING:
+                break;
+            case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
+                if (DEBUG_INPUT) {
+                    Slog.d(TAG, "Executing stem primary triple press action behavior.");
+                }
+                toggleTalkBack();
+                break;
+        }
+    }
+
+    private void stemPrimaryLongPress() {
+        if (DEBUG_INPUT) {
+            Slog.d(TAG, "Executing stem primary long press action behavior.");
+        }
+
+        switch (mLongPressOnStemPrimaryBehavior) {
+            case LONG_PRESS_PRIMARY_NOTHING:
+                break;
+            case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
+                launchVoiceAssist(/* allowDuringSetup= */false);
+                break;
+        }
+    }
+
+    private void toggleTalkBack() {
+        final ComponentName componentName = getTalkbackComponent();
+        if (componentName == null) {
+            return;
+        }
+
+        final Set<ComponentName> enabledServices =
+                AccessibilityUtils.getEnabledServicesFromSettings(mContext, mCurrentUserId);
+
+        AccessibilityUtils.setAccessibilityServiceState(mContext, componentName,
+                !enabledServices.contains(componentName));
+    }
+
+    private ComponentName getTalkbackComponent() {
+        AccessibilityManager accessibilityManager = mContext.getSystemService(
+                AccessibilityManager.class);
+        List<AccessibilityServiceInfo> serviceInfos =
+                accessibilityManager.getInstalledAccessibilityServiceList();
+
+        for (AccessibilityServiceInfo service : serviceInfos) {
+            final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
+            if (isTalkback(serviceInfo)) {
+                return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+            }
+        }
+        return null;
+    }
+
+    private boolean isTalkback(ServiceInfo info) {
+        String label = info.loadLabel(mPackageManager).toString();
+        return label.equals(TALKBACK_LABEL);
+    }
+
+    /**
+     * Load most recent task (expect current task) and bring it to the front.
+     */
+    private void switchRecentTask() {
+        RecentTaskInfo targetTask = mActivityTaskManagerInternal.getMostRecentTaskFromBackground();
+        if (targetTask == null) {
+            if (DEBUG_INPUT) {
+                Slog.w(TAG, "No recent task available! Show watch face.");
+            }
+            goHome();
+            return;
+        }
+
+        if (DEBUG_INPUT) {
+            Slog.d(
+                    TAG,
+                    "Starting task from recents. id="
+                            + targetTask.id
+                            + ", persistentId="
+                            + targetTask.persistentId
+                            + ", topActivity="
+                            + targetTask.topActivity
+                            + ", baseIntent="
+                            + targetTask.baseIntent);
+        }
+        try {
+            ActivityManager.getService().startActivityFromRecents(targetTask.persistentId, null);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to start task " + targetTask.persistentId + " from recents", e);
+        }
+    }
+
+    private int getMaxMultiPressStemPrimaryCount() {
+        switch (mTriplePressOnStemPrimaryBehavior) {
+            case TRIPLE_PRESS_PRIMARY_NOTHING:
+                break;
+            case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
+                if (Settings.System.getIntForUser(
+                                mContext.getContentResolver(),
+                                Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
+                                /* def= */ 0,
+                                UserHandle.USER_CURRENT)
+                        == 1) {
+                    return 3;
+                }
+                break;
+        }
+        if (mDoublePressOnStemPrimaryBehavior != DOUBLE_PRESS_PRIMARY_NOTHING) {
+            return 2;
+        }
+        return 1;
+    }
+
     private boolean hasLongPressOnPowerBehavior() {
         return getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;
     }
@@ -1204,6 +1395,16 @@
         return mLongPressOnBackBehavior != LONG_PRESS_BACK_NOTHING;
     }
 
+    private boolean hasLongPressOnStemPrimaryBehavior() {
+        return mLongPressOnStemPrimaryBehavior != LONG_PRESS_PRIMARY_NOTHING;
+    }
+
+    private boolean hasStemPrimaryBehavior() {
+        return getMaxMultiPressStemPrimaryCount() > 1
+                || hasLongPressOnStemPrimaryBehavior()
+                || mShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
+    }
+
     private void interceptScreenshotChord() {
         mHandler.removeCallbacks(mScreenshotRunnable);
         mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
@@ -2037,6 +2238,35 @@
         }
     }
 
+    /**
+     * Rule for single stem primary key gesture.
+     */
+    private final class StemPrimaryKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
+        StemPrimaryKeyRule(int gestures) {
+            super(mContext, KeyEvent.KEYCODE_STEM_PRIMARY, gestures);
+        }
+
+        @Override
+        int getMaxMultiPressCount() {
+            return getMaxMultiPressStemPrimaryCount();
+        }
+
+        @Override
+        void onPress(long downTime) {
+            stemPrimaryPress(1 /*count*/);
+        }
+
+        @Override
+        void onLongPress(long eventTime) {
+            stemPrimaryLongPress();
+        }
+
+        @Override
+        void onMultiPress(long downTime, int count) {
+            stemPrimaryPress(count);
+        }
+    }
+
     private void initSingleKeyGestureRules() {
         mSingleKeyGestureDetector = new SingleKeyGestureDetector();
 
@@ -2052,6 +2282,13 @@
         if (hasLongPressOnBackBehavior()) {
             mSingleKeyGestureDetector.addRule(new BackKeyRule(KEY_LONGPRESS));
         }
+        if (hasStemPrimaryBehavior()) {
+            int stemPrimaryKeyGestures = 0;
+            if (hasLongPressOnStemPrimaryBehavior()) {
+                stemPrimaryKeyGestures |= KEY_LONGPRESS;
+            }
+            mSingleKeyGestureDetector.addRule(new StemPrimaryKeyRule(stemPrimaryKeyGestures));
+        }
     }
 
     /**
@@ -2080,6 +2317,14 @@
         if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
         }
+        mShortPressOnStemPrimaryBehavior = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior);
+        mLongPressOnStemPrimaryBehavior = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior);
+        mDoublePressOnStemPrimaryBehavior = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_doublePressOnStemPrimaryBehavior);
+        mTriplePressOnStemPrimaryBehavior = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_triplePressOnStemPrimaryBehavior);
     }
 
     public void updateSettings() {
@@ -3063,7 +3308,6 @@
         if (mKeyguardOccludedChanged) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
                     + mPendingKeyguardOccluded);
-            mKeyguardOccludedChanged = false;
             if (setKeyguardOccludedLw(mPendingKeyguardOccluded, false /* force */,
                     transitionStarted)) {
                 return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
@@ -3273,14 +3517,6 @@
         mNavBarVirtualKeyHapticFeedbackEnabled = enabled;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public void setKeyguardCandidateLw(WindowState win) {
-        mKeyguardCandidate = win;
-        setKeyguardOccludedLw(isKeyguardOccluded(), true /* force */,
-                false /* keyguardOccludingStarted */);
-    }
-
     /**
      * Updates the occluded state of the Keyguard.
      *
@@ -3293,6 +3529,7 @@
     private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force,
             boolean transitionStarted) {
         if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
+        mKeyguardOccludedChanged = false;
         if (isKeyguardOccluded() == isOccluded && !force) {
             return false;
         }
@@ -5381,6 +5618,22 @@
                 pw.print("mShortPressOnWindowBehavior=");
                 pw.println(shortPressOnWindowBehaviorToString(mShortPressOnWindowBehavior));
         pw.print(prefix);
+                pw.print("mShortPressOnStemPrimaryBehavior=");
+                pw.println(shortPressOnStemPrimaryBehaviorToString(
+                    mShortPressOnStemPrimaryBehavior));
+        pw.print(prefix);
+                pw.print("mDoublePressOnStemPrimaryBehavior=");
+                pw.println(doublePressOnStemPrimaryBehaviorToString(
+                    mDoublePressOnStemPrimaryBehavior));
+        pw.print(prefix);
+                pw.print("mTriplePressOnStemPrimaryBehavior=");
+                pw.println(triplePressOnStemPrimaryBehaviorToString(
+                    mTriplePressOnStemPrimaryBehavior));
+        pw.print(prefix);
+                pw.print("mLongPressOnStemPrimaryBehavior=");
+                pw.println(longPressOnStemPrimaryBehaviorToString(
+                    mLongPressOnStemPrimaryBehavior));
+        pw.print(prefix);
                 pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
                 pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
         pw.print(prefix);
@@ -5596,6 +5849,50 @@
         }
     }
 
+    private static String shortPressOnStemPrimaryBehaviorToString(int behavior) {
+        switch (behavior) {
+            case SHORT_PRESS_PRIMARY_NOTHING:
+                return "SHORT_PRESS_PRIMARY_NOTHING";
+            case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
+                return "SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS";
+            default:
+                return Integer.toString(behavior);
+        }
+    }
+
+    private static String doublePressOnStemPrimaryBehaviorToString(int behavior) {
+        switch (behavior) {
+            case DOUBLE_PRESS_PRIMARY_NOTHING:
+                return "DOUBLE_PRESS_PRIMARY_NOTHING";
+            case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
+                return "DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP";
+            default:
+                return Integer.toString(behavior);
+        }
+    }
+
+    private static String triplePressOnStemPrimaryBehaviorToString(int behavior) {
+        switch (behavior) {
+            case TRIPLE_PRESS_PRIMARY_NOTHING:
+                return "TRIPLE_PRESS_PRIMARY_NOTHING";
+            case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
+                return "TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY";
+            default:
+                return Integer.toString(behavior);
+        }
+    }
+
+    private static String longPressOnStemPrimaryBehaviorToString(int behavior) {
+        switch (behavior) {
+            case LONG_PRESS_PRIMARY_NOTHING:
+                return "LONG_PRESS_PRIMARY_NOTHING";
+            case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
+                return "LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT";
+            default:
+                return Integer.toString(behavior);
+        }
+    }
+
     private static String lidBehaviorToString(int behavior) {
         switch (behavior) {
             case LID_BEHAVIOR_LOCK:
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 1d4943f..b7fb6e5 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -53,6 +53,7 @@
     // Key code of current key down event, reset when key up.
     private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
     private volatile boolean mHandledByLongPress = false;
+    private volatile boolean mHandledByMultiPress = false;
     private final Handler mHandler;
     private long mLastDownTime = 0;
     private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
@@ -271,6 +272,7 @@
                         mKeyPressCounter + 1, mActiveRule);
                 msg.setAsynchronous(true);
                 mHandler.sendMessage(msg);
+                mHandledByMultiPress = true;
                 mKeyPressCounter = 0;
             }
         }
@@ -284,8 +286,9 @@
             return false;
         }
 
-        if (mHandledByLongPress) {
+        if (mHandledByLongPress || mHandledByMultiPress) {
             mHandledByLongPress = false;
+            mHandledByMultiPress = false;
             mKeyPressCounter = 0;
             return true;
         }
@@ -339,6 +342,7 @@
         }
 
         mHandledByLongPress = false;
+        mHandledByMultiPress = false;
         mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
     }
 
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ab69da7..81dd9c5 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -714,13 +714,6 @@
             int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId);
 
     /**
-     * Set or clear a window which can behave as the keyguard.
-     *
-     * @param win The window which can behave as the keyguard.
-     */
-    void setKeyguardCandidateLw(@Nullable WindowState win);
-
-    /**
      * Create and return an animation to re-display a window that was force hidden by Keyguard.
      */
     public Animation createHiddenByKeyguardExit(boolean onWallpaper,
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 0f1563b..720c773 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1162,6 +1162,7 @@
         assertInWorkerThread();
         Slog.i(TAG, "makeRollbackAvailable id=" + rollback.info.getRollbackId());
         rollback.makeAvailable();
+        mPackageHealthObserver.notifyRollbackAvailable(rollback.info);
 
         // TODO(zezeozue): Provide API to explicitly start observing instead
         // of doing this for all rollbacks. If we do this for all rollbacks,
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 1295b70..1856786 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -51,6 +51,7 @@
 
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
@@ -75,8 +76,11 @@
     private final Handler mHandler;
     private final ApexManager mApexManager;
     private final File mLastStagedRollbackIdsFile;
+    private final File mTwoPhaseRollbackEnabledFile;
     // Staged rollback ids that have been committed but their session is not yet ready
     private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>();
+    // True if needing to roll back only rebootless apexes when native crash happens
+    private boolean mTwoPhaseRollbackEnabled;
 
     RollbackPackageHealthObserver(Context context) {
         mContext = context;
@@ -86,8 +90,19 @@
         File dataDir = new File(Environment.getDataDirectory(), "rollback-observer");
         dataDir.mkdirs();
         mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
+        mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
         PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
         mApexManager = ApexManager.getInstance();
+
+        if (SystemProperties.getBoolean("sys.boot_completed", false)) {
+            // Load the value from the file if system server has crashed and restarted
+            mTwoPhaseRollbackEnabled = readBoolean(mTwoPhaseRollbackEnabledFile);
+        } else {
+            // Disable two-phase rollback for a normal reboot. We assume the rebootless apex
+            // installed before reboot is stable if native crash didn't happen.
+            mTwoPhaseRollbackEnabled = false;
+            writeBoolean(mTwoPhaseRollbackEnabledFile, false);
+        }
     }
 
     @Override
@@ -144,6 +159,31 @@
         PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
     }
 
+    @AnyThread
+    void notifyRollbackAvailable(RollbackInfo rollback) {
+        mHandler.post(() -> {
+            // Enable two-phase rollback when a rebootless apex rollback is made available.
+            // We assume the rebootless apex is stable and is less likely to be the cause
+            // if native crash doesn't happen before reboot. So we will clear the flag and disable
+            // two-phase rollback after reboot.
+            if (isRebootlessApex(rollback)) {
+                mTwoPhaseRollbackEnabled = true;
+                writeBoolean(mTwoPhaseRollbackEnabledFile, true);
+            }
+        });
+    }
+
+    private static boolean isRebootlessApex(RollbackInfo rollback) {
+        if (!rollback.isStaged()) {
+            for (PackageRollbackInfo info : rollback.getPackages()) {
+                if (info.isApex()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot
      * to check for native crashes and mitigate them if needed.
      */
@@ -155,6 +195,7 @@
     @WorkerThread
     private void onBootCompleted() {
         assertInWorkerThread();
+
         RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
             // TODO(gavincorkery): Call into Package Watchdog from outside the observer
@@ -277,6 +318,23 @@
         return mPendingStagedRollbackIds.isEmpty();
     }
 
+    private static boolean readBoolean(File file) {
+        try (FileInputStream fis = new FileInputStream(file)) {
+            return fis.read() == 1;
+        } catch (IOException ignore) {
+            return false;
+        }
+    }
+
+    private static void writeBoolean(File file, boolean value) {
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            fos.write(value ? 1 : 0);
+            fos.flush();
+            FileUtils.sync(fos);
+        } catch (IOException ignore) {
+        }
+    }
+
     @WorkerThread
     private void saveStagedRollbackId(int stagedRollbackId, @Nullable VersionedPackage logPackage) {
         assertInWorkerThread();
@@ -420,13 +478,44 @@
                 Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender());
     }
 
+    /**
+     * Two-phase rollback:
+     * 1. roll back rebootless apexes first
+     * 2. roll back all remaining rollbacks if native crash doesn't stop after (1) is done
+     *
+     * This approach gives us a better chance to correctly attribute native crash to rebootless
+     * apex update without rolling back Mainline updates which might contains critical security
+     * fixes.
+     */
+    @WorkerThread
+    private boolean useTwoPhaseRollback(List<RollbackInfo> rollbacks) {
+        assertInWorkerThread();
+        if (!mTwoPhaseRollbackEnabled) {
+            return false;
+        }
+
+        Slog.i(TAG, "Rolling back all rebootless APEX rollbacks");
+        boolean found = false;
+        for (RollbackInfo rollback : rollbacks) {
+            if (isRebootlessApex(rollback)) {
+                VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom();
+                rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+                found = true;
+            }
+        }
+        return found;
+    }
+
     @WorkerThread
     private void rollbackAll() {
         assertInWorkerThread();
-        Slog.i(TAG, "Rolling back all available rollbacks");
         RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
+        if (useTwoPhaseRollback(rollbacks)) {
+            return;
+        }
 
+        Slog.i(TAG, "Rolling back all available rollbacks");
         // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all
         // pending staged rollbacks are handled.
         for (RollbackInfo rollback : rollbacks) {
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 106cff1..2be29d4 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -29,6 +29,7 @@
 import static android.net.NetworkIdentity.OEM_PRIVATE;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkTemplate.MATCH_ETHERNET;
 import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD;
@@ -1348,7 +1349,7 @@
     @Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
         final NetworkTemplate template = (transport == TRANSPORT_CELLULAR)
                 ? NetworkTemplate.buildTemplateMobileWithRatType(
-                /*subscriptionId=*/null, NETWORK_TYPE_ALL)
+                /*subscriptionId=*/null, NETWORK_TYPE_ALL, METERED_YES)
                 : NetworkTemplate.buildTemplateWifiWildcard();
         return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
     }
@@ -1388,7 +1389,8 @@
         final List<NetworkStatsExt> ret = new ArrayList<>();
         for (final int ratType : getAllCollapsedRatTypes()) {
             final NetworkTemplate template =
-                    buildTemplateMobileWithRatType(subInfo.subscriberId, ratType);
+                    buildTemplateMobileWithRatType(subInfo.subscriberId, ratType,
+                    METERED_YES);
             final NetworkStats stats =
                     getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
             if (stats != null) {
diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
index 1867ee2..3b32549 100644
--- a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
+++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
@@ -16,9 +16,11 @@
 
 package com.android.server.timezonedetector;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.ShellCommand;
+import android.os.SystemClock;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -29,27 +31,39 @@
 import java.util.StringTokenizer;
 
 /**
- * A time zone suggestion from a geolocation source.
+ * A time zone suggestion from the location_time_zone_manager service to the time_zone_detector
+ * service.
  *
- * <p> Geolocation-based suggestions have the following properties:
+ * <p>Geolocation-based suggestions have the following properties:
  *
  * <ul>
+ *     <li>{@code effectiveFromElapsedMillis}: The time according to the elapsed realtime clock
+ *     after which the suggestion should be considered in effect. For example, when a location fix
+ *     used to establish the time zone is old, then the suggestion
+ *     {@code effectiveFromElapsedMillis} should reflect this and indicates the time zone that was
+ *     detected / correct at that time. The time_zone_detector is only expected to use the latest
+ *     suggestion it has received, and so later suggestions always counteract previous suggestions.
+ *     The inclusion of this information means that the time_zone_detector can take into account
+ *     ordering when comparing suggestions from different sources.
+ *     <br />Note: Because the times can be back-dated, time_zone_detector can be sent a sequence of
+ *     suggestions where the {@code effectiveFromElapsedMillis} of later suggestions is before
+ *     the {@code effectiveFromElapsedMillis} of an earlier one.</li>
  *     <li>{@code zoneIds}. When not {@code null}, {@code zoneIds} contains a list of suggested time
  *     zone IDs, e.g. ["America/Phoenix", "America/Denver"]. Usually there will be a single zoneId.
  *     When there are multiple, this indicates multiple answers are possible for the current
- *     location / accuracy, i.e. if there is a nearby time zone border. The detection logic
- *     receiving the suggestion is expected to use the first element in the absence of other
- *     information, but one of the others may be used if there is supporting evidence / preferences
- *     such as a device setting or corroborating signals from another source.
+ *     location / accuracy, e.g. if there is a nearby time zone border. The time_zone_detector is
+ *     expected to use the first element in the absence of other information, but one of the other
+ *     zone IDs may be used if there is supporting evidence / preferences such as a device setting
+ *     or corroborating signals from another source.
  *     <br />{@code zoneIds} can be empty if the current location has been determined to have no
  *     time zone. For example, oceans or disputed areas. This is considered a strong signal and the
- *     received need not look for time zone from other sources.
- *     <br />{@code zoneIds} can be {@code null} to indicate that the geolocation source has entered
- *     an "un-opinionated" state and any previous suggestion is being withdrawn. This indicates the
- *     source cannot provide a valid suggestion due to technical limitations. For example, a
- *     geolocation source may become un-opinionated if the device's location is no longer known with
- *     sufficient accuracy, or if the location is known but no time zone can be determined because
- *     no time zone mapping information is available.</li>
+ *     time_zone_detector need not look for time zone from other sources.
+ *     <br />{@code zoneIds} can be {@code null} to indicate that the location_time_zone_manager has
+ *     entered an "uncertain" state and any previous suggestion is being withdrawn. This indicates
+ *     the location_time_zone_manager cannot provide a valid suggestion. For example, the
+ *     location_time_zone_manager may become uncertain if components further downstream cannot
+ *     determine the device's location with sufficient accuracy, or if the location is known but no
+ *     time zone can be determined because no time zone mapping information is available.</li>
  *     <li>{@code debugInfo} contains debugging metadata associated with the suggestion. This is
  *     used to record why the suggestion exists and how it was obtained. This information exists
  *     only to aid in debugging and therefore is used by {@link #toString()}, but it is not for use
@@ -61,10 +75,14 @@
  */
 public final class GeolocationTimeZoneSuggestion {
 
+    @ElapsedRealtimeLong private final long mEffectiveFromElapsedMillis;
     @Nullable private final List<String> mZoneIds;
     @Nullable private ArrayList<String> mDebugInfo;
 
-    public GeolocationTimeZoneSuggestion(@Nullable List<String> zoneIds) {
+    private GeolocationTimeZoneSuggestion(
+            @ElapsedRealtimeLong long effectiveFromElapsedMillis,
+            @Nullable List<String> zoneIds) {
+        mEffectiveFromElapsedMillis = effectiveFromElapsedMillis;
         if (zoneIds == null) {
             // Unopinionated
             mZoneIds = null;
@@ -74,6 +92,34 @@
     }
 
     /**
+     * Creates a "uncertain" suggestion instance.
+     */
+    @NonNull
+    public static GeolocationTimeZoneSuggestion createUncertainSuggestion(
+            @ElapsedRealtimeLong long effectiveFromElapsedMillis) {
+        return new GeolocationTimeZoneSuggestion(effectiveFromElapsedMillis, null);
+    }
+
+    /**
+     * Creates a "certain" suggestion instance.
+     */
+    @NonNull
+    public static GeolocationTimeZoneSuggestion createCertainSuggestion(
+            @ElapsedRealtimeLong long effectiveFromElapsedMillis,
+            @NonNull List<String> zoneIds) {
+        return new GeolocationTimeZoneSuggestion(effectiveFromElapsedMillis, zoneIds);
+    }
+
+    /**
+     * Returns the "effective from" time associated with the suggestion. See {@link
+     * GeolocationTimeZoneSuggestion} for details.
+     */
+    @ElapsedRealtimeLong
+    public long getEffectiveFromElapsedMillis() {
+        return mEffectiveFromElapsedMillis;
+    }
+
+    /**
      * Returns the zone Ids being suggested. See {@link GeolocationTimeZoneSuggestion} for details.
      */
     @Nullable
@@ -110,18 +156,20 @@
         }
         GeolocationTimeZoneSuggestion
                 that = (GeolocationTimeZoneSuggestion) o;
-        return Objects.equals(mZoneIds, that.mZoneIds);
+        return mEffectiveFromElapsedMillis == that.mEffectiveFromElapsedMillis
+                && Objects.equals(mZoneIds, that.mZoneIds);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mZoneIds);
+        return Objects.hash(mEffectiveFromElapsedMillis, mZoneIds);
     }
 
     @Override
     public String toString() {
         return "GeolocationTimeZoneSuggestion{"
-                + "mZoneIds=" + mZoneIds
+                + "mEffectiveFromElapsedMillis=" + mEffectiveFromElapsedMillis
+                + ", mZoneIds=" + mZoneIds
                 + ", mDebugInfo=" + mDebugInfo
                 + '}';
     }
@@ -141,8 +189,11 @@
                 }
             }
         }
+
+        long elapsedRealtimeMillis = SystemClock.elapsedRealtime();
         List<String> zoneIds = parseZoneIdsArg(zoneIdsString);
-        GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(zoneIds);
+        GeolocationTimeZoneSuggestion suggestion =
+                new GeolocationTimeZoneSuggestion(elapsedRealtimeMillis, zoneIds);
         suggestion.addDebugInfo("Command line injection");
         return suggestion;
     }
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index 551a059..20fb61d 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -16,7 +16,9 @@
 
 package com.android.server.timezonedetector.location;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
+import android.os.SystemClock;
 
 import com.android.server.LocalServices;
 import com.android.server.timezonedetector.ConfigurationChangeListener;
@@ -83,4 +85,9 @@
     Duration getProviderEventFilteringAgeThreshold() {
         return mServiceConfigAccessor.getLocationTimeZoneProviderEventFilteringAgeThreshold();
     }
+
+    @Override
+    @ElapsedRealtimeLong long elapsedRealtimeMillis() {
+        return SystemClock.elapsedRealtime();
+    }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index 1d58cea1..466a039 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -31,10 +31,11 @@
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
 
 import android.annotation.DurationMillisLong;
-import android.annotation.IntRange;
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.service.timezone.TimeZoneProviderEvent;
+import android.service.timezone.TimeZoneProviderSuggestion;
 import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
@@ -43,7 +44,6 @@
 import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
 
 import java.time.Duration;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -184,7 +184,7 @@
         // re-started).
         if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
             GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
-                    "Providers are stopping");
+                    mEnvironment.elapsedRealtimeMillis(), "Providers are stopping");
             makeSuggestion(suggestion);
         }
     }
@@ -272,6 +272,7 @@
                     // If both providers are {perm failed} then the controller immediately
                     // becomes uncertain.
                     GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+                            mEnvironment.elapsedRealtimeMillis(),
                             "Providers are failed:"
                                     + " primary=" + mPrimaryProvider.getCurrentState()
                                     + " secondary=" + mPrimaryProvider.getCurrentState());
@@ -410,6 +411,7 @@
             // If both providers are now terminated, then a suggestion must be sent informing the
             // time zone detector that there are no further updates coming in future.
             GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+                    mEnvironment.elapsedRealtimeMillis(),
                     "Both providers are terminated:"
                             + " primary=" + primaryCurrentState.provider
                             + ", secondary=" + secondaryCurrentState.provider);
@@ -432,8 +434,9 @@
             // the loss of a binder-based provider, or initialization took too long. This is treated
             // the same as explicit uncertainty, i.e. where the provider has explicitly told this
             // process it is uncertain.
-            handleProviderUncertainty(provider, "provider=" + provider
-                    + ", implicit uncertainty, event=null");
+            long uncertaintyStartedElapsedMillis = mEnvironment.elapsedRealtimeMillis();
+            handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis,
+                    "provider=" + provider + ", implicit uncertainty, event=null");
             return;
         }
 
@@ -448,18 +451,17 @@
         switch (event.getType()) {
             case EVENT_TYPE_PERMANENT_FAILURE: {
                 // This shouldn't happen. A provider cannot be started and have this event type.
-                warnLog("Provider=" + provider
-                        + " is started, but event suggests it shouldn't be");
+                warnLog("Provider=" + provider + " is started, but event suggests it shouldn't be");
                 break;
             }
             case EVENT_TYPE_UNCERTAIN: {
-                handleProviderUncertainty(provider, "provider=" + provider
-                        + ", explicit uncertainty. event=" + event);
+                long uncertaintyStartedElapsedMillis = event.getCreationElapsedMillis();
+                handleProviderUncertainty(provider, uncertaintyStartedElapsedMillis,
+                        "provider=" + provider + ", explicit uncertainty. event=" + event);
                 break;
             }
             case EVENT_TYPE_SUGGESTION: {
-                handleProviderSuggestion(provider, event.getSuggestion().getTimeZoneIds(),
-                        "Event received provider=" + provider + ", event=" + event);
+                handleProviderSuggestion(provider, event);
                 break;
             }
             default: {
@@ -475,8 +477,8 @@
     @GuardedBy("mSharedLock")
     private void handleProviderSuggestion(
             @NonNull LocationTimeZoneProvider provider,
-            @Nullable List<String> timeZoneIds,
-            @NonNull String reason) {
+            @NonNull TimeZoneProviderEvent providerEvent) {
+
         // By definition, the controller is now certain.
         cancelUncertaintyTimeout();
 
@@ -484,10 +486,25 @@
             stopProviderIfStarted(mSecondaryProvider);
         }
 
-        GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(timeZoneIds);
-        suggestion.addDebugInfo(reason);
-        // Rely on the receiver to dedupe suggestions. It is better to over-communicate.
-        makeSuggestion(suggestion);
+        TimeZoneProviderSuggestion providerSuggestion = providerEvent.getSuggestion();
+
+        // For the suggestion's effectiveFromElapsedMillis, use the time embedded in the provider's
+        // suggestion (which indicates the time when the provider detected the location used to
+        // establish the time zone).
+        //
+        // An alternative would be to use the current time or the providerEvent creation time, but
+        // this would hinder the ability for the time_zone_detector to judge which suggestions are
+        // based on newer information when comparing suggestions between different sources.
+        long effectiveFromElapsedMillis = providerSuggestion.getElapsedRealtimeMillis();
+        GeolocationTimeZoneSuggestion geoSuggestion =
+                GeolocationTimeZoneSuggestion.createCertainSuggestion(
+                        effectiveFromElapsedMillis, providerSuggestion.getTimeZoneIds());
+
+        String debugInfo = "Event received provider=" + provider
+                + ", providerEvent=" + providerEvent
+                + ", suggestionCreationTime=" + mEnvironment.elapsedRealtimeMillis();
+        geoSuggestion.addDebugInfo(debugInfo);
+        makeSuggestion(geoSuggestion);
     }
 
     @Override
@@ -551,7 +568,9 @@
      */
     @GuardedBy("mSharedLock")
     void handleProviderUncertainty(
-            @NonNull LocationTimeZoneProvider provider, @NonNull String reason) {
+            @NonNull LocationTimeZoneProvider provider,
+            @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis,
+            @NonNull String reason) {
         Objects.requireNonNull(provider);
 
         // Start the uncertainty timeout if needed to ensure the controller will eventually make an
@@ -559,9 +578,11 @@
         if (!mUncertaintyTimeoutQueue.hasQueued()) {
             debugLog("Starting uncertainty timeout: reason=" + reason);
 
-            Duration delay = mEnvironment.getUncertaintyDelay();
-            mUncertaintyTimeoutQueue.runDelayed(() -> onProviderUncertaintyTimeout(provider),
-                    delay.toMillis());
+            Duration uncertaintyDelay = mEnvironment.getUncertaintyDelay();
+            mUncertaintyTimeoutQueue.runDelayed(
+                    () -> onProviderUncertaintyTimeout(
+                            provider, uncertaintyStartedElapsedMillis, uncertaintyDelay),
+                    uncertaintyDelay.toMillis());
         }
 
         if (provider == mPrimaryProvider) {
@@ -573,21 +594,45 @@
         }
     }
 
-    private void onProviderUncertaintyTimeout(@NonNull LocationTimeZoneProvider provider) {
+    private void onProviderUncertaintyTimeout(
+            @NonNull LocationTimeZoneProvider provider,
+            @ElapsedRealtimeLong long uncertaintyStartedElapsedMillis,
+            @NonNull Duration uncertaintyDelay) {
         mThreadingDomain.assertCurrentThread();
 
         synchronized (mSharedLock) {
+            long afterUncertaintyTimeoutElapsedMillis = mEnvironment.elapsedRealtimeMillis();
+
+            // For the effectiveFromElapsedMillis suggestion property, use the
+            // uncertaintyStartedElapsedMillis. This is the time when the provider first reported
+            // uncertainty, i.e. before the uncertainty timeout.
+            //
+            // afterUncertaintyTimeoutElapsedMillis could be used instead, which is the time when
+            // the location_time_zone_manager finally confirms that the time zone was uncertain,
+            // but the suggestion property allows the information to be back-dated, which should
+            // help when comparing suggestions from different sources.
             GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+                    uncertaintyStartedElapsedMillis,
                     "Uncertainty timeout triggered for " + provider.getName() + ":"
                             + " primary=" + mPrimaryProvider
-                            + ", secondary=" + mSecondaryProvider);
+                            + ", secondary=" + mSecondaryProvider
+                            + ", uncertaintyStarted="
+                            + Duration.ofMillis(uncertaintyStartedElapsedMillis)
+                            + ", afterUncertaintyTimeout="
+                            + Duration.ofMillis(afterUncertaintyTimeoutElapsedMillis)
+                            + ", uncertaintyDelay=" + uncertaintyDelay
+            );
             makeSuggestion(suggestion);
         }
     }
 
     @NonNull
-    private static GeolocationTimeZoneSuggestion createUncertainSuggestion(@NonNull String reason) {
-        GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(null);
+    private static GeolocationTimeZoneSuggestion createUncertainSuggestion(
+            @ElapsedRealtimeLong long effectiveFromElapsedMillis,
+            @NonNull String reason) {
+        GeolocationTimeZoneSuggestion suggestion =
+                GeolocationTimeZoneSuggestion.createUncertainSuggestion(
+                        effectiveFromElapsedMillis);
         suggestion.addDebugInfo(reason);
         return suggestion;
     }
@@ -622,19 +667,4 @@
             return builder.build();
         }
     }
-
-    @Nullable
-    private LocationTimeZoneProvider getLocationTimeZoneProvider(
-            @IntRange(from = 0, to = 1) int providerIndex) {
-        LocationTimeZoneProvider targetProvider;
-        if (providerIndex == 0) {
-            targetProvider = mPrimaryProvider;
-        } else if (providerIndex == 1) {
-            targetProvider = mSecondaryProvider;
-        } else {
-            warnLog("Bad providerIndex=" + providerIndex);
-            targetProvider = null;
-        }
-        return targetProvider;
-    }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
index d2fd9a9..fdb9c14 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
@@ -17,6 +17,7 @@
 package com.android.server.timezonedetector.location;
 
 import android.annotation.DurationMillisLong;
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.os.Handler;
 
@@ -140,6 +141,12 @@
          * passed on.
          */
         abstract Duration getUncertaintyDelay();
+
+        /**
+         * Returns the elapsed realtime as millis, the same as {@link
+         * android.os.SystemClock#elapsedRealtime()}.
+         */
+        abstract @ElapsedRealtimeLong long elapsedRealtimeMillis();
     }
 
     /**
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 2894708..87ce1ce 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -44,6 +44,8 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.media.PlaybackParams;
+import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.DvbDeviceInfo;
 import android.media.tv.ITvInputClient;
 import android.media.tv.ITvInputHardware;
@@ -2327,6 +2329,29 @@
         }
 
         @Override
+        public void requestBroadcastInfo(IBinder sessionToken, BroadcastInfoRequest request,
+                int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
+                    userId, "requestBroadcastInfo");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).requestBroadcastInfo(request);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in requestBroadcastInfo", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            };
+        }
+
+        @Override
         public int getClientPid(String sessionId) {
             ensureTunerResourceAccessPermission();
             final long identity = Binder.clearCallingIdentity();
@@ -3342,6 +3367,23 @@
                 }
             }
         }
+
+        @Override
+        public void onBroadcastInfoResponse (BroadcastInfoResponse response) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onBroadcastInfoResponse()");
+                }
+                if (mSessionState.session == null || mSessionState.client == null) {
+                    return;
+                }
+                try {
+                    mSessionState.client.onBroadcastInfoResponse(response, mSessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onBroadcastInfoResponse", e);
+                }
+            }
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 620be3a..c4e5660 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -16,21 +16,56 @@
 
 package com.android.server.tv.interactive;
 
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.media.tv.interactive.ITvIAppClient;
 import android.media.tv.interactive.ITvIAppManager;
+import android.media.tv.interactive.ITvIAppService;
+import android.media.tv.interactive.ITvIAppServiceCallback;
 import android.media.tv.interactive.ITvIAppSession;
+import android.media.tv.interactive.ITvIAppSessionCallback;
+import android.media.tv.interactive.TvIAppService;
+import android.os.Binder;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
 import com.android.server.utils.Slogf;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * This class provides a system service that manages interactive TV applications.
  */
 public class TvIAppManagerService extends SystemService {
     private static final boolean DEBUG = false;
     private static final String TAG = "TvIAppManagerService";
+    // A global lock.
+    private final Object mLock = new Object();
+    private final Context mContext;
+    // ID of the current user.
+    @GuardedBy("mLock")
+    private int mCurrentUserId = UserHandle.USER_SYSTEM;
+    // IDs of the running profiles. Their parent user ID should be mCurrentUserId.
+    @GuardedBy("mLock")
+    private final Set<Integer> mRunningProfiles = new HashSet<>();
+    // A map from user id to UserState.
+    @GuardedBy("mLock")
+    private final SparseArray<UserState> mUserStates = new SparseArray<>();
 
     /**
      * Initializes the system service.
@@ -43,6 +78,7 @@
      */
     public TvIAppManagerService(Context context) {
         super(context);
+        mContext = context;
     }
 
     @Override
@@ -59,7 +95,132 @@
         return null;
     }
 
+    private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
+            String methodName) {
+        return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
+                false, methodName, null);
+    }
+
+    @GuardedBy("mLock")
+    private UserState getOrCreateUserStateLocked(int userId) {
+        UserState userState = getUserStateLocked(userId);
+        if (userState == null) {
+            userState = new UserState(userId);
+            mUserStates.put(userId, userState);
+        }
+        return userState;
+    }
+
+    @GuardedBy("mLock")
+    private UserState getUserStateLocked(int userId) {
+        return mUserStates.get(userId);
+    }
+
+    @GuardedBy("mLock")
+    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        return getSessionStateLocked(sessionToken, callingUid, userState);
+    }
+
+    @GuardedBy("mLock")
+    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid,
+            UserState userState) {
+        SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
+        if (sessionState == null) {
+            throw new SessionNotFoundException("Session state not found for token " + sessionToken);
+        }
+        // Only the application that requested this session or the system can access it.
+        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
+            throw new SecurityException("Illegal access to the session with token " + sessionToken
+                    + " from uid " + callingUid);
+        }
+        return sessionState;
+    }
+
     private final class BinderService extends ITvIAppManager.Stub {
+
+        @Override
+        public void createSession(final ITvIAppClient client, final String iAppServiceId, int type,
+                int seq, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
+                    userId, "createSession");
+            final long identity = Binder.clearCallingIdentity();
+
+            try {
+                synchronized (mLock) {
+                    if (userId != mCurrentUserId && !mRunningProfiles.contains(userId)) {
+                        // Only current user and its running profiles can create sessions.
+                        // Let the client get onConnectionFailed callback for this case.
+                        sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
+                        return;
+                    }
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
+                    if (iAppState == null) {
+                        Slogf.w(TAG, "Failed to find state for iAppServiceId=" + iAppServiceId);
+                        sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
+                        return;
+                    }
+                    ServiceState serviceState =
+                            userState.mServiceStateMap.get(iAppState.mComponentName);
+                    if (serviceState == null) {
+                        int tiasUid = PackageManager.getApplicationInfoAsUserCached(
+                                iAppState.mComponentName.getPackageName(), 0, resolvedUserId).uid;
+                        serviceState = new ServiceState(iAppState.mComponentName, resolvedUserId);
+                        userState.mServiceStateMap.put(iAppState.mComponentName, serviceState);
+                    }
+                    // Send a null token immediately while reconnecting.
+                    if (serviceState.mReconnecting) {
+                        sendSessionTokenToClientLocked(client, iAppServiceId, null, seq);
+                        return;
+                    }
+
+                    // Create a new session token and a session state.
+                    IBinder sessionToken = new Binder();
+                    SessionState sessionState = new SessionState(sessionToken, iAppServiceId, type,
+                            iAppState.mComponentName, client, seq, callingUid,
+                            callingPid, resolvedUserId);
+
+                    // Add them to the global session state map of the current user.
+                    userState.mSessionStateMap.put(sessionToken, sessionState);
+
+                    // Also, add them to the session state map of the current service.
+                    serviceState.mSessionTokens.add(sessionToken);
+
+                    if (serviceState.mService != null) {
+                        if (!createSessionInternalLocked(serviceState.mService, sessionToken,
+                                resolvedUserId)) {
+                            removeSessionStateLocked(sessionToken, resolvedUserId);
+                        }
+                    } else {
+                        updateServiceConnectionLocked(iAppState.mComponentName, resolvedUserId);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void releaseSession(IBinder sessionToken, int userId) {
+            if (DEBUG) {
+                Slogf.d(TAG, "releaseSession(sessionToken=" + sessionToken + ")");
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "releaseSession");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
         @Override
         public void startIApp(IBinder sessionToken, int userId) {
             if (DEBUG) {
@@ -76,16 +237,422 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void sendSessionTokenToClientLocked(ITvIAppClient client, String iAppServiceId,
+            IBinder sessionToken, int seq) {
+        try {
+            client.onSessionCreated(iAppServiceId, sessionToken, seq);
+        } catch (RemoteException e) {
+            Slogf.e(TAG, "error in onSessionCreated", e);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean createSessionInternalLocked(ITvIAppService service, IBinder sessionToken,
+            int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
+        if (DEBUG) {
+            Slogf.d(TAG, "createSessionInternalLocked(iAppServiceId="
+                    + sessionState.mIAppServiceId + ")");
+        }
+
+        // Set up a callback to send the session token.
+        ITvIAppSessionCallback callback = new SessionCallback(sessionState);
+
+        boolean created = true;
+        // Create a session. When failed, send a null token immediately.
+        try {
+            service.createSession(callback, sessionState.mIAppServiceId, sessionState.mType);
+        } catch (RemoteException e) {
+            Slogf.e(TAG, "error in createSession", e);
+            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mIAppServiceId, null,
+                    sessionState.mSeq);
+            created = false;
+        }
+        return created;
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+        SessionState sessionState = null;
+        try {
+            sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+            UserState userState = getOrCreateUserStateLocked(userId);
+            if (sessionState.mSession != null) {
+                sessionState.mSession.asBinder().unlinkToDeath(sessionState, 0);
+                sessionState.mSession.release();
+            }
+        } catch (RemoteException | SessionNotFoundException e) {
+            Slogf.e(TAG, "error in releaseSession", e);
+        } finally {
+            if (sessionState != null) {
+                sessionState.mSession = null;
+            }
+        }
+        removeSessionStateLocked(sessionToken, userId);
+        return sessionState;
+    }
+
+    @GuardedBy("mLock")
+    private void removeSessionStateLocked(IBinder sessionToken, int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+
+        // Remove the session state from the global session state map of the current user.
+        SessionState sessionState = userState.mSessionStateMap.remove(sessionToken);
+
+        if (sessionState == null) {
+            Slogf.e(TAG, "sessionState null, no more remove session action!");
+            return;
+        }
+
+        // Also remove the session token from the session token list of the current client and
+        // service.
+        ClientState clientState = userState.mClientStateMap.get(sessionState.mClient.asBinder());
+        if (clientState != null) {
+            clientState.mSessionTokens.remove(sessionToken);
+            if (clientState.isEmpty()) {
+                userState.mClientStateMap.remove(sessionState.mClient.asBinder());
+                sessionState.mClient.asBinder().unlinkToDeath(clientState, 0);
+            }
+        }
+
+        ServiceState serviceState = userState.mServiceStateMap.get(sessionState.mComponent);
+        if (serviceState != null) {
+            serviceState.mSessionTokens.remove(sessionToken);
+        }
+        updateServiceConnectionLocked(sessionState.mComponent, userId);
+    }
+
+    @GuardedBy("mLock")
+    private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
+            String iAppServiceId, int userId) {
+        // Let clients know the create session requests are failed.
+        UserState userState = getOrCreateUserStateLocked(userId);
+        List<SessionState> sessionsToAbort = new ArrayList<>();
+        for (IBinder sessionToken : serviceState.mSessionTokens) {
+            SessionState sessionState = userState.mSessionStateMap.get(sessionToken);
+            if (sessionState.mSession == null
+                    && (iAppServiceId == null
+                    || sessionState.mIAppServiceId.equals(iAppServiceId))) {
+                sessionsToAbort.add(sessionState);
+            }
+        }
+        for (SessionState sessionState : sessionsToAbort) {
+            removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
+            sendSessionTokenToClientLocked(sessionState.mClient,
+                    sessionState.mIAppServiceId, null, sessionState.mSeq);
+        }
+        updateServiceConnectionLocked(serviceState.mComponent, userId);
+    }
+
+    @GuardedBy("mLock")
+    private void updateServiceConnectionLocked(ComponentName component, int userId) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        ServiceState serviceState = userState.mServiceStateMap.get(component);
+        if (serviceState == null) {
+            return;
+        }
+        if (serviceState.mReconnecting) {
+            if (!serviceState.mSessionTokens.isEmpty()) {
+                // wait until all the sessions are removed.
+                return;
+            }
+            serviceState.mReconnecting = false;
+        }
+
+        boolean shouldBind = !serviceState.mSessionTokens.isEmpty();
+
+        if (serviceState.mService == null && shouldBind) {
+            // This means that the service is not yet connected but its state indicates that we
+            // have pending requests. Then, connect the service.
+            if (serviceState.mBound) {
+                // We have already bound to the service so we don't try to bind again until after we
+                // unbind later on.
+                return;
+            }
+            if (DEBUG) {
+                Slogf.d(TAG, "bindServiceAsUser(service=" + component + ", userId=" + userId + ")");
+            }
+
+            Intent i = new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+            serviceState.mBound = mContext.bindServiceAsUser(
+                    i, serviceState.mConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+                    new UserHandle(userId));
+        } else if (serviceState.mService != null && !shouldBind) {
+            // This means that the service is already connected but its state indicates that we have
+            // nothing to do with it. Then, disconnect the service.
+            if (DEBUG) {
+                Slogf.d(TAG, "unbindService(service=" + component + ")");
+            }
+            mContext.unbindService(serviceState.mConnection);
+            userState.mServiceStateMap.remove(component);
+        }
+    }
+
+    private static final class UserState {
+        private final int mUserId;
+        // A mapping from the TV IApp ID to its TvIAppState.
+        private Map<String, TvIAppState> mIAppMap = new HashMap<>();
+        // A mapping from the token of a client to its state.
+        private final Map<IBinder, ClientState> mClientStateMap = new HashMap<>();
+        // A mapping from the name of a TV IApp service to its state.
+        private final Map<ComponentName, ServiceState> mServiceStateMap = new HashMap<>();
+        // A mapping from the token of a TV IApp session to its state.
+        private final Map<IBinder, SessionState> mSessionStateMap = new HashMap<>();
+
+        private UserState(int userId) {
+            mUserId = userId;
+        }
+    }
+
+    private static final class TvIAppState {
+        private final String mIAppServiceId;
+        private final ComponentName mComponentName;
+
+        TvIAppState(String id, ComponentName componentName) {
+            mIAppServiceId = id;
+            mComponentName = componentName;
+        }
+    }
+
     private final class SessionState implements IBinder.DeathRecipient {
         private final IBinder mSessionToken;
         private ITvIAppSession mSession;
+        private final String mIAppServiceId;
+        private final int mType;
+        private final ITvIAppClient mClient;
+        private final int mSeq;
+        private final ComponentName mComponent;
 
-        private SessionState(IBinder sessionToken) {
+        // The UID of the application that created the session.
+        // The application is usually the TV app.
+        private final int mCallingUid;
+
+        // The PID of the application that created the session.
+        // The application is usually the TV app.
+        private final int mCallingPid;
+
+        private final int mUserId;
+
+        private SessionState(IBinder sessionToken, String iAppServiceId, int type,
+                ComponentName componentName, ITvIAppClient client, int seq, int callingUid,
+                int callingPid, int userId) {
             mSessionToken = sessionToken;
+            mIAppServiceId = iAppServiceId;
+            mComponent = componentName;
+            mType = type;
+            mClient = client;
+            mSeq = seq;
+            mCallingUid = callingUid;
+            mCallingPid = callingPid;
+            mUserId = userId;
         }
 
         @Override
         public void binderDied() {
         }
     }
+
+    private final class ClientState implements IBinder.DeathRecipient {
+        private final List<IBinder> mSessionTokens = new ArrayList<>();
+
+        private IBinder mClientToken;
+        private final int mUserId;
+
+        ClientState(IBinder clientToken, int userId) {
+            mClientToken = clientToken;
+            mUserId = userId;
+        }
+
+        public boolean isEmpty() {
+            return mSessionTokens.isEmpty();
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                UserState userState = getOrCreateUserStateLocked(mUserId);
+                // DO NOT remove the client state of clientStateMap in this method. It will be
+                // removed in releaseSessionLocked().
+                ClientState clientState = userState.mClientStateMap.get(mClientToken);
+                if (clientState != null) {
+                    while (clientState.mSessionTokens.size() > 0) {
+                        IBinder sessionToken = clientState.mSessionTokens.get(0);
+                        releaseSessionLocked(
+                                sessionToken, Process.SYSTEM_UID, mUserId);
+                        // the releaseSessionLocked function may return before the sessionToken
+                        // is removed if the related sessionState is null. So need to check again
+                        // to avoid death circulation.
+                        if (clientState.mSessionTokens.contains(sessionToken)) {
+                            Slogf.d(TAG, "remove sessionToken " + sessionToken + " for "
+                                    + mClientToken);
+                            clientState.mSessionTokens.remove(sessionToken);
+                        }
+                    }
+                }
+                mClientToken = null;
+            }
+        }
+    }
+
+    private final class ServiceState {
+        private final List<IBinder> mSessionTokens = new ArrayList<>();
+        private final ServiceConnection mConnection;
+        private final ComponentName mComponent;
+
+        private ITvIAppService mService;
+        private ServiceCallback mCallback;
+        private boolean mBound;
+        private boolean mReconnecting;
+
+        private ServiceState(ComponentName component, int userId) {
+            mComponent = component;
+            mConnection = new IAppServiceConnection(component, userId);
+        }
+    }
+
+    private final class IAppServiceConnection implements ServiceConnection {
+        private final ComponentName mComponent;
+        private final int mUserId;
+
+        private IAppServiceConnection(ComponentName component, int userId) {
+            mComponent = component;
+            mUserId = userId;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+            if (DEBUG) {
+                Slogf.d(TAG, "onServiceConnected(component=" + component + ")");
+            }
+            synchronized (mLock) {
+                UserState userState = getUserStateLocked(mUserId);
+                if (userState == null) {
+                    // The user was removed while connecting.
+                    mContext.unbindService(this);
+                    return;
+                }
+                ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
+                serviceState.mService = ITvIAppService.Stub.asInterface(service);
+
+                List<IBinder> tokensToBeRemoved = new ArrayList<>();
+
+                // And create sessions, if any.
+                for (IBinder sessionToken : serviceState.mSessionTokens) {
+                    if (!createSessionInternalLocked(
+                            serviceState.mService, sessionToken, mUserId)) {
+                        tokensToBeRemoved.add(sessionToken);
+                    }
+                }
+
+                for (IBinder sessionToken : tokensToBeRemoved) {
+                    removeSessionStateLocked(sessionToken, mUserId);
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (DEBUG) {
+                Slogf.d(TAG, "onServiceDisconnected(component=" + component + ")");
+            }
+            if (!mComponent.equals(component)) {
+                throw new IllegalArgumentException("Mismatched ComponentName: "
+                        + mComponent + " (expected), " + component + " (actual).");
+            }
+            synchronized (mLock) {
+                UserState userState = getOrCreateUserStateLocked(mUserId);
+                ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
+                if (serviceState != null) {
+                    serviceState.mReconnecting = true;
+                    serviceState.mBound = false;
+                    serviceState.mService = null;
+                    serviceState.mCallback = null;
+
+                    abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
+                }
+            }
+        }
+    }
+
+    private final class ServiceCallback extends ITvIAppServiceCallback.Stub {
+        private final ComponentName mComponent;
+        private final int mUserId;
+
+        ServiceCallback(ComponentName component, int userId) {
+            mComponent = component;
+            mUserId = userId;
+        }
+    }
+
+    private final class SessionCallback extends ITvIAppSessionCallback.Stub {
+        private final SessionState mSessionState;
+
+        SessionCallback(SessionState sessionState) {
+            mSessionState = sessionState;
+        }
+
+        @Override
+        public void onSessionCreated(ITvIAppSession session) {
+            if (DEBUG) {
+                Slogf.d(TAG, "onSessionCreated(iAppServiceId="
+                        + mSessionState.mIAppServiceId + ")");
+            }
+            synchronized (mLock) {
+                mSessionState.mSession = session;
+                if (session != null && addSessionTokenToClientStateLocked(session)) {
+                    sendSessionTokenToClientLocked(
+                            mSessionState.mClient,
+                            mSessionState.mIAppServiceId,
+                            mSessionState.mSessionToken,
+                            mSessionState.mSeq);
+                } else {
+                    removeSessionStateLocked(mSessionState.mSessionToken, mSessionState.mUserId);
+                    sendSessionTokenToClientLocked(mSessionState.mClient,
+                            mSessionState.mIAppServiceId, null, mSessionState.mSeq);
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) {
+            try {
+                session.asBinder().linkToDeath(mSessionState, 0);
+            } catch (RemoteException e) {
+                Slogf.e(TAG, "session process has already died", e);
+                return false;
+            }
+
+            IBinder clientToken = mSessionState.mClient.asBinder();
+            UserState userState = getOrCreateUserStateLocked(mSessionState.mUserId);
+            ClientState clientState = userState.mClientStateMap.get(clientToken);
+            if (clientState == null) {
+                clientState = new ClientState(clientToken, mSessionState.mUserId);
+                try {
+                    clientToken.linkToDeath(clientState, 0);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "client process has already died", e);
+                    return false;
+                }
+                userState.mClientStateMap.put(clientToken, clientState);
+            }
+            clientState.mSessionTokens.add(mSessionState.mSessionToken);
+            return true;
+        }
+    }
+
+    private static class SessionNotFoundException extends IllegalArgumentException {
+        SessionNotFoundException(String name) {
+            super(name);
+        }
+    }
+
+    private static class ClientPidNotFoundException extends IllegalArgumentException {
+        ClientPidNotFoundException(String name) {
+            super(name);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index b8eabe0..3177fac 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -893,7 +893,7 @@
                 } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
                     // Record the frontend id with the lowest client priority among all the
                     // in use frontends when no available frontend has been found.
-                    int priority = updateAndGetOwnerClientPriority(fr.getOwnerClientId());
+                    int priority = getFrontendHighestClientPriority(fr.getOwnerClientId());
                     if (currentLowestPriority > priority) {
                         inUseLowestPriorityFrHandle = fr.getHandle();
                         currentLowestPriority = priority;
@@ -1369,6 +1369,33 @@
         return profile.getPriority();
     }
 
+    /**
+     * Update the owner and sharee clients' priority and get the highest priority
+     * for frontend resource
+     *
+     * @param clientId the owner client id.
+     * @return the highest priority among all the clients holding the same frontend resource.
+     */
+    private int getFrontendHighestClientPriority(int clientId) {
+        // Check if the owner profile exists
+        ClientProfile ownerClient = getClientProfile(clientId);
+        if (ownerClient == null) {
+            return 0;
+        }
+
+        // Update and get the priority of the owner client
+        int highestPriority = updateAndGetOwnerClientPriority(clientId);
+
+        // Update and get all the client IDs of frontend resource holders
+        for (int shareeId : ownerClient.getShareFeClientIds()) {
+            int priority = updateAndGetOwnerClientPriority(shareeId);
+            if (priority > highestPriority) {
+                highestPriority = priority;
+            }
+        }
+        return highestPriority;
+    }
+
     @VisibleForTesting
     @Nullable
     protected FrontendResource getFrontendResource(int frontendHandle) {
@@ -1546,6 +1573,9 @@
     }
 
     private void clearAllResourcesAndClientMapping(ClientProfile profile) {
+        if (profile == null) {
+            return;
+        }
         // Clear Lnb
         for (Integer lnbHandle : profile.getInUseLnbHandles()) {
             getLnbResource(lnbHandle).removeOwner();
diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
index 9b0ef15..25ae000 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
@@ -263,6 +263,33 @@
     }
 
     /**
+     * Removes all of the mappings whose index is between {@code fromIndex}, inclusive, and
+     * {@code toIndex}, exclusive. The matrix does not shrink.
+     */
+    public void removeRange(int fromIndex, int toIndex) {
+        if (toIndex < fromIndex) {
+            throw new ArrayIndexOutOfBoundsException("toIndex < fromIndex");
+        }
+        final int num = toIndex - fromIndex;
+        if (num == 0) {
+            return;
+        }
+        validateIndex(fromIndex);
+        validateIndex(toIndex - 1);
+        for (int i = fromIndex; i < toIndex; i++) {
+            mInUse[mMap[i]] = false;
+        }
+        System.arraycopy(mKeys, toIndex, mKeys, fromIndex, mSize - toIndex);
+        System.arraycopy(mMap, toIndex, mMap, fromIndex, mSize - toIndex);
+        for (int i = mSize - num; i < mSize; i++) {
+            mKeys[i] = 0;
+            mMap[i] = 0;
+        }
+        mSize -= num;
+        onChanged();
+    }
+
+    /**
      * Returns the number of key-value mappings that this WatchedSparseBooleanMatrix
      * currently stores.
      */
@@ -371,7 +398,7 @@
                 // Preemptively grow the matrix, which also grows the free list.
                 growMatrix();
             }
-            int newIndex = nextFree();
+            int newIndex = nextFree(true /* acquire */);
             mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
             mMap = GrowingArrayUtils.insert(mMap, mSize, i, newIndex);
             mSize++;
@@ -447,12 +474,12 @@
     }
 
     /**
-     * Find an unused storage index, mark it in-use, and return it.
+     * Find an unused storage index, and return it. Mark it in-use if the {@code acquire} is true.
      */
-    private int nextFree() {
+    private int nextFree(boolean acquire) {
         for (int i = 0; i < mInUse.length; i++) {
             if (!mInUse[i]) {
-                mInUse[i] = true;
+                mInUse[i] = acquire;
                 return i;
             }
         }
@@ -488,7 +515,8 @@
         }
         // dst and src are identify raw (row, col) in mValues.  srcIndex is the index (as
         // in the result of keyAt()) of the key being relocated.
-        for (int dst = nextFree(); dst < mSize; dst = nextFree()) {
+        for (int dst = nextFree(false); dst < mSize; dst = nextFree(false)) {
+            mInUse[dst] = true;
             int srcIndex = lastInuse();
             int src = mMap[srcIndex];
             mInUse[src] = false;
@@ -539,6 +567,20 @@
     }
 
     /**
+     * Set capacity to enlarge the size of the 2D matrix. Capacity less than the {@link #capacity()}
+     * is not supported.
+     */
+    public void setCapacity(int capacity) {
+        if (capacity <= mOrder) {
+            return;
+        }
+        if (capacity % STEP != 0) {
+            capacity = ((capacity / STEP) + 1) * STEP;
+        }
+        resizeMatrix(capacity);
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index efccd57..4b7fd90 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -41,12 +41,11 @@
 
     private final Object mLock = new Object();
     private final NativeWrapper mNativeWrapper;
-    private final VibratorInfo.Builder mVibratorInfoBuilder;
 
     @GuardedBy("mLock")
     private VibratorInfo mVibratorInfo;
     @GuardedBy("mLock")
-    private boolean mVibratorInfoLoaded;
+    private boolean mVibratorInfoLoadSuccessful;
     @GuardedBy("mLock")
     private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
             new RemoteCallbackList<>();
@@ -73,10 +72,16 @@
             NativeWrapper nativeWrapper) {
         mNativeWrapper = nativeWrapper;
         mNativeWrapper.init(vibratorId, listener);
-        mVibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
-        mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
-                mVibratorInfoBuilder);
-        mVibratorInfo = mVibratorInfoBuilder.build();
+        VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
+        mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
+                vibratorInfoBuilder);
+        mVibratorInfo = vibratorInfoBuilder.build();
+
+        if (!mVibratorInfoLoadSuccessful) {
+            Slog.e(TAG,
+                    "Vibrator controller initialization failed to load some HAL info for vibrator "
+                            + vibratorId);
+        }
     }
 
     /** Register state listener for this vibrator. */
@@ -108,15 +113,33 @@
         }
     }
 
+    /** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */
+    public void reloadVibratorInfoIfNeeded() {
+        synchronized (mLock) {
+            if (mVibratorInfoLoadSuccessful) {
+                return;
+            }
+            int vibratorId = mVibratorInfo.getId();
+            VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
+            mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
+                    vibratorInfoBuilder);
+            mVibratorInfo = vibratorInfoBuilder.build();
+            if (!mVibratorInfoLoadSuccessful) {
+                Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId);
+            }
+        }
+    }
+
+    /** Checks if the {@link VibratorInfo} was loaded from the vibrator hardware successfully. */
+    boolean isVibratorInfoLoadSuccessful() {
+        synchronized (mLock) {
+            return mVibratorInfoLoadSuccessful;
+        }
+    }
+
     /** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
     public VibratorInfo getVibratorInfo() {
         synchronized (mLock) {
-            if (!mVibratorInfoLoaded) {
-                // Try to load the vibrator metadata that has failed in the last attempt.
-                mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
-                        mVibratorInfoBuilder);
-                mVibratorInfo = mVibratorInfoBuilder.build();
-            }
             return mVibratorInfo;
         }
     }
@@ -164,7 +187,9 @@
      * @return true if this vibrator has this capability, false otherwise
      */
     public boolean hasCapability(long capability) {
-        return mVibratorInfo.hasCapability(capability);
+        synchronized (mLock) {
+            return mVibratorInfo.hasCapability(capability);
+        }
     }
 
     /** Return {@code true} if the underlying vibrator is currently available, false otherwise. */
@@ -178,10 +203,10 @@
      * <p>This will affect the state of {@link #isUnderExternalControl()}.
      */
     public void setExternalControl(boolean externalControl) {
-        if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
-            return;
-        }
         synchronized (mLock) {
+            if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+                return;
+            }
             mIsUnderExternalControl = externalControl;
             mNativeWrapper.setExternalControl(externalControl);
         }
@@ -192,10 +217,10 @@
      * if given {@code effect} is {@code null}.
      */
     public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) {
-        if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
-            return;
-        }
         synchronized (mLock) {
+            if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
+                return;
+            }
             if (prebaked == null) {
                 mNativeWrapper.alwaysOnDisable(id);
             } else {
@@ -268,10 +293,10 @@
      * do not support the input or a negative number if the operation failed.
      */
     public long on(PrimitiveSegment[] primitives, long vibrationId) {
-        if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
-            return 0;
-        }
         synchronized (mLock) {
+            if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+                return 0;
+            }
             long duration = mNativeWrapper.compose(primitives, vibrationId);
             if (duration > 0) {
                 mCurrentAmplitude = -1;
@@ -290,10 +315,10 @@
      * @return The duration of the effect playing, or 0 if unsupported.
      */
     public long on(RampSegment[] primitives, long vibrationId) {
-        if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
-            return 0;
-        }
         synchronized (mLock) {
+            if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
+                return 0;
+            }
             int braking = mVibratorInfo.getDefaultBraking();
             long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
             if (duration > 0) {
@@ -327,6 +352,7 @@
         synchronized (mLock) {
             return "VibratorController{"
                     + "mVibratorInfo=" + mVibratorInfo
+                    + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
                     + ", mIsVibrating=" + mIsVibrating
                     + ", mCurrentAmplitude=" + mCurrentAmplitude
                     + ", mIsUnderExternalControl=" + mIsUnderExternalControl
@@ -393,10 +419,15 @@
          * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}.
          */
         private static native long getNativeFinalizer();
+
         private static native boolean isAvailable(long nativePtr);
+
         private static native long on(long nativePtr, long milliseconds, long vibrationId);
+
         private static native void off(long nativePtr);
+
         private static native void setAmplitude(long nativePtr, float amplitude);
+
         private static native long performEffect(long nativePtr, long effect, long strength,
                 long vibrationId);
 
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 239a112..3a3ce5b 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -128,6 +128,8 @@
     private VibrationThread mNextVibration;
     @GuardedBy("mLock")
     private ExternalVibrationHolder mCurrentExternalVibration;
+    @GuardedBy("mLock")
+    private boolean mServiceReady;
 
     private final VibrationSettings mVibrationSettings;
     private final VibrationScaler mVibrationScaler;
@@ -201,6 +203,9 @@
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
         mWakeLock.setReferenceCounted(true);
 
+        // Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only
+        // once and assumed unchanged for the lifecycle of this service. Each individual vibrator
+        // can still retry loading each individual vibrator hardware spec once more at systemReady.
         mCapabilities = mNativeWrapper.getCapabilities();
         int[] vibratorIds = mNativeWrapper.getVibratorIds();
         if (vibratorIds == null) {
@@ -235,6 +240,11 @@
         Slog.v(TAG, "Initializing VibratorManager service...");
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "systemReady");
         try {
+            // Will retry to load each vibrator's info, if any request have failed.
+            for (int i = 0; i < mVibrators.size(); i++) {
+                mVibrators.valueAt(i).reloadVibratorInfoIfNeeded();
+            }
+
             mVibrationSettings.onSystemReady();
             mInputDeviceDelegate.onSystemReady();
 
@@ -243,6 +253,9 @@
             // Will update settings and input devices.
             updateServiceState();
         } finally {
+            synchronized (mLock) {
+                mServiceReady = true;
+            }
             Slog.v(TAG, "VibratorManager service initialized");
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
@@ -256,8 +269,19 @@
     @Override // Binder call
     @Nullable
     public VibratorInfo getVibratorInfo(int vibratorId) {
-        VibratorController controller = mVibrators.get(vibratorId);
-        return controller == null ? null : controller.getVibratorInfo();
+        final VibratorController controller = mVibrators.get(vibratorId);
+        if (controller == null) {
+            return null;
+        }
+        final VibratorInfo info = controller.getVibratorInfo();
+        synchronized (mLock) {
+            if (mServiceReady) {
+                return info;
+            }
+        }
+        // If the service is not ready and the load was unsuccessful then return null while waiting
+        // for the service to be ready. It will retry to load the complete info from the HAL.
+        return controller.isVibratorInfoLoadSuccessful() ? info : null;
     }
 
     @Override // Binder call
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d084b13..50f3d8b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -154,6 +154,7 @@
 import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
 import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
 import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING;
+import static com.android.server.wm.ActivityRecordProto.MIN_ASPECT_RATIO;
 import static com.android.server.wm.ActivityRecordProto.NAME;
 import static com.android.server.wm.ActivityRecordProto.NUM_DRAWN_WINDOWS;
 import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS;
@@ -1140,10 +1141,11 @@
             if (info.getMaxAspectRatio() != 0) {
                 pw.println(prefix + "maxAspectRatio=" + info.getMaxAspectRatio());
             }
-            if (info.getMinAspectRatio() != 0) {
-                pw.println(prefix + "minAspectRatio=" + info.getMinAspectRatio());
+            final float minAspectRatio = getMinAspectRatio();
+            if (minAspectRatio != 0) {
+                pw.println(prefix + "minAspectRatio=" + minAspectRatio);
             }
-            if (info.getMinAspectRatio() != info.getManifestMinAspectRatio()) {
+            if (minAspectRatio != info.getManifestMinAspectRatio()) {
                 // Log the fact that we've overridden the min aspect ratio from the manifest
                 pw.println(prefix + "manifestMinAspectRatio="
                         + info.getManifestMinAspectRatio());
@@ -3601,6 +3603,10 @@
 
         if (mPendingRelaunchCount > 0) {
             mPendingRelaunchCount--;
+            if (mPendingRelaunchCount == 0 && !isClientVisible()) {
+                // Don't count if the client won't report drawn.
+                mRelaunchStartTime = 0;
+            }
         } else {
             // Update keyguard flags upon finishing relaunch.
             checkKeyguardFlagsChanged();
@@ -4628,6 +4634,7 @@
         if (app != null) {
             mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
         }
+        logAppCompatState();
     }
 
     /**
@@ -4654,7 +4661,6 @@
                 ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
         mTaskSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
         mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
-        logAppCompatState();
     }
 
     @VisibleForTesting
@@ -4894,7 +4900,7 @@
      */
     private void postApplyAnimation(boolean visible, boolean fromTransition) {
         final boolean usingShellTransitions = mTransitionController.isShellTransitionsEnabled();
-        final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN,
+        final boolean delayed = isAnimating(PARENTS | CHILDREN,
                 ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
                         | ANIMATION_TYPE_RECENTS);
         if (!delayed && !usingShellTransitions) {
@@ -7204,7 +7210,7 @@
                 return false;
             }
         }
-        return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio())
+        return !isResizeable() && (info.isFixedOrientation() || hasFixedAspectRatio())
                 // The configuration of non-standard type should be enforced by system.
                 // {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is
                 // added to a task, but this function is called when resolving the launch params, at
@@ -7443,7 +7449,7 @@
     /**
      * Adjusts horizontal position of resolved bounds if they doesn't fill the parent using gravity
      * requested in the config or via an ADB command. For more context see {@link
-     * WindowManagerService#getLetterboxHorizontalPositionMultiplier}.
+     * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)}.
      */
     private void updateResolvedBoundsHorizontalPosition(Configuration newParentConfiguration) {
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
@@ -7780,7 +7786,7 @@
         // Below figure is an example that puts an activity which was launched in a larger container
         // into a smaller container.
         //   The outermost rectangle is the real display bounds.
-        //   "@" is the container app bounds (parent bounds or fixed orientation bouds)
+        //   "@" is the container app bounds (parent bounds or fixed orientation bounds)
         //   "#" is the {@code resolvedBounds} that applies to application.
         //   "*" is the {@code mSizeCompatBounds} that used to show on screen if scaled.
         // ------------------------------
@@ -7823,12 +7829,15 @@
             mSizeCompatBounds = null;
         }
 
-        // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor
-        // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition.
+        // Vertically center within parent (bounds) - this is a UX choice and exclude the horizontal
+        // decor if needed. Horizontal position is adjusted in
+        // updateResolvedBoundsHorizontalPosition.
         // Above coordinates are in "@" space, now place "*" and "#" to screen space.
         final boolean fillContainer = resolvedBounds.equals(containingBounds);
         final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
-        final int screenPosY = containerBounds.top;
+        final int screenPosY = mSizeCompatBounds == null
+                ? (containerBounds.height() - resolvedBounds.height()) / 2
+                : (containerBounds.height() - mSizeCompatBounds.height()) / 2;
         if (screenPosX != 0 || screenPosY != 0) {
             if (mSizeCompatBounds != null) {
                 mSizeCompatBounds.offset(screenPosX, screenPosY);
@@ -7872,13 +7881,14 @@
                 return false;
             }
         }
-        if (info.getMinAspectRatio() > 0) {
+        final float minAspectRatio = getMinAspectRatio();
+        if (minAspectRatio > 0) {
             // The activity should have at least the min aspect ratio, so this checks if the
             // container still has available space to provide larger aspect ratio.
             final float containerAspectRatio =
                     (0.5f + Math.max(containerAppWidth, containerAppHeight))
                             / Math.min(containerAppWidth, containerAppHeight);
-            if (containerAspectRatio <= info.getMinAspectRatio()) {
+            if (containerAspectRatio <= minAspectRatio) {
                 // The long side has reached the parent.
                 return false;
             }
@@ -8089,8 +8099,7 @@
             Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) {
         final float maxAspectRatio = info.getMaxAspectRatio();
         final Task rootTask = getRootTask();
-        final float minAspectRatio = info.getMinAspectRatio();
-
+        final float minAspectRatio = getMinAspectRatio();
         if (task == null || rootTask == null
                 || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
                 && !fixedOrientationLetterboxed)
@@ -8194,6 +8203,20 @@
     }
 
     /**
+     * Returns the min aspect ratio of this activity.
+     */
+    private float getMinAspectRatio() {
+        return info.getMinAspectRatio(getRequestedOrientation());
+    }
+
+    /**
+     * Returns true if the activity has maximum or minimum aspect ratio.
+     */
+    private boolean hasFixedAspectRatio() {
+        return info.hasFixedAspectRatio(getRequestedOrientation());
+    }
+
+    /**
      * Returns the aspect ratio of the given {@code rect}.
      */
     static float computeAspectRatio(Rect rect) {
@@ -8922,6 +8945,7 @@
         }
         proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
         proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
+        proto.write(MIN_ASPECT_RATIO, getMinAspectRatio());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d944c58..b966ed1 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1599,7 +1599,7 @@
         // transition based on a sub-action.
         // Only do the create here (and defer requestStart) since startActivityInner might abort.
         final TransitionController transitionController = r.mTransitionController;
-        final Transition newTransition = (!transitionController.isCollecting()
+        Transition newTransition = (!transitionController.isCollecting()
                 && transitionController.getTransitionPlayer() != null)
                 ? transitionController.createTransition(TRANSIT_OPEN) : null;
         RemoteTransition remoteTransition = r.takeRemoteTransition();
@@ -1654,6 +1654,10 @@
                     // The activity is started new rather than just brought forward, so record
                     // it as an existence change.
                     transitionController.collectExistenceChange(r);
+                } else if (result == START_DELIVERED_TO_TOP && newTransition != null) {
+                    // We just delivered to top, so there isn't an actual transition here
+                    newTransition.abort();
+                    newTransition = null;
                 }
                 if (isTransient) {
                     // `r` isn't guaranteed to be the actual relevant activity, so we must wait
@@ -2771,9 +2775,11 @@
         // If it exist, we need to reparent target root task from TDA to launch root task.
         final TaskDisplayArea tda = mTargetRootTask.getDisplayArea();
         final Task launchRootTask = tda.getLaunchRootTask(mTargetRootTask.getWindowingMode(),
-                mTargetRootTask.getActivityType(), null /** options */, null /** sourceTask */,
-                0 /** launchFlags */);
-        if (launchRootTask != null && launchRootTask != mTargetRootTask) {
+                mTargetRootTask.getActivityType(), null /** options */,
+                mSourceRootTask, 0 /** launchFlags */);
+        // If target root task is created by organizer, let organizer handle reparent itself.
+        if (!mTargetRootTask.mCreatedByOrganizer && launchRootTask != null
+                && launchRootTask != mTargetRootTask) {
             mTargetRootTask.reparent(launchRootTask, POSITION_TOP);
             mTargetRootTask = launchRootTask;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 9c68f2d..64950c7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.AppProtoEnums;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
@@ -663,13 +664,23 @@
          * This setting is persisted and will overlay on top of the system locales for
          * the said application.
          * @return the current {@link PackageConfigurationUpdater} updated with the provided locale.
+         *
+         * <p>NOTE: This method should not be called by clients directly to set app locales,
+         * instead use the {@link LocaleManagerService#setApplicationLocales}
          */
         PackageConfigurationUpdater setLocales(LocaleList locales);
 
         /**
          * Commit changes.
+         * @return true if the configuration changes were persisted,
+         * false if there were no changes, or if erroneous inputs were provided, such as:
+         * <ui>
+         *     <li>Invalid packageName</li>
+         *     <li>Invalid userId</li>
+         *     <li>no WindowProcessController found for the package</li>
+         * </ui>
          */
-        void commit();
+        boolean commit();
     }
 
     /**
@@ -688,4 +699,7 @@
     public abstract void registerActivityStartInterceptor(
             @ActivityInterceptorCallback.OrderedId int id,
             ActivityInterceptorCallback callback);
+
+    /** Get the most recent task excluding the first running task (the one on the front most). */
+    public abstract ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground();
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 886b05f..a1a357e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6609,5 +6609,33 @@
                 mActivityInterceptorCallbacks.put(id, callback);
             }
         }
+
+        @Override
+        public ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground() {
+            List<ActivityManager.RunningTaskInfo> runningTaskInfoList = getTasks(1);
+            ActivityManager.RunningTaskInfo runningTaskInfo;
+            if (runningTaskInfoList.size() > 0) {
+                runningTaskInfo = runningTaskInfoList.get(0);
+            } else {
+                Slog.i(TAG, "No running task found!");
+                return null;
+            }
+            // Get 2 most recent tasks.
+            List<ActivityManager.RecentTaskInfo> recentTaskInfoList =
+                    getRecentTasks(
+                                    2,
+                                    ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+                                    mContext.getUserId())
+                            .getList();
+            ActivityManager.RecentTaskInfo targetTask = null;
+            for (ActivityManager.RecentTaskInfo info : recentTaskInfoList) {
+                // Find a recent task that is not the current running task on screen.
+                if (info.id != runningTaskInfo.id) {
+                    targetTask = info;
+                    break;
+                }
+            }
+            return targetTask;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 38e1c99..fa43b39 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -31,7 +31,7 @@
 import android.view.InputApplicationHandle;
 
 import com.android.server.am.ActivityManagerService;
-import com.android.server.am.CriticalEventLog;
+import com.android.server.criticalevents.CriticalEventLog;
 
 import java.io.File;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index bd08d01..7817f54 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -81,7 +81,6 @@
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
-import static com.android.internal.policy.TransitionAnimation.prepareThumbnailAnimationWithDuration;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
 import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
@@ -97,7 +96,6 @@
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -126,9 +124,6 @@
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
 import android.view.animation.ScaleAnimation;
 import android.view.animation.TranslateAnimation;
 
@@ -151,25 +146,13 @@
 // made visible or hidden at the next transition.
 public class AppTransition implements Dump {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransition" : TAG_WM;
-    private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8;
-
-    /** Fraction of animation at which the recents thumbnail stays completely transparent */
-    private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
-    /** Fraction of animation at which the recents thumbnail becomes completely transparent */
-    private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
 
     static final int DEFAULT_APP_TRANSITION_DURATION = 336;
 
-    /** Interpolator to be used for animations that respond directly to a touch */
-    static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
-            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
-
     /**
      * Maximum duration for the clip reveal animation. This is used when there is a lot of movement
      * involved, to make it more understandable.
      */
-    private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420;
-    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
     private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
     static final int MAX_APP_TRANSITION_DURATION = 3 * 1000; // 3 secs.
 
@@ -225,11 +208,6 @@
     private boolean mNextAppTransitionAnimationsSpecsPending;
     private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec;
 
-    private Rect mNextAppTransitionInsets = new Rect();
-
-    private Rect mTmpFromClipRect = new Rect();
-    private Rect mTmpToClipRect = new Rect();
-
     private final Rect mTmpRect = new Rect();
 
     private final static int APP_STATE_IDLE = 0;
@@ -238,29 +216,11 @@
     private final static int APP_STATE_TIMEOUT = 3;
     private int mAppTransitionState = APP_STATE_IDLE;
 
-    private final int mConfigShortAnimTime;
-    private final Interpolator mDecelerateInterpolator;
-    private final Interpolator mThumbnailFadeInInterpolator;
-    private final Interpolator mThumbnailFadeOutInterpolator;
-    private final Interpolator mLinearOutSlowInInterpolator;
-    private final Interpolator mFastOutLinearInInterpolator;
-    private final Interpolator mFastOutSlowInInterpolator;
-    private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
-
-    private final int mClipRevealTranslationY;
-
-    private int mCurrentUserId = 0;
-    private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
-
     private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
     private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
     private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
 
-    private int mLastClipRevealMaxTranslation;
-    private boolean mLastHadClipReveal;
-
     private final boolean mGridLayoutRecentsEnabled;
-    private final boolean mLowRamRecentsEnabled;
 
     private final int mDefaultWindowAnimationStyleResId;
     private boolean mOverrideTaskTransition;
@@ -276,43 +236,8 @@
         mHandler = new Handler(service.mH.getLooper());
         mDisplayContent = displayContent;
         mTransitionAnimation = new TransitionAnimation(context, DEBUG_ANIM, TAG);
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.linear_out_slow_in);
-        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_linear_in);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_slow_in);
-        mConfigShortAnimTime = context.getResources().getInteger(
-                com.android.internal.R.integer.config_shortAnimTime);
-        mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.decelerate_cubic);
-        mThumbnailFadeInInterpolator = new Interpolator() {
-            @Override
-            public float getInterpolation(float input) {
-                // Linear response for first fraction, then complete after that.
-                if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
-                    return 0f;
-                }
-                float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) /
-                        (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
-                return mFastOutLinearInInterpolator.getInterpolation(t);
-            }
-        };
-        mThumbnailFadeOutInterpolator = new Interpolator() {
-            @Override
-            public float getInterpolation(float input) {
-                // Linear response for first fraction, then complete after that.
-                if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
-                    float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
-                    return mLinearOutSlowInInterpolator.getInterpolation(t);
-                }
-                return 1f;
-            }
-        };
-        mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP
-                * mContext.getResources().getDisplayMetrics().density);
+
         mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
-        mLowRamRecentsEnabled = ActivityManager.isLowRamDeviceStatic();
 
         final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes(
                 com.android.internal.R.styleable.Window);
@@ -421,9 +346,6 @@
         if (!isRunning()) {
             setAppTransitionState(APP_STATE_IDLE);
             notifyAppTransitionPendingLocked();
-            mLastHadClipReveal = false;
-            mLastClipRevealMaxTranslation = 0;
-            mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
             return true;
         }
         return false;
@@ -602,21 +524,6 @@
         }
     }
 
-    void getNextAppTransitionStartRect(WindowContainer container, Rect rect) {
-        AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
-                container.hashCode());
-        if (spec == null) {
-            spec = mDefaultNextAppTransitionAnimationSpec;
-        }
-        if (spec == null || spec.rect == null) {
-            Slog.e(TAG, "Starting rect for container: " + container
-                            + " requested, but not available", new Throwable());
-            rect.setEmpty();
-        } else {
-            rect.set(spec.rect);
-        }
-    }
-
     private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height,
             HardwareBuffer buffer) {
         mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
@@ -624,49 +531,6 @@
     }
 
     /**
-     * Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
-     * the start rect is outside of the target rect, and there is a lot of movement going on.
-     *
-     * @param cutOff whether the start rect was not fully contained by the end rect
-     * @param translationX the total translation the surface moves in x direction
-     * @param translationY the total translation the surfaces moves in y direction
-     * @param displayFrame our display frame
-     *
-     * @return the duration of the clip reveal animation, in milliseconds
-     */
-    private long calculateClipRevealTransitionDuration(boolean cutOff, float translationX,
-            float translationY, Rect displayFrame) {
-        if (!cutOff) {
-            return DEFAULT_APP_TRANSITION_DURATION;
-        }
-        final float fraction = Math.max(Math.abs(translationX) / displayFrame.width(),
-                Math.abs(translationY) / displayFrame.height());
-        return (long) (DEFAULT_APP_TRANSITION_DURATION + fraction *
-                (MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION));
-    }
-
-    /**
-     * Prepares the specified animation with a standard duration, interpolator, etc.
-     */
-    Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
-        // Pick the desired duration.  If this is an inter-activity transition,
-        // it  is the standard duration for that.  Otherwise we use the longer
-        // task transition duration.
-        final int duration;
-        switch (transit) {
-            case TRANSIT_OLD_ACTIVITY_OPEN:
-            case TRANSIT_OLD_ACTIVITY_CLOSE:
-                duration = mConfigShortAnimTime;
-                break;
-            default:
-                duration = DEFAULT_APP_TRANSITION_DURATION;
-                break;
-        }
-        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
-                mDecelerateInterpolator);
-    }
-
-    /**
      * Creates an overlay with a background color and a thumbnail for the cross profile apps
      * animation.
      */
@@ -694,32 +558,6 @@
                 mNextAppTransitionScaleUp);
     }
 
-    private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
-        return new TranslateAnimation(fromX, toX, fromY, toY);
-    }
-
-    private long getAspectScaleDuration() {
-        return THUMBNAIL_APP_TRANSITION_DURATION;
-    }
-
-    private Interpolator getAspectScaleInterpolator() {
-        return TOUCH_RESPONSE_INTERPOLATOR;
-    }
-
-    private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
-            @Nullable Rect surfaceInsets, WindowContainer container) {
-        getNextAppTransitionStartRect(container, mTmpRect);
-        return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
-                true);
-    }
-
-    private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
-            @Nullable Rect surfaceInsets, WindowContainer container) {
-        getNextAppTransitionStartRect(container, mTmpRect);
-        return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
-                false);
-    }
-
     private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame,
             Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) {
         final float sourceWidth = sourceFrame.width();
@@ -755,48 +593,6 @@
     }
 
     /**
-     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
-     * when a thumbnail is specified with the pending animation override.
-     */
-    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
-            HardwareBuffer thumbnailHeader) {
-        Animation a;
-        getDefaultNextAppTransitionStartRect(mTmpRect);
-        final int thumbWidthI = thumbnailHeader.getWidth();
-        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
-        final int thumbHeightI = thumbnailHeader.getHeight();
-        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
-
-        if (mNextAppTransitionScaleUp) {
-            // Animation for the thumbnail zooming from its initial size to the full screen
-            float scaleW = appWidth / thumbWidth;
-            float scaleH = appHeight / thumbHeight;
-            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
-                    TransitionAnimation.computePivot(mTmpRect.left, 1 / scaleW),
-                    TransitionAnimation.computePivot(mTmpRect.top, 1 / scaleH));
-            scale.setInterpolator(mDecelerateInterpolator);
-
-            Animation alpha = new AlphaAnimation(1, 0);
-            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
-
-            // This AnimationSet uses the Interpolators assigned above.
-            AnimationSet set = new AnimationSet(false);
-            set.addAnimation(scale);
-            set.addAnimation(alpha);
-            a = set;
-        } else {
-            // Animation for the thumbnail zooming down from the full screen to its final size
-            float scaleW = appWidth / thumbWidth;
-            float scaleH = appHeight / thumbHeight;
-            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
-                    TransitionAnimation.computePivot(mTmpRect.left, 1 / scaleW),
-                    TransitionAnimation.computePivot(mTmpRect.top, 1 / scaleH));
-        }
-
-        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
-    }
-
-    /**
      * @return true if and only if the first frame of the transition can be skipped, i.e. the first
      *         frame of the transition doesn't change the visuals on screen, so we can start
      *         directly with the second one
@@ -1545,10 +1341,6 @@
         }
     }
 
-    public void setCurrentUser(int newUserId) {
-        mCurrentUserId = newUserId;
-    }
-
     boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
         if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
             return false;
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 4355b38..2a8ac39e 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -20,9 +20,11 @@
 
 import android.annotation.NonNull;
 import android.util.ArraySet;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.SurfaceControl;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 
 /**
@@ -65,6 +67,7 @@
     class SyncGroup {
         final int mSyncId;
         final TransactionReadyListener mListener;
+        final Runnable mOnTimeout;
         boolean mReady = false;
         final ArraySet<WindowContainer> mRootMembers = new ArraySet<>();
         private SurfaceControl.Transaction mOrphanTransaction = null;
@@ -72,6 +75,12 @@
         private SyncGroup(TransactionReadyListener listener, int id) {
             mSyncId = id;
             mListener = listener;
+            mOnTimeout = () -> {
+                Slog.w(TAG, "Sync group " + mSyncId + " timeout");
+                synchronized (mWm.mGlobalLock) {
+                    onTimeout();
+                }
+            };
         }
 
         /**
@@ -114,6 +123,7 @@
             }
             mListener.onTransactionReady(mSyncId, merged);
             mActiveSyncs.remove(mSyncId);
+            mWm.mH.removeCallbacks(mOnTimeout);
         }
 
         private void setReady(boolean ready) {
@@ -136,6 +146,17 @@
         void onCancelSync(WindowContainer wc) {
             mRootMembers.remove(wc);
         }
+
+        private void onTimeout() {
+            if (!mActiveSyncs.contains(mSyncId)) return;
+            for (int i = mRootMembers.size() - 1; i >= 0; --i) {
+                final WindowContainer<?> wc = mRootMembers.valueAt(i);
+                if (!wc.isSyncFinished()) {
+                    Slog.i(TAG, "Unfinished container: " + wc);
+                }
+            }
+            finishNow();
+        }
     }
 
     private final WindowManagerService mWm;
@@ -147,13 +168,23 @@
     }
 
     int startSyncSet(TransactionReadyListener listener) {
+        return startSyncSet(listener, WindowState.BLAST_TIMEOUT_DURATION);
+    }
+
+    int startSyncSet(TransactionReadyListener listener, long timeoutMs) {
         final int id = mNextSyncId++;
         final SyncGroup s = new SyncGroup(listener, id);
         mActiveSyncs.put(id, s);
         ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s", id, listener);
+        scheduleTimeout(s, timeoutMs);
         return id;
     }
 
+    @VisibleForTesting
+    void scheduleTimeout(SyncGroup s, long timeoutMs) {
+        mWm.mH.postDelayed(s.mOnTimeout, timeoutMs);
+    }
+
     void addToSyncSet(int id, WindowContainer wc) {
         mActiveSyncs.get(id).addToSync(wc);
     }
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 98757f0..7485a1e 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -272,6 +272,10 @@
 
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
+        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+            return;
+        }
+
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(NAME, mName);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ce6f292..cfe1aef 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -91,6 +91,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -117,6 +118,7 @@
 import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -3200,6 +3202,11 @@
         }
         final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, this);
         if (t != null) {
+            if (getRotation() != getWindowConfiguration().getRotation()) {
+                mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
+                controller.mTransitionMetricsReporter.associate(t,
+                        startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
+            }
             t.setKnownConfigChanges(this, changes);
         }
     }
@@ -3418,7 +3425,9 @@
 
     @Override
     public String toString() {
-        return "Display " + mDisplayId + " info=" + mDisplayInfo + " rootTasks=" + mChildren;
+        return "Display{#" + mDisplayId + " state=" + Display.stateToString(mDisplayInfo.state)
+                + " size=" + mDisplayInfo.logicalWidth + "x" + mDisplayInfo.logicalHeight
+                + " " + Surface.rotationToString(mDisplayInfo.rotation) + "}";
     }
 
     String getName() {
@@ -4935,15 +4944,20 @@
         // Keep IME window in surface parent as long as app's starting window
         // exists so it get's layered above the starting window.
         if (imeTarget != null && !(imeTarget.mActivityRecord != null
-                && imeTarget.mActivityRecord.hasStartingWindow()) && (
-                !(imeTarget.inMultiWindowMode()
-                        || imeTarget.mToken.isAppTransitioning()) && (
-                        imeTarget.getSurfaceControl() != null))) {
-            mImeWindowsContainer.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
-                    // TODO: We need to use an extra level on the app surface to ensure
-                    // this is always above SurfaceView but always below attached window.
-                    1, forceUpdate);
-        } else if (mInputMethodSurfaceParent != null) {
+                && imeTarget.mActivityRecord.hasStartingWindow())) {
+            final boolean canImeTargetSetRelativeLayer = imeTarget.getSurfaceControl() != null
+                    && !imeTarget.inMultiWindowMode()
+                    && imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS,
+                            ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null;
+            if (canImeTargetSetRelativeLayer) {
+                mImeWindowsContainer.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
+                        // TODO: We need to use an extra level on the app surface to ensure
+                        // this is always above SurfaceView but always below attached window.
+                        1, forceUpdate);
+                return;
+            }
+        }
+        if (mInputMethodSurfaceParent != null) {
             // The IME surface parent may not be its window parent's surface
             // (@see #computeImeParent), so set relative layer here instead of letting the window
             // parent to assign layer.
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 32e43ca..45d7141 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -84,7 +84,7 @@
         final Rect safe = mDisplayCutoutSafe;
         final DisplayCutout cutout = displayCutout.getDisplayCutout();
         if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight
-                 && state.getDisplayCutout().equals(cutout)
+                && state.getDisplayCutout().equals(cutout)
                 && state.getRoundedCorners().equals(roundedCorners)
                 && state.getPrivacyIndicatorBounds().equals(indicatorBounds)) {
             return false;
@@ -93,24 +93,12 @@
         mDisplayHeight = info.logicalHeight;
         final Rect unrestricted = mUnrestricted;
         unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
-        safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
         state.setDisplayFrame(unrestricted);
         state.setDisplayCutout(cutout);
         state.setRoundedCorners(roundedCorners);
         state.setPrivacyIndicatorBounds(indicatorBounds);
+        state.getDisplayCutoutSafe(safe);
         if (!cutout.isEmpty()) {
-            if (cutout.getSafeInsetLeft() > 0) {
-                safe.left = unrestricted.left + cutout.getSafeInsetLeft();
-            }
-            if (cutout.getSafeInsetTop() > 0) {
-                safe.top = unrestricted.top + cutout.getSafeInsetTop();
-            }
-            if (cutout.getSafeInsetRight() > 0) {
-                safe.right = unrestricted.right - cutout.getSafeInsetRight();
-            }
-            if (cutout.getSafeInsetBottom() > 0) {
-                safe.bottom = unrestricted.bottom - cutout.getSafeInsetBottom();
-            }
             state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(
                     unrestricted.left, unrestricted.top, safe.left, unrestricted.bottom);
             state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 54078c4..253b3a5 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -29,7 +29,6 @@
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
@@ -37,7 +36,6 @@
 import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
 import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
-import static android.view.ViewRootImpl.computeWindowBounds;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -50,21 +48,15 @@
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
@@ -147,6 +139,7 @@
 import android.view.ViewDebug;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowLayout;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerGlobal;
@@ -157,6 +150,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.policy.SystemBarUtils;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.function.TriConsumer;
@@ -356,15 +350,15 @@
     // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
     private long mPendingPanicGestureUptime;
 
-    private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
     private static final Rect sTmpRect = new Rect();
     private static final Rect sTmpNavFrame = new Rect();
     private static final Rect sTmpStatusFrame = new Rect();
     private static final Rect sTmpDecorFrame = new Rect();
-    private static final Rect sTmpScreenDecorFrame = new Rect();
     private static final Rect sTmpLastParentFrame = new Rect();
     private static final Rect sTmpDisplayFrameBounds = new Rect();
 
+    private final WindowLayout mWindowLayout = new WindowLayout();
+
     private WindowState mTopFullscreenOpaqueWindowState;
     private boolean mTopIsFullscreen;
     private boolean mForceStatusBar;
@@ -1111,9 +1105,6 @@
         switch (attrs.type) {
             case TYPE_NOTIFICATION_SHADE:
                 mNotificationShade = win;
-                if (mDisplayContent.isDefaultDisplay) {
-                    mService.mPolicy.setKeyguardCandidateLw(win);
-                }
                 break;
             case TYPE_STATUS_BAR:
                 mStatusBar = win;
@@ -1309,9 +1300,6 @@
             mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
         } else if (mNotificationShade == win) {
             mNotificationShade = null;
-            if (mDisplayContent.isDefaultDisplay) {
-                mService.mPolicy.setKeyguardCandidateLw(null);
-            }
         } else if (mClimateBarAlt == win) {
             mClimateBarAlt = null;
             mDisplayContent.setInsetProvider(ITYPE_CLIMATE_BAR, null, null);
@@ -1338,6 +1326,11 @@
         return Math.max(statusBarHeight, displayFrames.mDisplayCutoutSafe.top);
     }
 
+    @VisibleForTesting
+    int getStatusBarHeightForRotation(@Surface.Rotation int rotation) {
+        return SystemBarUtils.getStatusBarHeightForRotation(mUiContext, rotation);
+    }
+
     WindowState getStatusBar() {
         return mStatusBar != null ? mStatusBar : mStatusBarAlt;
     }
@@ -1484,7 +1477,7 @@
      * @param outInsetsState The insets state of this display from the client's perspective.
      * @param localClient Whether the client is from the our process.
      * @return Whether to always consume the system bars.
-     *         See {@link #areSystemBarsForcedShownLw(WindowState)}.
+     *         See {@link #areSystemBarsForcedShownLw()}.
      */
     boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, InsetsState outInsetsState,
             boolean localClient) {
@@ -1607,8 +1600,6 @@
         // For layout, the status bar is always at the top with our fixed height.
         int statusBarBottom = displayFrames.mUnrestricted.top
                 + mStatusBarHeightForRotation[displayFrames.mRotation];
-        // Make sure the status bar covers the entire cutout height
-        statusBarBottom = Math.max(statusBarBottom, displayFrames.mDisplayCutoutSafe.top);
 
         if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
             // Make sure that the zone we're avoiding for the cutout is at least as tall as the
@@ -1721,108 +1712,24 @@
 
         final int type = attrs.type;
         final int fl = attrs.flags;
-        final int pfl = attrs.privateFlags;
         final int sim = attrs.softInputMode;
 
         displayFrames = win.getDisplayFrames(displayFrames);
         final WindowFrames windowFrames = win.getLayoutingWindowFrames();
 
-        sTmpLastParentFrame.set(windowFrames.mParentFrame);
         final Rect pf = windowFrames.mParentFrame;
         final Rect df = windowFrames.mDisplayFrame;
-        windowFrames.setParentFrameWasClippedByDisplayCutout(false);
+        final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null;
+        sTmpLastParentFrame.set(pf);
 
-        final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
-        final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
+        // Override the bounds in window token has many side effects. Directly use the display
+        // frame set for the simulated layout for this case.
+        final Rect winBounds = windowFrames.mIsSimulatingDecorWindow ? df : win.getBounds();
 
-        final InsetsState state = win.getInsetsState();
-        if (windowFrames.mIsSimulatingDecorWindow && INSETS_LAYOUT_GENERALIZATION) {
-            // Override the bounds in window token has many side effects. Directly use the display
-            // frame set for the simulated layout for this case.
-            computeWindowBounds(attrs, state, df, df);
-        } else {
-            computeWindowBounds(attrs, state, win.getBounds(), df);
-        }
-        if (attached == null) {
-            pf.set(df);
-            if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
-                final InsetsSource source = state.peekSource(ITYPE_IME);
-                if (source != null) {
-                    pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */));
-                }
-            }
-        } else {
-            pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
-        }
-
-        final int cutoutMode = attrs.layoutInDisplayCutoutMode;
-        // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
-        // the cutout safe zone.
-        if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
-            final boolean attachedInParent = attached != null && !layoutInScreen;
-            final boolean requestedFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR);
-            final boolean requestedHideNavigation =
-                    !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
-
-            // TYPE_BASE_APPLICATION windows are never considered floating here because they don't
-            // get cropped / shifted to the displayFrame in WindowState.
-            final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
-                    && type != TYPE_BASE_APPLICATION;
-            final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
-            displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
-            if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
-                if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) {
-                    displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
-                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
-                } else {
-                    displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
-                    displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
-                }
-            }
-
-            if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
-                    && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
-                    || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
-                // At the top we have the status bar, so apps that are
-                // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
-                // already expect that there's an inset there and we don't need to exclude
-                // the window from that area.
-                displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
-            }
-            if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
-                    && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
-                    || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
-                // Same for the navigation bar.
-                switch (mNavigationBarPosition) {
-                    case NAV_BAR_BOTTOM:
-                        displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
-                        break;
-                    case NAV_BAR_RIGHT:
-                        displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
-                        break;
-                    case NAV_BAR_LEFT:
-                        displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
-                        break;
-                }
-            }
-            if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) {
-                // The IME can always extend under the bottom cutout if the navbar is there.
-                displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
-            }
-            // Windows that are attached to a parent and laid out in said parent already avoid
-            // the cutout according to that parent and don't need to be further constrained.
-            // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
-            // They will later be cropped or shifted using the displayFrame in WindowState,
-            // which prevents overlap with the DisplayCutout.
-            if (!attachedInParent && !floatingInScreenWindow) {
-                sTmpRect.set(pf);
-                pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
-                windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
-            }
-            // Make sure that NO_LIMITS windows clipped to the display don't extend under the
-            // cutout.
-            df.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
-        }
+        final boolean clippedByDisplayCutout = mWindowLayout.computeWindowFrames(attrs,
+                win.getInsetsState(), displayFrames.mDisplayCutoutSafe, winBounds,
+                win.getRequestedVisibilities(), attachedWindowFrame, df, pf);
+        windowFrames.setParentFrameWasClippedByDisplayCutout(clippedByDisplayCutout);
 
         // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
         // Also, we don't allow windows in multi-window mode to extend out of the screen.
@@ -1843,16 +1750,6 @@
         }
 
         win.computeFrameAndUpdateSourceFrame(displayFrames);
-        if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_STATUS_BAR) {
-            if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
-                // Make sure that the zone we're avoiding for the cutout is at least as tall as the
-                // status bar; otherwise fullscreen apps will end up cutting halfway into the status
-                // bar.
-                displayFrames.mDisplayCutoutSafe.top = Math.max(
-                        displayFrames.mDisplayCutoutSafe.top,
-                        windowFrames.mFrame.bottom);
-            }
-        }
     }
 
     WindowState getTopFullscreenOpaqueWindow() {
@@ -1890,6 +1787,13 @@
      */
     public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
             WindowState attached, WindowState imeTarget) {
+        if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_NAVIGATION_BAR) {
+            // Keep mNavigationBarPosition updated to make sure the transient detection and bar
+            // color control is working correctly.
+            final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames;
+            mNavigationBarPosition = navigationBarPosition(displayFrames.mDisplayWidth,
+                    displayFrames.mDisplayHeight, displayFrames.mRotation);
+        }
         final boolean affectsSystemUi = win.canAffectSystemUiFlags();
         if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
         applyKeyguardPolicy(win, imeTarget);
@@ -2141,10 +2045,11 @@
         if (hasStatusBar()) {
             mStatusBarHeightForRotation[portraitRotation] =
                     mStatusBarHeightForRotation[upsideDownRotation] =
-                            res.getDimensionPixelSize(R.dimen.status_bar_height_portrait);
+                            getStatusBarHeightForRotation(portraitRotation);
             mStatusBarHeightForRotation[landscapeRotation] =
-                    mStatusBarHeightForRotation[seascapeRotation] =
-                            res.getDimensionPixelSize(R.dimen.status_bar_height_landscape);
+                    getStatusBarHeightForRotation(landscapeRotation);
+            mStatusBarHeightForRotation[seascapeRotation] =
+                    getStatusBarHeightForRotation(seascapeRotation);
             mDisplayCutoutTouchableRegionSize = res.getDimensionPixelSize(
                     R.dimen.display_cutout_touchable_region_size);
         } else {
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 081a53e..6de8ef7 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -172,7 +172,7 @@
     }
 
     @GuardedBy("mLock")
-    void updateFromImpl(String packageName, int userId,
+    boolean updateFromImpl(String packageName, int userId,
             PackageConfigurationUpdaterImpl impl) {
         synchronized (mLock) {
             PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId);
@@ -198,7 +198,7 @@
                 }
 
                 if (!updateNightMode(record, writeRecord) && !updateLocales(record, writeRecord)) {
-                    return;
+                    return false;
                 }
 
                 if (DEBUG) {
@@ -206,6 +206,7 @@
                 }
                 mPersisterQueue.addItem(new WriteProcessItem(writeRecord), false /* flush */);
             }
+            return true;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
index 8bbcf1f..0cd3680 100644
--- a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
+++ b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
@@ -68,7 +68,7 @@
     }
 
     @Override
-    public void commit() {
+    public boolean commit() {
         synchronized (this) {
             synchronized (mAtm.mGlobalLock) {
                 final long ident = Binder.clearCallingIdentity();
@@ -79,7 +79,7 @@
                         if (wpc == null) {
                             Slog.w(TAG, "commit: Override application configuration failed: "
                                     + "cannot find pid " + mPid);
-                            return;
+                            return false;
                         }
                         uid = wpc.mUid;
                         mUserId = wpc.mUserId;
@@ -90,11 +90,12 @@
                         if (uid < 0) {
                             Slog.w(TAG, "commit: update of application configuration failed: "
                                     + "userId or packageName not valid " + mUserId);
-                            return;
+                            return false;
                         }
                     }
                     updateConfig(uid, mPackageName);
-                    mAtm.mPackageConfigPersister.updateFromImpl(mPackageName, mUserId, this);
+                    return mAtm.mPackageConfigPersister
+                            .updateFromImpl(mPackageName, mUserId, this);
 
                 } finally {
                     Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 16e77f0..7e784ae 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1209,6 +1209,11 @@
                 }
             }
         }
+        // For better split UX, If task launch by the source task which root task is created by
+        // organizer, it should also launch in that root too.
+        if (sourceTask != null && sourceTask.getRootTask().mCreatedByOrganizer) {
+            return sourceTask.getRootTask();
+        }
         return null;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index d543c1f..b8ceb4a 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -818,7 +818,7 @@
         final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;
 
         // Aspect ratio requirements.
-        final float minAspectRatio = info.getMinAspectRatio();
+        final float minAspectRatio = info.getMinAspectRatio(orientation);
         final float maxAspectRatio = info.getMaxAspectRatio();
 
         final int width = Math.min(defaultWidth, Math.max(phoneWidth, layoutMinWidth));
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 8bd6257..5d82553 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -165,13 +165,13 @@
     private boolean mNavBarAttachedToApp = false;
     private int mRecentsDisplayId = INVALID_DISPLAY;
 
-    Transition(@TransitionType int type, @TransitionFlags int flags,
+    Transition(@TransitionType int type, @TransitionFlags int flags, long timeoutMs,
             TransitionController controller, BLASTSyncEngine syncEngine) {
         mType = type;
         mFlags = flags;
         mController = controller;
         mSyncEngine = syncEngine;
-        mSyncId = mSyncEngine.startSyncSet(this);
+        mSyncId = mSyncEngine.startSyncSet(this, timeoutMs);
     }
 
     void addFlag(int flag) {
@@ -469,6 +469,7 @@
         if (mState != STATE_COLLECTING) {
             throw new IllegalStateException("Too late to abort.");
         }
+        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Aborting Transition: %d", mSyncId);
         mController.dispatchLegacyAppTransitionCancelled();
         mState = STATE_ABORT;
         // Syncengine abort will call through to onTransactionReady()
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index b849170..91825cc 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
@@ -30,10 +31,12 @@
 import android.os.IRemoteCallback;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.WindowManager;
 import android.window.IRemoteTransition;
+import android.window.ITransitionMetricsReporter;
 import android.window.ITransitionPlayer;
 import android.window.RemoteTransition;
 import android.window.TransitionInfo;
@@ -45,6 +48,7 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.util.ArrayList;
+import java.util.function.LongConsumer;
 
 /**
  * Handles all the aspects of recording and synchronizing transitions.
@@ -52,12 +56,19 @@
 class TransitionController {
     private static final String TAG = "TransitionController";
 
+    /** The same as legacy APP_TRANSITION_TIMEOUT_MS. */
+    private static final int DEFAULT_TIMEOUT_MS = 5000;
+    /** Less duration for CHANGE type because it does not involve app startup. */
+    private static final int CHANGE_TIMEOUT_MS = 2000;
+
     // State constants to line-up with legacy app-transition proto expectations.
     private static final int LEGACY_STATE_IDLE = 0;
     private static final int LEGACY_STATE_READY = 1;
     private static final int LEGACY_STATE_RUNNING = 2;
 
     private ITransitionPlayer mTransitionPlayer;
+    final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter();
+
     final ActivityTaskManagerService mAtm;
     final TaskSnapshotController mTaskSnapshotController;
 
@@ -116,7 +127,10 @@
         if (mCollectingTransition != null) {
             throw new IllegalStateException("Simultaneous transitions not supported yet.");
         }
-        mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);
+        // Distinguish change type because the response time is usually expected to be not too long.
+        final long timeoutMs = type == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS;
+        mCollectingTransition = new Transition(type, flags, timeoutMs, this,
+                mAtm.mWindowManager.mSyncEngine);
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s",
                 mCollectingTransition);
         dispatchLegacyAppTransitionPending();
@@ -324,6 +338,8 @@
 
     /** @see Transition#finishTransition */
     void finishTransition(@NonNull IBinder token) {
+        // It is usually a no-op but make sure that the metric consumer is removed.
+        mTransitionMetricsReporter.reportAnimationStart(token, 0 /* startTime */);
         final Transition record = Transition.fromBinder(token);
         if (record == null || !mPlayingTransitions.contains(record)) {
             Slog.e(TAG, "Trying to finish a non-playing transition " + token);
@@ -420,6 +436,28 @@
         proto.end(token);
     }
 
+    static class TransitionMetricsReporter extends ITransitionMetricsReporter.Stub {
+        private final ArrayMap<IBinder, LongConsumer> mMetricConsumers = new ArrayMap<>();
+
+        void associate(IBinder transitionToken, LongConsumer consumer) {
+            synchronized (mMetricConsumers) {
+                mMetricConsumers.put(transitionToken, consumer);
+            }
+        }
+
+        @Override
+        public void reportAnimationStart(IBinder transitionToken, long startTime) {
+            final LongConsumer c;
+            synchronized (mMetricConsumers) {
+                if (mMetricConsumers.isEmpty()) return;
+                c = mMetricConsumers.remove(transitionToken);
+            }
+            if (c != null) {
+                c.accept(startTime);
+            }
+        }
+    }
+
     class Lock {
         private int mTransitionWaiters = 0;
         void runWhenIdle(long timeout, Runnable r) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index a92e088..d93b649 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -801,6 +801,18 @@
                 wallpaperBuffer.getHardwareBuffer(), wallpaperBuffer.getColorSpace());
     }
 
+    /**
+     * Mirrors the visible wallpaper if it's available.
+     *
+     * @return A SurfaceControl for the parent of the mirrored wallpaper.
+     */
+    SurfaceControl mirrorWallpaperSurface() {
+        final WindowState wallpaperWindowState = getTopVisibleWallpaper();
+        return wallpaperWindowState != null
+                ? SurfaceControl.mirrorSurface(wallpaperWindowState.getSurfaceControl())
+                : null;
+    }
+
     WindowState getTopVisibleWallpaper() {
         mTmpTopWallpaper = null;
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ecaa3c4..c4a5183 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.Manifest.permission.INPUT_CONSUMER;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
 import static android.Manifest.permission.MANAGE_APP_TOKENS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
@@ -268,6 +269,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.TaskTransitionSpec;
 import android.view.View;
 import android.view.WindowContentFrameStats;
 import android.view.WindowInsets;
@@ -434,7 +436,7 @@
             "persist.wm.enable_remote_keyguard_animation";
 
     private static final int sEnableRemoteKeyguardAnimation =
-            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 0);
+            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 1);
 
     /**
      * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
@@ -756,6 +758,11 @@
      */
     final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper());
 
+    /**
+     * Used during task transitions to allow SysUI and launcher to customize task transitions.
+     */
+    TaskTransitionSpec mTaskTransitionSpec;
+
     boolean mHardKeyboardAvailable;
     WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
     SettingsObserver mSettingsObserver;
@@ -3461,8 +3468,6 @@
             // Notify whether the root docked task exists for the current user
             final DisplayContent displayContent = getDefaultDisplayContentLocked();
 
-            mRoot.forAllDisplays(dc -> dc.mAppTransition.setCurrentUser(newUserId));
-
             // If the display is already prepared, update the density.
             // Otherwise, we'll update it when it's prepared.
             if (mDisplayReady) {
@@ -3781,6 +3786,14 @@
         }
     }
 
+    @Override
+    public SurfaceControl mirrorWallpaperSurface(int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+            return dc.mWallpaperController.mirrorWallpaperSurface();
+        }
+    }
+
     /**
      * Takes a snapshot of the screen.  In landscape mode this grabs the whole screen.
      * In portrait mode, it grabs the upper region of the screen based on the vertical dimension
@@ -5377,6 +5390,7 @@
                 case WINDOW_STATE_BLAST_SYNC_TIMEOUT: {
                     synchronized (mGlobalLock) {
                         final WindowState ws = (WindowState) msg.obj;
+                        Slog.i(TAG, "Blast sync timeout: " + ws);
                         ws.immediatelyNotifyBlastSync();
                     }
                     break;
@@ -5817,7 +5831,9 @@
             return;
         }
 
-        if (!displayContent.isReady() || !mPolicy.isScreenOn() || !displayContent.okToAnimate()) {
+        if (!displayContent.isReady() || !displayContent.getDisplayPolicy().isScreenOnFully()
+                || displayContent.getDisplayInfo().state == Display.STATE_OFF
+                || !displayContent.okToAnimate()) {
             // No need to freeze the screen before the display is ready,  if the screen is off,
             // or we can't currently animate.
             return;
@@ -8704,4 +8720,22 @@
     public void setTaskSnapshotEnabled(boolean enabled) {
         mTaskSnapshotController.setTaskSnapshotEnabled(enabled);
     }
+
+    @Override
+    public void setTaskTransitionSpec(TaskTransitionSpec spec) {
+        if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS, "setTaskTransitionSpec()")) {
+            throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
+        }
+
+        mTaskTransitionSpec = spec;
+    }
+
+    @Override
+    public void clearTaskTransitionSpec() {
+        if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS, "clearTaskTransitionSpec()")) {
+            throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
+        }
+
+        mTaskTransitionSpec = null;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 968c635..79a46ea 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -66,6 +66,7 @@
 import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskFragmentOrganizerController;
 import android.window.ITaskOrganizerController;
+import android.window.ITransitionMetricsReporter;
 import android.window.ITransitionPlayer;
 import android.window.IWindowContainerTransactionCallback;
 import android.window.IWindowOrganizerController;
@@ -1039,6 +1040,11 @@
         }
     }
 
+    @Override
+    public ITransitionMetricsReporter getTransitionMetricsReporter() {
+        return mTransitionController.mTransitionMetricsReporter;
+    }
+
     /** Whether the configuration changes are important to report back to an organizer. */
     static boolean configurationsAreEqualForOrganizer(
             Configuration newConfig, @Nullable Configuration oldConfig) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index af38641..81878e3 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1559,7 +1559,9 @@
                 // activity as it could lead to incorrect display metrics. For ex, IME services
                 // expect their config to match the config of the display with the IME window
                 // showing.
+                // If the configuration has been overridden by previous activity, empty it.
                 mIsActivityConfigOverrideAllowed = false;
+                unregisterActivityConfigurationListener();
                 break;
             default:
                 break;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f77b6f2..d68a8ea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -259,7 +259,6 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.utils.CoordinateTransforms;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -1177,14 +1176,13 @@
 
     /**
      * @return {@code true} if the application runs in size compatibility mode or has an app level
-     * scaling override set. This method always returns {@code false} on child window because it
-     * should follow parent's scale.
+     * scaling override set.
      * @see CompatModePackages#getCompatScale
      * @see android.content.res.CompatibilityInfo#supportsScreen
      * @see ActivityRecord#hasSizeCompatBounds()
      */
     boolean hasCompatScale() {
-        return (mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord)) && !mIsChildWindow;
+        return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord);
     }
 
     /**
@@ -4478,22 +4476,6 @@
             h = Math.min(h, ph);
         }
 
-        if (mIsChildWindow) {
-            final WindowState parent = getTopParentWindow();
-            if (parent.hasCompatScale()) {
-                // Scale the containing and display frames because they are in screen coordinates.
-                // The position of frames are already relative to parent so only size is scaled.
-                mTmpRect.set(containingFrame);
-                containingFrame = mTmpRect;
-                CoordinateTransforms.scaleRectSize(containingFrame, parent.mInvGlobalScale);
-                if (fitToDisplay) {
-                    mTmpRect2.set(displayFrame);
-                    displayFrame = mTmpRect2;
-                    CoordinateTransforms.scaleRectSize(displayFrame, parent.mInvGlobalScale);
-                }
-            }
-        }
-
         // Set mFrame
         Gravity.apply(attrs.gravity, w, h, containingFrame,
                 (int) (x + attrs.horizontalMargin * pw),
@@ -5124,19 +5106,6 @@
         }
     }
 
-    /**
-     * Expand the given rectangle by this windows surface insets. This
-     * takes you from the 'window size' to the 'surface size'.
-     * The surface insets are positive in each direction, so we inset by
-     * the inverse.
-     */
-    void expandForSurfaceInsets(Rect r) {
-        r.inset(-mAttrs.surfaceInsets.left,
-                -mAttrs.surfaceInsets.top,
-                -mAttrs.surfaceInsets.right,
-                -mAttrs.surfaceInsets.bottom);
-    }
-
     boolean surfaceInsetsChanging() {
         return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets);
     }
@@ -5454,6 +5423,10 @@
     }
 
     private void updateScaleIfNeeded() {
+        if (mIsChildWindow) {
+            // Child window follows parent's scale.
+            return;
+        }
         float newHScale = mHScale * mGlobalScale * mWallpaperScale;
         float newVScale = mVScale * mGlobalScale * mWallpaperScale;
         if (mLastHScale != newHScale ||
@@ -5537,15 +5510,18 @@
         // If changed, also adjust getTransformationMatrix
         final WindowContainer parentWindowContainer = getParent();
         if (isChildWindow()) {
-            // TODO: This probably falls apart at some point and we should
-            // actually compute relative coordinates.
-
+            final WindowState parent = getParentWindow();
+            outPoint.offset(-parent.mWindowFrames.mFrame.left, -parent.mWindowFrames.mFrame.top);
+            // Undo the scale of window position because the relative coordinates for child are
+            // based on the scaled parent.
+            if (mInvGlobalScale != 1f) {
+                outPoint.x = (int) (outPoint.x * mInvGlobalScale + 0.5f);
+                outPoint.y = (int) (outPoint.y * mInvGlobalScale + 0.5f);
+            }
             // Since the parent was outset by its surface insets, we need to undo the outsetting
             // with insetting by the same amount.
-            final WindowState parent = getParentWindow();
             transformSurfaceInsetsPosition(mTmpPoint, parent.mAttrs.surfaceInsets);
-            outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x,
-                    -parent.mWindowFrames.mFrame.top + mTmpPoint.y);
+            outPoint.offset(mTmpPoint.x, mTmpPoint.y);
         } else if (parentWindowContainer != null) {
             final Rect parentBounds = isStartingWindowAssociatedToTask()
                     ? mStartingData.mAssociatedTask.getBounds()
@@ -5564,7 +5540,7 @@
             outPoint.offset(outset, outset);
         }
 
-        // Expand for surface insets. See WindowState.expandForSurfaceInsets.
+        // The surface size is larger than the window if the window has positive surface insets.
         transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
         outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);
 
@@ -5576,7 +5552,9 @@
      * scaled, the insets also need to be scaled for surface position in global coordinate.
      */
     private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) {
-        if (!hasCompatScale()) {
+        // Ignore the scale for child window because its insets have been scaled with the
+        // parent surface.
+        if (mGlobalScale == 1f || mIsChildWindow) {
             outPos.x = surfaceInsets.left;
             outPos.y = surfaceInsets.top;
             return;
@@ -5930,16 +5908,13 @@
         // in WAITING state rather than READY.
         mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
         requestRedrawForSync();
-
-        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
-        mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
-            BLAST_TIMEOUT_DURATION);
         return true;
     }
 
     @Override
     boolean isSyncFinished() {
-        if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mViewVisibility == View.GONE) {
+        if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mViewVisibility == View.GONE
+                && !isVisibleRequested()) {
             // Don't wait for GONE windows. However, we don't alter the state in case the window
             // becomes un-gone while the syncset is still active.
             return true;
diff --git a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
index 6d8e07a..a2f37a5 100644
--- a/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
+++ b/services/core/java/com/android/server/wm/utils/CoordinateTransforms.java
@@ -152,10 +152,4 @@
         transform.mapRect(tmp);
         inOutRect.set((int) tmp.left, (int) tmp.top, (int) tmp.right, (int) tmp.bottom);
     }
-
-    /** Scales the rect without changing its position. */
-    public static void scaleRectSize(Rect inOutRect, float scale) {
-        inOutRect.right = inOutRect.left + (int) (inOutRect.width() * scale + .5f);
-        inOutRect.bottom = inOutRect.top + (int) (inOutRect.height() * scale + .5f);
-    }
 }
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 9029fe7..546b075 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -443,7 +443,7 @@
     env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
                           frequencyMapping);
 
-    return info.checkAndLogFailure("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
+    return info.isFailedLogged("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
 }
 
 static const JNINativeMethod method_table[] = {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 64c426b..349eb22 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -24,17 +24,17 @@
 import android.content.pm.PackageManager
 import android.content.pm.SigningDetails
 import android.content.pm.parsing.ParsingPackage
-import android.content.pm.parsing.component.ParsedActivity
-import android.content.pm.parsing.component.ParsedAttribution
-import android.content.pm.parsing.component.ParsedComponent
-import android.content.pm.parsing.component.ParsedInstrumentation
-import android.content.pm.parsing.component.ParsedIntentInfo
-import android.content.pm.parsing.component.ParsedPermission
-import android.content.pm.parsing.component.ParsedPermissionGroup
-import android.content.pm.parsing.component.ParsedProcess
-import android.content.pm.parsing.component.ParsedProvider
-import android.content.pm.parsing.component.ParsedService
-import android.content.pm.parsing.component.ParsedUsesPermission
+import android.content.pm.parsing.component.ParsedActivityImpl
+import android.content.pm.parsing.component.ParsedAttributionImpl
+import android.content.pm.parsing.component.ParsedComponentImpl
+import android.content.pm.parsing.component.ParsedInstrumentationImpl
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
+import android.content.pm.parsing.component.ParsedPermissionGroupImpl
+import android.content.pm.parsing.component.ParsedPermissionImpl
+import android.content.pm.parsing.component.ParsedProcessImpl
+import android.content.pm.parsing.component.ParsedProviderImpl
+import android.content.pm.parsing.component.ParsedServiceImpl
+import android.content.pm.parsing.component.ParsedUsesPermissionImpl
 import android.net.Uri
 import android.os.Bundle
 import android.os.Parcelable
@@ -43,14 +43,12 @@
 import android.util.SparseIntArray
 import com.android.internal.R
 import com.android.server.pm.parsing.pkg.AndroidPackage
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils
 import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
 import java.security.KeyPairGenerator
 import java.security.PublicKey
 import kotlin.contracts.ExperimentalContracts
-import kotlin.reflect.KFunction1
 
 @ExperimentalContracts
 class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, PackageImpl::class) {
@@ -285,7 +283,7 @@
             PackageImpl::addAttribution,
             Triple("testTag", 13, listOf("testInherit")),
             transformGet = { it.singleOrNull()?.let { Triple(it.tag, it.label, it.inheritFrom) } },
-            transformSet = { it?.let { ParsedAttribution(it.first, it.second, it.third) } }
+            transformSet = { it?.let { ParsedAttributionImpl(it.first, it.second, it.third) } }
         ),
         getSetByValue2(
             AndroidPackage::getKeySetMapping,
@@ -298,22 +296,25 @@
             PackageImpl::addPermissionGroup,
             "test.permission.GROUP",
             transformGet = { it.singleOrNull()?.name },
-            transformSet = { ParsedPermissionGroup().apply { setName(it) } }
+            transformSet = { ParsedPermissionGroupImpl().apply { setName(it) } }
         ),
         getSetByValue2(
             AndroidPackage::getPreferredActivityFilters,
             PackageImpl::addPreferredActivityFilter,
-            "TestClassName" to ParsedIntentInfo().apply {
-                addDataScheme("http")
-                addDataAuthority("test.pm.server.android.com", null)
+            "TestClassName" to ParsedIntentInfoImpl().apply {
+                intentFilter.apply {
+                    addDataScheme("http")
+                    addDataAuthority("test.pm.server.android.com", null)
+                }
             },
             transformGet = { it.singleOrNull()?.let { it.first to it.second } },
             compare = { first, second ->
                 equalBy(
                     first, second,
                     { it.first },
-                    { it.second.schemesIterator().asSequence().singleOrNull() },
-                    { it.second.authoritiesIterator().asSequence().singleOrNull()?.host },
+                    { it.second.intentFilter.schemesIterator().asSequence().singleOrNull() },
+                    { it.second.intentFilter.authoritiesIterator().asSequence()
+                        .singleOrNull()?.host },
                 )
             }
         ),
@@ -356,35 +357,35 @@
             PackageImpl::addActivity,
             "TestActivityName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedActivity().apply { name = it }.withMimeGroups() }
+            transformSet = { ParsedActivityImpl().apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
             AndroidPackage::getReceivers,
             PackageImpl::addReceiver,
             "TestReceiverName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedActivity().apply { name = it }.withMimeGroups() }
+            transformSet = { ParsedActivityImpl().apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
             AndroidPackage::getServices,
             PackageImpl::addService,
             "TestServiceName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedService().apply { name = it }.withMimeGroups() }
+            transformSet = { ParsedServiceImpl().apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
             AndroidPackage::getProviders,
             PackageImpl::addProvider,
             "TestProviderName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedProvider().apply { name = it }.withMimeGroups() }
+            transformSet = { ParsedProviderImpl().apply { name = it }.withMimeGroups() }
         ),
         getSetByValue(
             AndroidPackage::getInstrumentations,
             PackageImpl::addInstrumentation,
             "TestInstrumentationName",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedInstrumentation().apply { name = it } }
+            transformSet = { ParsedInstrumentationImpl().apply { name = it } }
         ),
         getSetByValue(
             AndroidPackage::getConfigPreferences,
@@ -409,7 +410,7 @@
             PackageImpl::addPermission,
             "test.PERMISSION",
             transformGet = { it.singleOrNull()?.name.orEmpty() },
-            transformSet = { ParsedPermission().apply { name = it } }
+            transformSet = { ParsedPermissionImpl().apply { name = it } }
         ),
         getSetByValue(
             AndroidPackage::getUsesPermissions,
@@ -420,7 +421,7 @@
                 it.filterNot { it.name == "test.implicit.PERMISSION" }
                     .singleOrNull()?.name.orEmpty()
             },
-            transformSet = { ParsedUsesPermission(it, 0) }
+            transformSet = { ParsedUsesPermissionImpl(it, 0) }
         ),
         getSetByValue(
             AndroidPackage::getRequestedFeatures,
@@ -445,7 +446,7 @@
         getSetByValue(
             AndroidPackage::getProcesses,
             PackageImpl::setProcesses,
-            mapOf("testProcess" to ParsedProcess().apply { name = "testProcessName" }),
+            mapOf("testProcess" to ParsedProcessImpl().apply { name = "testProcessName" }),
             compare = { first, second ->
                 equalBy(
                     first, second,
@@ -562,10 +563,12 @@
         .generateKeyPair()
         .public
 
-    private fun <T : ParsedComponent> T.withMimeGroups() = apply {
+    private fun <T : ParsedComponentImpl> T.withMimeGroups() = apply {
         val componentName = name
-        addIntent(ParsedIntentInfo().apply {
-            addMimeGroup("$componentName/mimeGroup")
+        addIntent(ParsedIntentInfoImpl().apply {
+            intentFilter.apply {
+                addMimeGroup("$componentName/mimeGroup")
+            }
         })
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index ece600b..8170acf 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -18,13 +18,17 @@
 
 import android.content.pm.ActivityInfo
 import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedActivityImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedActivityTest : ParsedMainComponentTest(ParsedActivity::class) {
+class ParsedActivityTest : ParsedMainComponentTest(
+    ParsedActivity::class,
+    ParsedActivityImpl::class
+) {
 
-    override val defaultImpl = ParsedActivity()
-    override val creator = ParsedActivity.CREATOR
+    override val defaultImpl = ParsedActivityImpl()
+    override val creator = ParsedActivityImpl.CREATOR
 
     override val mainComponentSubclassBaseParams = listOf(
         ParsedActivity::getPermission,
@@ -54,7 +58,7 @@
     override fun mainComponentSubclassExtraParams() = listOf(
         getSetByValue(
             ParsedActivity::getWindowLayout,
-            ParsedActivity::setWindowLayout,
+            ParsedActivityImpl::setWindowLayout,
             ActivityInfo.WindowLayout(1, 1f, 2, 1f, 3, 4, 5),
             compare = { first, second ->
                 equalBy(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
index e739dc7..503301b 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
@@ -17,13 +17,15 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.parsing.component.ParsedAttribution
+import android.content.pm.parsing.component.ParsedAttributionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedAttributionTest : ParcelableComponentTest(ParsedAttribution::class) {
+class ParsedAttributionTest : ParcelableComponentTest(ParsedAttribution::class,
+    ParsedAttributionImpl::class) {
 
-    override val defaultImpl = ParsedAttribution("", 0, emptyList())
-    override val creator = ParsedAttribution.CREATOR
+    override val defaultImpl = ParsedAttributionImpl("", 0, emptyList())
+    override val creator = ParsedAttributionImpl.CREATOR
 
     override val baseParams = listOf(
         ParsedAttribution::getTag,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
index 0a22f6d..e978dd3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
@@ -18,7 +18,9 @@
 
 import android.content.pm.PackageManager
 import android.content.pm.parsing.component.ParsedComponent
+import android.content.pm.parsing.component.ParsedComponentImpl
 import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
 import android.os.Bundle
 import android.os.Parcelable
 import kotlin.contracts.ExperimentalContracts
@@ -26,8 +28,11 @@
 import kotlin.reflect.KFunction1
 
 @ExperimentalContracts
-abstract class ParsedComponentTest(kClass: KClass<out Parcelable>) :
-    ParcelableComponentTest(kClass) {
+abstract class ParsedComponentTest(getterType: KClass<*>, setterType: KClass<out Parcelable>) :
+    ParcelableComponentTest(getterType, setterType) {
+
+    constructor(getterAndSetterType: KClass<out Parcelable>) :
+            this(getterAndSetterType, getterAndSetterType)
 
     final override val excludedMethods
         get() = subclassExcludedMethods + listOf(
@@ -57,14 +62,14 @@
     final override fun extraParams() = subclassExtraParams() + listOf(
         getSetByValue(
             ParsedComponent::getIntents,
-            ParsedComponent::addIntent,
+            ParsedComponentImpl::addIntent,
             "TestLabel",
             transformGet = { it.singleOrNull()?.nonLocalizedLabel },
-            transformSet = { ParsedIntentInfo().setNonLocalizedLabel(it) },
+            transformSet = { ParsedIntentInfoImpl().setNonLocalizedLabel(it!!) },
         ),
         getSetByValue(
             ParsedComponent::getProperties,
-            ParsedComponent::addProperty,
+            ParsedComponentImpl::addProperty,
             PackageManager.Property(
                 "testPropertyName",
                 "testPropertyValue",
@@ -84,7 +89,7 @@
         ),
         getSetByValue(
             ParsedComponent::getMetaData,
-            ParsedComponent::setMetaData,
+            ParsedComponentImpl::setMetaData,
             "testBundleKey" to "testBundleValue",
             transformGet = { "testBundleKey" to it?.getString("testBundleKey") },
             transformSet = { Bundle().apply { putString(it.first, it.second) } }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
index b7a85cc..f15b911 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
@@ -17,13 +17,17 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.parsing.component.ParsedInstrumentation
+import android.content.pm.parsing.component.ParsedInstrumentationImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedInstrumentationTest : ParsedComponentTest(ParsedInstrumentation::class) {
+class ParsedInstrumentationTest : ParsedComponentTest(
+    ParsedInstrumentation::class,
+    ParsedInstrumentationImpl::class
+) {
 
-    override val defaultImpl = ParsedInstrumentation()
-    override val creator = ParsedInstrumentation.CREATOR
+    override val defaultImpl = ParsedInstrumentationImpl()
+    override val creator = ParsedInstrumentationImpl.CREATOR
 
     override val subclassBaseParams = listOf(
         ParsedInstrumentation::getTargetPackage,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
index e27bdf2..f04e851 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
@@ -17,102 +17,23 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.parsing.component.ParsedIntentInfo
-import android.os.Parcel
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
 import android.os.Parcelable
 import android.os.PatternMatcher
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedIntentInfoTest : ParcelableComponentTest(ParsedIntentInfo::class) {
+class ParsedIntentInfoTest : ParcelableComponentTest(
+    ParsedIntentInfo::class,
+    ParsedIntentInfoImpl::class,
+) {
 
-    override val defaultImpl = ParsedIntentInfo()
-
-    override val creator = object : Parcelable.Creator<ParsedIntentInfo> {
-        override fun createFromParcel(source: Parcel) = ParsedIntentInfo(source)
-        override fun newArray(size: Int) = Array<ParsedIntentInfo?>(size) { null }
-    }
+    override val defaultImpl = ParsedIntentInfoImpl()
+    override val creator = ParsedIntentInfoImpl.CREATOR
 
     override val excludedMethods = listOf(
-        // Used to parcel
-        "writeIntentInfoToParcel",
-        // All remaining IntentFilter methods, which are out of scope
-        "hasDataPath",
-        "hasDataSchemeSpecificPart",
-        "matchAction",
-        "matchData",
-        "actionsIterator",
-        "addAction",
-        "addCategory",
-        "addDataAuthority",
-        "addDataPath",
-        "addDataScheme",
-        "addDataSchemeSpecificPart",
-        "addDataType",
-        "addDynamicDataType",
-        "addMimeGroup",
-        "asPredicate",
-        "asPredicateWithTypeResolution",
-        "authoritiesIterator",
-        "categoriesIterator",
-        "clearDynamicDataTypes",
-        "countActions",
-        "countCategories",
-        "countDataAuthorities",
-        "countDataPaths",
-        "countDataSchemeSpecificParts",
-        "countDataSchemes",
-        "countDataTypes",
-        "countMimeGroups",
-        "countStaticDataTypes",
-        "dataTypes",
-        "debugCheck",
-        "dump",
-        "dumpDebug",
-        "getAction",
-        "getAutoVerify",
-        "getCategory",
-        "getDataAuthority",
-        "getDataPath",
-        "getDataScheme",
-        "getDataSchemeSpecificPart",
-        "getDataType",
-        "getHosts",
-        "getHostsList",
-        "getMimeGroup",
-        "getOrder",
-        "getPriority",
-        "getVisibilityToInstantApp",
-        "handleAllWebDataURI",
-        "handlesWebUris",
-        "hasAction",
-        "hasCategory",
-        "hasDataAuthority",
-        "hasDataScheme",
-        "hasDataType",
-        "hasExactDataType",
-        "hasExactDynamicDataType",
-        "hasExactStaticDataType",
-        "hasMimeGroup",
-        "isExplicitlyVisibleToInstantApp",
-        "isImplicitlyVisibleToInstantApp",
-        "isVerified",
-        "isVisibleToInstantApp",
-        "match",
-        "matchCategories",
-        "matchDataAuthority",
-        "mimeGroupsIterator",
-        "needsVerification",
-        "pathsIterator",
-        "readFromXml",
-        "schemeSpecificPartsIterator",
-        "schemesIterator",
-        "setAutoVerify",
-        "setOrder",
-        "setPriority",
-        "setVerified",
-        "setVisibilityToInstantApp",
-        "typesIterator",
-        "writeToXml",
+        // Tested through extraAssertions, since this isn't a settable field
+        "getIntentFilter"
     )
 
     override val baseParams = listOf(
@@ -122,31 +43,30 @@
         ParsedIntentInfo::getNonLocalizedLabel,
     )
 
-    override fun initialObject() = ParsedIntentInfo().apply {
-        addAction("test.ACTION")
-        addDataAuthority("testAuthority", "404")
-        addCategory("test.CATEGORY")
-        addMimeGroup("testMime")
-        addDataPath("testPath", PatternMatcher.PATTERN_LITERAL)
+    override fun initialObject() = ParsedIntentInfoImpl().apply {
+        intentFilter.apply {
+            addAction("test.ACTION")
+            addDataAuthority("testAuthority", "404")
+            addCategory("test.CATEGORY")
+            addMimeGroup("testMime")
+            addDataPath("testPath", PatternMatcher.PATTERN_LITERAL)
+        }
     }
 
     override fun extraAssertions(before: Parcelable, after: Parcelable) {
         super.extraAssertions(before, after)
-        after as ParsedIntentInfo
-        expect.that(after.actionsIterator().asSequence().singleOrNull())
+        val intentFilter = (after as ParsedIntentInfo).intentFilter
+        expect.that(intentFilter.actionsIterator().asSequence().singleOrNull())
             .isEqualTo("test.ACTION")
 
-        val authority = after.authoritiesIterator().asSequence().singleOrNull()
+        val authority = intentFilter.authoritiesIterator().asSequence().singleOrNull()
         expect.that(authority?.host).isEqualTo("testAuthority")
         expect.that(authority?.port).isEqualTo(404)
 
-        expect.that(after.categoriesIterator().asSequence().singleOrNull())
+        expect.that(intentFilter.categoriesIterator().asSequence().singleOrNull())
             .isEqualTo("test.CATEGORY")
-        expect.that(after.mimeGroupsIterator().asSequence().singleOrNull())
+        expect.that(intentFilter.mimeGroupsIterator().asSequence().singleOrNull())
             .isEqualTo("testMime")
-        expect.that(after.hasDataPath("testPath")).isTrue()
+        expect.that(intentFilter.hasDataPath("testPath")).isTrue()
     }
-
-    override fun writeToParcel(parcel: Parcel, value: Parcelable) =
-        ParsedIntentInfo.PARCELER.parcel(value as ParsedIntentInfo, parcel, 0)
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
index 411cb09..214734a 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.parsing.component.ParsedMainComponent
+import android.content.pm.parsing.component.ParsedMainComponentImpl
 import android.content.pm.parsing.component.ParsedService
 import android.os.Parcelable
 import java.util.Arrays
@@ -25,8 +26,11 @@
 import kotlin.reflect.KFunction1
 
 @ExperimentalContracts
-abstract class ParsedMainComponentTest(kClass: KClass<out Parcelable>) :
-    ParsedComponentTest(kClass) {
+abstract class ParsedMainComponentTest(getterType: KClass<*>, setterType: KClass<out Parcelable>) :
+    ParsedComponentTest(getterType, setterType) {
+
+    constructor(getterAndSetterType: KClass<out Parcelable>) :
+            this(getterAndSetterType, getterAndSetterType)
 
     final override val subclassBaseParams
         get() = mainComponentSubclassBaseParams + listOf(
@@ -42,8 +46,8 @@
 
     final override fun subclassExtraParams() = mainComponentSubclassExtraParams() + listOf(
         getSetByValue(
-            ParsedService::getAttributionTags,
-            ParsedService::setAttributionTags,
+            ParsedMainComponent::getAttributionTags,
+            ParsedMainComponentImpl::setAttributionTags,
             arrayOf("testAttributionTag"),
             compare = Arrays::equals
         ),
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
index 53c862a..f876ed0 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
@@ -17,13 +17,17 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.parsing.component.ParsedPermissionGroup
+import android.content.pm.parsing.component.ParsedPermissionGroupImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedPermissionGroupTest : ParsedComponentTest(ParsedPermissionGroup::class) {
+class ParsedPermissionGroupTest : ParsedComponentTest(
+    ParsedPermissionGroup::class,
+    ParsedPermissionGroupImpl::class,
+) {
 
-    override val defaultImpl = ParsedPermissionGroup()
-    override val creator = ParsedPermissionGroup.CREATOR
+    override val defaultImpl = ParsedPermissionGroupImpl()
+    override val creator = ParsedPermissionGroupImpl.CREATOR
 
     override val subclassBaseParams = listOf(
         ParsedPermissionGroup::getRequestDetailResourceId,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
index bb63e2e2..6f48e24 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
@@ -18,13 +18,18 @@
 
 import android.content.pm.parsing.component.ParsedPermission
 import android.content.pm.parsing.component.ParsedPermissionGroup
+import android.content.pm.parsing.component.ParsedPermissionGroupImpl
+import android.content.pm.parsing.component.ParsedPermissionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedPermissionTest : ParsedComponentTest(ParsedPermission::class) {
+class ParsedPermissionTest : ParsedComponentTest(
+    ParsedPermission::class,
+    ParsedPermissionImpl::class
+) {
 
-    override val defaultImpl = ParsedPermission()
-    override val creator = ParsedPermission.CREATOR
+    override val defaultImpl = ParsedPermissionImpl()
+    override val creator = ParsedPermissionImpl.CREATOR
 
     override val subclassExcludedMethods = listOf(
         // Utility methods
@@ -47,8 +52,8 @@
         getter(ParsedPermission::getKnownCerts, setOf("testCert")),
         getSetByValue(
             ParsedPermission::getParsedPermissionGroup,
-            ParsedPermission::setParsedPermissionGroup,
-            ParsedPermissionGroup().apply { name = "test.permission.group" },
+            ParsedPermissionImpl::setParsedPermissionGroup,
+            ParsedPermissionGroupImpl().apply { name = "test.permission.group" },
             compare = { first, second -> equalBy(first, second, ParsedPermissionGroup::getName) }
         ),
     )
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
index 34f46f2..e633850 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -17,13 +17,14 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.parsing.component.ParsedProcess
+import android.content.pm.parsing.component.ParsedProcessImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class) {
+class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class, ParsedProcessImpl::class) {
 
-    override val defaultImpl = ParsedProcess()
-    override val creator = ParsedProcess.CREATOR
+    override val defaultImpl = ParsedProcessImpl()
+    override val creator = ParsedProcessImpl.CREATOR
 
     override val excludedMethods = listOf(
         // Copying method
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
index e6d5c0f..78e4b79 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
@@ -18,14 +18,15 @@
 
 import android.content.pm.PathPermission
 import android.content.pm.parsing.component.ParsedProvider
+import android.content.pm.parsing.component.ParsedProviderImpl
 import android.os.PatternMatcher
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class) {
+class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class, ParsedProviderImpl::class) {
 
-    override val defaultImpl = ParsedProvider()
-    override val creator = ParsedProvider.CREATOR
+    override val defaultImpl = ParsedProviderImpl()
+    override val creator = ParsedProviderImpl.CREATOR
 
     override val mainComponentSubclassBaseParams = listOf(
         ParsedProvider::getAuthority,
@@ -41,7 +42,7 @@
     override fun mainComponentSubclassExtraParams() = listOf(
         getSetByValue(
             ParsedProvider::getUriPermissionPatterns,
-            ParsedProvider::setUriPermissionPatterns,
+            ParsedProviderImpl::setUriPermissionPatterns,
             PatternMatcher("testPattern", PatternMatcher.PATTERN_LITERAL),
             transformGet = { it?.singleOrNull() },
             transformSet = { arrayOf(it) },
@@ -55,7 +56,7 @@
         ),
         getSetByValue(
             ParsedProvider::getPathPermissions,
-            ParsedProvider::setPathPermissions,
+            ParsedProviderImpl::setPathPermissions,
             PathPermission(
                 "testPermissionPattern",
                 PatternMatcher.PATTERN_LITERAL,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
index 5530184..9363aa3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
@@ -17,13 +17,14 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.parsing.component.ParsedService
+import android.content.pm.parsing.component.ParsedServiceImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedServiceTest : ParsedMainComponentTest(ParsedService::class) {
+class ParsedServiceTest : ParsedMainComponentTest(ParsedService::class, ParsedServiceImpl::class) {
 
-    override val defaultImpl = ParsedService()
-    override val creator = ParsedService.CREATOR
+    override val defaultImpl = ParsedServiceImpl()
+    override val creator = ParsedServiceImpl.CREATOR
 
     override val mainComponentSubclassBaseParams = listOf(
         ParsedService::getForegroundServiceType,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
index 1131c72..81e800f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
@@ -17,19 +17,22 @@
 package com.android.server.pm.test.parsing.parcelling
 
 import android.content.pm.parsing.component.ParsedUsesPermission
-import android.os.Parcelable
+import android.content.pm.parsing.component.ParsedUsesPermissionImpl
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
-class ParsedUsesPermissionTest : ParcelableComponentTest(ParsedUsesPermission::class) {
+class ParsedUsesPermissionTest : ParcelableComponentTest(
+    ParsedUsesPermission::class,
+    ParsedUsesPermissionImpl::class
+) {
 
-    override val defaultImpl = ParsedUsesPermission("", 0)
-    override val creator = ParsedUsesPermission.CREATOR
+    override val defaultImpl = ParsedUsesPermissionImpl("", 0)
+    override val creator = ParsedUsesPermissionImpl.CREATOR
 
     override val baseParams = listOf(
         ParsedUsesPermission::getName,
         ParsedUsesPermission::getUsesPermissionFlags
     )
 
-    override fun initialObject() = ParsedUsesPermission("", 0)
+    override fun initialObject() = ParsedUsesPermissionImpl("", 0)
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index 4de8d52..33234d5 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -18,8 +18,8 @@
 
 import android.content.Intent
 import android.content.pm.ApplicationInfo
-import android.content.pm.parsing.component.ParsedActivity
-import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.parsing.component.ParsedActivityImpl
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
 import android.os.Build
 import android.os.PatternMatcher
 import android.util.ArraySet
@@ -92,40 +92,44 @@
             whenever(targetSdkVersion) { Build.VERSION_CODES.R }
 
             val activityList = listOf(
-                    ParsedActivity().apply {
-                        addIntent(
-                                ParsedIntentInfo().apply {
-                                    addAction(Intent.ACTION_VIEW)
-                                    addCategory(Intent.CATEGORY_BROWSABLE)
-                                    addCategory(Intent.CATEGORY_DEFAULT)
-                                    addDataScheme("http")
-                                    addDataScheme("https")
-                                    addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
-                                    addDataAuthority("example1.com", null)
-                                    addDataAuthority("invalid1", null)
-                                }
-                        )
-                    },
-                    ParsedActivity().apply {
-                        addIntent(
-                                ParsedIntentInfo().apply {
-                                    setAutoVerify(true)
-                                    addAction(Intent.ACTION_VIEW)
-                                    addCategory(Intent.CATEGORY_BROWSABLE)
-                                    addCategory(Intent.CATEGORY_DEFAULT)
-                                    addDataScheme("http")
-                                    addDataScheme("https")
+                ParsedActivityImpl().apply {
+                    addIntent(
+                        ParsedIntentInfoImpl().apply {
+                            intentFilter.apply {
+                                addAction(Intent.ACTION_VIEW)
+                                addCategory(Intent.CATEGORY_BROWSABLE)
+                                addCategory(Intent.CATEGORY_DEFAULT)
+                                addDataScheme("http")
+                                addDataScheme("https")
+                                addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                addDataAuthority("example1.com", null)
+                                addDataAuthority("invalid1", null)
+                            }
+                        }
+                    )
+                },
+                ParsedActivityImpl().apply {
+                    addIntent(
+                        ParsedIntentInfoImpl().apply {
+                            intentFilter.apply {
+                                setAutoVerify(true)
+                                addAction(Intent.ACTION_VIEW)
+                                addCategory(Intent.CATEGORY_BROWSABLE)
+                                addCategory(Intent.CATEGORY_DEFAULT)
+                                addDataScheme("http")
+                                addDataScheme("https")
 
-                                    // The presence of a non-web-scheme as the only autoVerify
-                                    // intent-filter, when non-forced, means that v1 will not pick
-                                    // up the package for verification.
-                                    addDataScheme("nonWebScheme")
-                                    addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
-                                    addDataAuthority("example2.com", null)
-                                    addDataAuthority("invalid2", null)
-                                }
-                        )
-                    },
+                                // The presence of a non-web-scheme as the only autoVerify
+                                // intent-filter, when non-forced, means that v1 will not pick
+                                // up the package for verification.
+                                addDataScheme("nonWebScheme")
+                                addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                addDataAuthority("example2.com", null)
+                                addDataAuthority("invalid2", null)
+                            }
+                        }
+                    )
+                },
             )
 
             whenever(activities) { activityList }
@@ -264,9 +268,10 @@
 
             // The intents are split into separate Activities to test that multiple are collected
             val activityList = listOf(
-                    ParsedActivity().apply {
+                    ParsedActivityImpl().apply {
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addAction(Intent.ACTION_VIEW)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
@@ -277,9 +282,11 @@
                                     addDataAuthority("example1.com", null)
                                     addDataAuthority("invalid1", null)
                                 }
+                            }
                         )
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     addAction(Intent.ACTION_VIEW)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
                                     addCategory(Intent.CATEGORY_DEFAULT)
@@ -288,11 +295,13 @@
                                     addDataAuthority("example2.com", null)
                                     addDataAuthority("invalid2", null)
                                 }
+                            }
                         )
                     },
-                    ParsedActivity().apply {
+                    ParsedActivityImpl().apply {
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addAction(Intent.ACTION_VIEW)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
@@ -302,11 +311,13 @@
                                     addDataAuthority("example3.com", null)
                                     addDataAuthority("invalid3", null)
                                 }
+                            }
                         )
                     },
-                    ParsedActivity().apply {
+                    ParsedActivityImpl().apply {
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addAction(Intent.ACTION_VIEW)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
@@ -315,9 +326,11 @@
                                     addDataAuthority("example4.com", null)
                                     addDataAuthority("invalid4", null)
                                 }
+                            }
                         )
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addAction(Intent.ACTION_VIEW)
                                     addCategory(Intent.CATEGORY_DEFAULT)
@@ -326,9 +339,11 @@
                                     addDataAuthority("example5.com", null)
                                     addDataAuthority("invalid5", null)
                                 }
+                            }
                         )
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
                                     addCategory(Intent.CATEGORY_DEFAULT)
@@ -337,30 +352,37 @@
                                     addDataAuthority("example6.com", null)
                                     addDataAuthority("invalid6", null)
                                 }
+                            }
                         )
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
                                     addCategory(Intent.CATEGORY_DEFAULT)
                                     addDataAuthority("example7.com", null)
                                 }
+                            }
                         )
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
                                     addCategory(Intent.CATEGORY_DEFAULT)
                                     addDataScheme("https")
                                 }
+                            }
                         )
                         addIntent(
-                                ParsedIntentInfo().apply {
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
                                     setAutoVerify(autoVerify)
                                     addCategory(Intent.CATEGORY_BROWSABLE)
                                     addCategory(Intent.CATEGORY_DEFAULT)
                                     addDataPath("/sub7", PatternMatcher.PATTERN_LITERAL)
                                 }
+                            }
                         )
                     },
             )
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 1ad1879..26a7efb 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -19,9 +19,8 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedActivity
-import android.content.pm.parsing.component.ParsedIntentInfo
-import android.content.pm.pkg.PackageUserState
+import android.content.pm.parsing.component.ParsedActivityImpl
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
 import android.content.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.verify.domain.DomainVerificationState
@@ -297,15 +296,17 @@
             whenever(isEnabled) { true }
             whenever(activities) {
                 listOf(
-                    ParsedActivity().apply {
+                    ParsedActivityImpl().apply {
                         addIntent(
-                            ParsedIntentInfo().apply {
-                                autoVerify = true
-                                addAction(Intent.ACTION_VIEW)
-                                addCategory(Intent.CATEGORY_BROWSABLE)
-                                addCategory(Intent.CATEGORY_DEFAULT)
-                                addDataScheme("https")
-                                addDataAuthority("example.com", null)
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
+                                    autoVerify = true
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("https")
+                                    addDataAuthority("example.com", null)
+                                }
                             }
                         )
                     }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index b65995f..34b248c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -19,8 +19,8 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedActivity
-import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.parsing.component.ParsedActivityImpl
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
 import android.content.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainOwner
 import android.content.pm.verify.domain.DomainVerificationInfo
@@ -520,18 +520,20 @@
             whenever(isEnabled) { true }
 
             val activityList = listOf(
-                ParsedActivity().apply {
+                ParsedActivityImpl().apply {
                     domains.forEach {
                         addIntent(
-                            ParsedIntentInfo().apply {
-                                autoVerify = true
-                                addAction(Intent.ACTION_VIEW)
-                                addCategory(Intent.CATEGORY_BROWSABLE)
-                                addCategory(Intent.CATEGORY_DEFAULT)
-                                addDataScheme("http")
-                                addDataScheme("https")
-                                addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
-                                addDataAuthority(it, null)
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
+                                    autoVerify = true
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("http")
+                                    addDataScheme("https")
+                                    addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority(it, null)
+                                }
                             }
                         )
                     }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index dbec56f..a22563e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -20,8 +20,8 @@
 import android.content.pm.PackageManager
 import android.content.pm.Signature
 import android.content.pm.SigningDetails
-import android.content.pm.parsing.component.ParsedActivity
-import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.parsing.component.ParsedActivityImpl
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
 import android.content.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainOwner
 import android.content.pm.verify.domain.DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED
@@ -813,22 +813,24 @@
             whenever(isEnabled) { true }
 
             val activityList = listOf(
-                    ParsedActivity().apply {
-                        domains.forEach {
-                            addIntent(
-                                    ParsedIntentInfo().apply {
-                                        autoVerify = true
-                                        addAction(Intent.ACTION_VIEW)
-                                        addCategory(Intent.CATEGORY_BROWSABLE)
-                                        addCategory(Intent.CATEGORY_DEFAULT)
-                                        addDataScheme("http")
-                                        addDataScheme("https")
-                                        addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
-                                        addDataAuthority(it, null)
-                                    }
-                            )
-                        }
-                    },
+                ParsedActivityImpl().apply {
+                    domains.forEach {
+                        addIntent(
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
+                                    autoVerify = true
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("http")
+                                    addDataScheme("https")
+                                    addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                    addDataAuthority(it, null)
+                                }
+                            }
+                        )
+                    }
+                },
             )
 
             whenever(activities) { activityList }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 8125128..cbb8e3a 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -19,9 +19,8 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedActivity
-import android.content.pm.parsing.component.ParsedIntentInfo
-import android.content.pm.pkg.PackageUserState
+import android.content.pm.parsing.component.ParsedActivityImpl
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
 import android.content.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainVerificationState
 import android.os.Build
@@ -196,15 +195,17 @@
             whenever(isEnabled) { true }
             whenever(activities) {
                 listOf(
-                    ParsedActivity().apply {
+                    ParsedActivityImpl().apply {
                         addIntent(
-                            ParsedIntentInfo().apply {
-                                autoVerify = true
-                                addAction(Intent.ACTION_VIEW)
-                                addCategory(Intent.CATEGORY_BROWSABLE)
-                                addCategory(Intent.CATEGORY_DEFAULT)
-                                addDataScheme("https")
-                                addDataAuthority("example.com", null)
+                            ParsedIntentInfoImpl().apply {
+                                intentFilter.apply {
+                                    autoVerify = true
+                                    addAction(Intent.ACTION_VIEW)
+                                    addCategory(Intent.CATEGORY_BROWSABLE)
+                                    addCategory(Intent.CATEGORY_DEFAULT)
+                                    addDataScheme("https")
+                                    addDataAuthority("example.com", null)
+                                }
                             }
                         )
                     }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 1711355..473d70c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -19,7 +19,9 @@
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedActivityImpl
 import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.parsing.component.ParsedIntentInfoImpl
 import android.content.pm.pkg.PackageUserState
 import android.content.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainVerificationManager
@@ -109,28 +111,32 @@
             whenever(isEnabled) { true }
 
             val activityList = listOf(
-                ParsedActivity().apply {
+                ParsedActivityImpl().apply {
                     addIntent(
-                        ParsedIntentInfo().apply {
-                            autoVerify = true
-                            addAction(Intent.ACTION_VIEW)
-                            addCategory(Intent.CATEGORY_BROWSABLE)
-                            addCategory(Intent.CATEGORY_DEFAULT)
-                            addDataScheme("http")
-                            addDataScheme("https")
-                            addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
-                            addDataAuthority(DOMAIN_ONE, null)
+                        ParsedIntentInfoImpl().apply {
+                            intentFilter.apply {
+                                autoVerify = true
+                                addAction(Intent.ACTION_VIEW)
+                                addCategory(Intent.CATEGORY_BROWSABLE)
+                                addCategory(Intent.CATEGORY_DEFAULT)
+                                addDataScheme("http")
+                                addDataScheme("https")
+                                addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                                addDataAuthority(DOMAIN_ONE, null)
+                            }
                         }
                     )
                     addIntent(
-                        ParsedIntentInfo().apply {
-                            autoVerify = true
-                            addAction(Intent.ACTION_VIEW)
-                            addCategory(Intent.CATEGORY_BROWSABLE)
-                            addCategory(Intent.CATEGORY_DEFAULT)
-                            addDataScheme("http")
-                            addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL)
-                            addDataAuthority("example2.com", null)
+                        ParsedIntentInfoImpl().apply {
+                            intentFilter.apply {
+                                autoVerify = true
+                                addAction(Intent.ACTION_VIEW)
+                                addCategory(Intent.CATEGORY_BROWSABLE)
+                                addCategory(Intent.CATEGORY_DEFAULT)
+                                addDataScheme("http")
+                                addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL)
+                                addDataAuthority("example2.com", null)
+                            }
                         }
                     )
                 },
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index 5e0cbca..06e691f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -191,7 +191,8 @@
         mBinder.setCommunalViewShowing(true);
         when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
         mAInfo.flags = 0;
-        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNotNull();
+        // TODO(b/191994709): Fix this assertion once we properly intercept activities.
+        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
     }
 
     @Test
@@ -203,7 +204,8 @@
         mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
 
         allowPackages("package1,package2");
-        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNotNull();
+        // TODO(b/191994709): Fix this assertion once we properly intercept activities.
+        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
     }
 
     @Test
@@ -215,7 +217,8 @@
         mAInfo.flags = 0;
 
         allowPackages(TEST_PACKAGE_NAME);
-        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNotNull();
+        // TODO(b/191994709): Fix this assertion once we properly intercept activities.
+        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index 72bc77e..3ee2348 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -105,7 +105,7 @@
         rule.system().validateFinalState()
         whenever(appHibernationManager.isHibernatingGlobally(TEST_PACKAGE_2_NAME)).thenReturn(true)
 
-        val optimizablePkgs = pm.optimizablePackages
+        val optimizablePkgs = DexOptHelper(pm).optimizablePackages
 
         assertTrue(optimizablePkgs.contains(TEST_PACKAGE_NAME))
         assertFalse(optimizablePkgs.contains(TEST_PACKAGE_2_NAME))
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index f1402ea..6a7d298 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -3,4 +3,5 @@
 per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
+per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
 per-file GestureLauncherServiceTest.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 2a5bb18..b1da890 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -16,7 +16,10 @@
 
 package com.android.server.accessibility;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -38,7 +41,6 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Icon;
 import android.os.IBinder;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -51,6 +53,7 @@
 import com.android.server.accessibility.magnification.MagnificationController;
 import com.android.server.accessibility.magnification.WindowMagnificationManager;
 import com.android.server.accessibility.test.MessageCapturingHandler;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -92,6 +95,7 @@
     @Mock private AccessibilityWindowManager mMockA11yWindowManager;
     @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
     @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
+    @Mock private UserManagerInternal mMockUserManagerInternal;
     @Mock private IBinder mMockBinder;
     @Mock private IAccessibilityServiceClient mMockServiceClient;
     @Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
@@ -109,16 +113,20 @@
         MockitoAnnotations.initMocks(this);
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.addService(
                 WindowManagerInternal.class, mMockWindowManagerService);
         LocalServices.addService(
                 ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
+        LocalServices.addService(
+                UserManagerInternal.class, mMockUserManagerInternal);
 
         when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
                 mMockWindowMagnificationMgr);
         when(mMockWindowManagerService.getAccessibilityController()).thenReturn(
                 mMockA11yController);
         when(mMockA11yController.isAccessibilityTracingEnabled()).thenReturn(false);
+        when(mMockUserManagerInternal.isUserUnlockingOrUnlocked(anyInt())).thenReturn(true);
         mA11yms = new AccessibilityManagerService(
             InstrumentationRegistry.getContext(),
             mMockPackageManager,
@@ -131,16 +139,15 @@
         mMockResources = mock(Resources.class);
         when(mMockContext.getResources()).thenReturn(mMockResources);
 
-        final AccessibilityUserState userState = new AccessibilityUserState(
+        mUserState = new AccessibilityUserState(
                 mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms);
-        mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), userState);
+        mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), mUserState);
     }
 
     private void setupAccessibilityServiceConnection() {
         when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn(
                 InstrumentationRegistry.getContext().getSystemService(
                         Context.DISPLAY_SERVICE));
-        mUserState = new AccessibilityUserState(UserHandle.USER_SYSTEM, mMockContext, mA11yms);
 
         when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
         mMockResolveInfo.serviceInfo = mock(ServiceInfo.class);
@@ -226,4 +233,27 @@
         assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
                 userState.getMagnificationModeLocked());
     }
+
+    @SmallTest
+    public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() {
+        mUserState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME);
+        mUserState.setMagnificationCapabilitiesLocked(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+
+        // Invokes client change to trigger onUserStateChanged.
+        mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+        verify(mMockWindowMagnificationMgr).requestConnection(true);
+    }
+
+    @SmallTest
+    public void testOnClientChange_boundServiceCanControlMagnification_requestConnection() {
+        setupAccessibilityServiceConnection();
+        when(mMockSecurityPolicy.canControlMagnification(any())).thenReturn(true);
+
+        // Invokes client change to trigger onUserStateChanged.
+        mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+        verify(mMockWindowMagnificationMgr).requestConnection(true);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/camera/CameraServiceProxyTest.java b/services/tests/servicestests/src/com/android/server/camera/CameraServiceProxyTest.java
new file mode 100644
index 0000000..ea746d1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/camera/CameraServiceProxyTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.server.camera;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.InstrumentationRegistry;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.view.Display;
+import android.view.Surface;
+
+import java.util.HashMap;
+
+@RunWith(JUnit4.class)
+public class CameraServiceProxyTest {
+
+    @Test
+    public void testGetCropRotateScale() {
+
+        Context ctx = InstrumentationRegistry.getContext();
+
+        // Check resizeability and SDK
+        CameraServiceProxy.TaskInfo taskInfo = new CameraServiceProxy.TaskInfo();
+        taskInfo.isResizeable = true;
+        taskInfo.displayId = Display.DEFAULT_DISPLAY;
+        taskInfo.isFixedOrientationLandscape = false;
+        taskInfo.isFixedOrientationPortrait = true;
+        // Resizeable apps should be ignored
+        assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
+                Surface.ROTATION_90 , CameraCharacteristics.LENS_FACING_BACK,
+                /*ignoreResizableAndSdkCheck*/false)).isEqualTo(
+                CameraMetadata.SCALER_ROTATE_AND_CROP_NONE);
+        // Resizeable apps will be considered in case the ignore flag is set
+        assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
+                Surface.ROTATION_90, CameraCharacteristics.LENS_FACING_BACK,
+                /*ignoreResizableAndSdkCheck*/true)).isEqualTo(
+                CameraMetadata.SCALER_ROTATE_AND_CROP_90);
+        taskInfo.isResizeable = false;
+        // Non-resizeable apps should be considered
+        assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
+                Surface.ROTATION_90, CameraCharacteristics.LENS_FACING_BACK,
+                /*ignoreResizableAndSdkCheck*/false)).isEqualTo(
+                CameraMetadata.SCALER_ROTATE_AND_CROP_90);
+        // The ignore flag for non-resizeable should have no effect
+        assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
+                Surface.ROTATION_90, CameraCharacteristics.LENS_FACING_BACK,
+                /*ignoreResizableAndSdkCheck*/true)).isEqualTo(
+                CameraMetadata.SCALER_ROTATE_AND_CROP_90);
+        // Non-fixed orientation should be ignored
+        taskInfo.isFixedOrientationLandscape = false;
+        taskInfo.isFixedOrientationPortrait = false;
+        assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
+                Surface.ROTATION_90, CameraCharacteristics.LENS_FACING_BACK,
+                /*ignoreResizableAndSdkCheck*/true)).isEqualTo(
+                CameraMetadata.SCALER_ROTATE_AND_CROP_NONE);
+        // Check rotation and lens facing combinations
+        HashMap<Integer, Integer> backFacingMap = new HashMap<Integer, Integer>() {{
+            put(Surface.ROTATION_0, CameraMetadata.SCALER_ROTATE_AND_CROP_NONE);
+            put(Surface.ROTATION_90, CameraMetadata.SCALER_ROTATE_AND_CROP_90);
+            put(Surface.ROTATION_270, CameraMetadata.SCALER_ROTATE_AND_CROP_270);
+            put(Surface.ROTATION_180, CameraMetadata.SCALER_ROTATE_AND_CROP_180);
+        }};
+        taskInfo.isFixedOrientationPortrait = true;
+        backFacingMap.forEach((key, value) -> {
+            assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
+                    key, CameraCharacteristics.LENS_FACING_BACK,
+                    /*ignoreResizableAndSdkCheck*/true)).isEqualTo(value);
+        });
+        HashMap<Integer, Integer> frontFacingMap = new HashMap<Integer, Integer>() {{
+            put(Surface.ROTATION_0, CameraMetadata.SCALER_ROTATE_AND_CROP_NONE);
+            put(Surface.ROTATION_90, CameraMetadata.SCALER_ROTATE_AND_CROP_270);
+            put(Surface.ROTATION_270, CameraMetadata.SCALER_ROTATE_AND_CROP_90);
+            put(Surface.ROTATION_180, CameraMetadata.SCALER_ROTATE_AND_CROP_180);
+        }};
+        frontFacingMap.forEach((key, value) -> {
+            assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
+                    key, CameraCharacteristics.LENS_FACING_FRONT,
+                    /*ignoreResizableAndSdkCheck*/true)).isEqualTo(value);
+        });
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/camera/OWNERS b/services/tests/servicestests/src/com/android/server/camera/OWNERS
new file mode 100644
index 0000000..f48a95c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/camera/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/av:/camera/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/am/CriticalEventLogTest.java b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/am/CriticalEventLogTest.java
rename to services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
index 903d7f2..dca666c 100644
--- a/services/tests/servicestests/src/com/android/server/am/CriticalEventLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
@@ -14,19 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.server.am;
+package com.android.server.criticalevents;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import com.android.framework.protobuf.nano.MessageNano;
-import com.android.server.am.CriticalEventLog.ILogLoader;
-import com.android.server.am.CriticalEventLog.LogLoader;
-import com.android.server.am.nano.CriticalEventLogProto;
-import com.android.server.am.nano.CriticalEventLogStorageProto;
-import com.android.server.am.nano.CriticalEventProto;
-import com.android.server.am.nano.CriticalEventProto.HalfWatchdog;
-import com.android.server.am.nano.CriticalEventProto.Watchdog;
+import com.android.server.criticalevents.CriticalEventLog.ILogLoader;
+import com.android.server.criticalevents.CriticalEventLog.LogLoader;
+import com.android.server.criticalevents.nano.CriticalEventLogProto;
+import com.android.server.criticalevents.nano.CriticalEventLogStorageProto;
+import com.android.server.criticalevents.nano.CriticalEventProto;
+import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index abc1468e..d441143 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -242,6 +242,101 @@
         doReturn(true).when(resources).getBoolean(
                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
 
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadLpcm_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadLpcmEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadLpcmEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadLpcmDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadLpcmDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDd_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDdEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDdEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDdDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadDdDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg1_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg1Enabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg1Enabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg1Disabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg1Disabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMp3_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMp3Enabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMp3Enabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMp3Disabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadMp3Disabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg2_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg2Enabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg2Enabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg2Disabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadMpeg2Disabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadAac_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadAacEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadAacEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadAacDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadAacDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDts_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDtsEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDtsEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDtsDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadDtsDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadAtrac_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadAtracEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadAtracEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadAtracDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadAtracDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecQuerySadOnebitaudio_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecQuerySadOnebitaudioEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecQuerySadOnebitaudioEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecQuerySadOnebitaudioDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecQuerySadOnebitaudioDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDdp_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDdpEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDdpEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDdpDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadDdpDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDtshd_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDtshdEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDtshdEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDtshdDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadDtshdDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadTruehd_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadTruehdEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadTruehdEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadTruehdDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadTruehdDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDst_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDstEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDstEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadDstDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadDstDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadWmapro_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadWmaproEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadWmaproEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadWmaproDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadWmaproDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMax_userConfigurable);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMaxEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMaxEnabled_default);
+        doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMaxDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadMaxDisabled_default);
+
         return resources;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index f307506..85d30a6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -89,7 +89,22 @@
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
                     HdmiControlManager
-                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX);
     }
 
     @Test
@@ -112,7 +127,22 @@
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
                     HdmiControlManager
-                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX);
     }
 
     @Test
@@ -135,7 +165,22 @@
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
                     HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
                     HdmiControlManager
-                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index 823ed2a..2d81fc9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -22,6 +22,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
 import android.os.Looper;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
@@ -68,6 +69,7 @@
     private Looper mMyLooper;
     private TestLooper mTestLooper = new TestLooper();
     private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+    private int mTvLogicalAddress;
     private List<byte[]> mSupportedSads;
     private RequestSadCallback mCallback =
             new RequestSadCallback() {
@@ -94,7 +96,7 @@
         mMyLooper = mTestLooper.getLooper();
 
         mHdmiControlService =
-                new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+                new HdmiControlService(context,
                         Collections.emptyList()) {
                     @Override
                     boolean isControlEnabled() {
@@ -129,6 +131,9 @@
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mNativeWrapper.setPhysicalAddress(0x0000);
         mTestLooper.dispatchAll();
+        synchronized (mHdmiCecLocalDeviceTv.mLock) {
+            mTvLogicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress();
+        }
         mNativeWrapper.clearResultMessages();
     }
 
@@ -140,8 +145,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
         mNativeWrapper.clearResultMessages();
@@ -153,8 +157,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
         mNativeWrapper.clearResultMessages();
@@ -166,8 +169,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
         mNativeWrapper.clearResultMessages();
@@ -179,8 +181,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
         mNativeWrapper.clearResultMessages();
@@ -200,14 +201,12 @@
         action.start();
         mTestLooper.dispatchAll();
         HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress,
                 Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
                 Constants.ABORT_INVALID_OPERAND);
 
         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
         mNativeWrapper.clearResultMessages();
@@ -215,8 +214,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
         mNativeWrapper.clearResultMessages();
@@ -224,8 +222,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
         mNativeWrapper.clearResultMessages();
@@ -233,8 +230,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
         action.processCommand(featureAbort);
@@ -251,8 +247,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_1 = new byte[]{
                 0x01, 0x18, 0x4A,
@@ -260,16 +255,14 @@
                 0x03, 0x4B, 0x00,
                 0x04, 0x20, 0x0A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_1);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response1);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_2 = new byte[]{
                 0x05, 0x18, 0x4A,
@@ -277,16 +270,14 @@
                 0x07, 0x4B, 0x00,
                 0x08, 0x20, 0x0A};
         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_2);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response2);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_3 = new byte[]{
                 0x09, 0x18, 0x4A,
@@ -294,24 +285,21 @@
                 0x0B, 0x4B, 0x00,
                 0x0C, 0x20, 0x0A};
         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_3);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response3);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_4 = new byte[]{
                 0x0D, 0x18, 0x4A,
                 0x0E, 0x64, 0x5A,
                 0x0F, 0x4B, 0x00};
         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_4);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
         action.processCommand(response4);
         mTestLooper.dispatchAll();
@@ -335,38 +323,33 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_1 = new byte[]{
                 0x01, 0x18, 0x4A,
                 0x03, 0x4B, 0x00,
                 0x04, 0x20, 0x0A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_1);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response1);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_2 = new byte[]{
                 0x08, 0x20, 0x0A};
         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_2);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response2);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_3 = new byte[]{
                 0x09, 0x18, 0x4A,
@@ -374,22 +357,19 @@
                 0x0B, 0x4B, 0x00,
                 0x0C, 0x20, 0x0A};
         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_3);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response3);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_4 = new byte[]{
                 0x0F, 0x4B, 0x00};
         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_4);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
         action.processCommand(response4);
         mTestLooper.dispatchAll();
@@ -413,8 +393,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_1 = new byte[]{
                 0x20, 0x18, 0x4A,
@@ -422,16 +401,14 @@
                 0x22, 0x4B, 0x00,
                 0x23, 0x20, 0x0A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_1);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response1);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_2 = new byte[]{
                 0x24, 0x18, 0x4A,
@@ -439,16 +416,14 @@
                 0x26, 0x4B, 0x00,
                 0x27, 0x20, 0x0A};
         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_2);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response2);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_3 = new byte[]{
                 0x28, 0x18, 0x4A,
@@ -456,24 +431,21 @@
                 0x2A, 0x4B, 0x00,
                 0x2B, 0x20, 0x0A};
         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_3);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response3);
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_4 = new byte[]{
                 0x2C, 0x18, 0x4A,
                 0x2D, 0x64, 0x5A,
                 0x2E, 0x4B, 0x00};
         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_4);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
         action.processCommand(response4);
         mTestLooper.dispatchAll();
@@ -489,8 +461,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_1 = new byte[]{
                 0x01, 0x18,
@@ -498,8 +469,7 @@
                 0x03, 0x4B, 0x00,
                 0x04, 0x20, 0x0A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_1);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response1);
@@ -512,8 +482,7 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_2 = new byte[]{
                 0x05, 0x18, 0x4A,
@@ -521,8 +490,7 @@
                 0x07,
                 0x08, 0x20, 0x0A};
         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_2);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response2);
@@ -535,13 +503,11 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_3 = new byte[0];
         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_3);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response3);
@@ -554,16 +520,14 @@
         mTestLooper.dispatchAll();
 
         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
-                Constants.ADDR_AUDIO_SYSTEM,
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_4 = new byte[]{
                 0x0D, 0x18, 0x4A,
                 0x0E, 0x64, 0x5A,
                 0x0F, 0x4B};
         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
-                Constants.ADDR_AUDIO_SYSTEM,
-                mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_4);
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
         mNativeWrapper.clearResultMessages();
         action.processCommand(response4);
@@ -576,4 +540,77 @@
 
         assertThat(mSupportedSads.size()).isEqualTo(0);
     }
+
+    @Test
+    public void selectedSads_allSupported_completeResult() {
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
+                HdmiControlManager.QUERY_SAD_DISABLED);
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
+                HdmiControlManager.QUERY_SAD_DISABLED);
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
+                HdmiControlManager.QUERY_SAD_DISABLED);
+        RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
+                mCallback);
+        action.start();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
+                new int[]{Constants.AUDIO_CODEC_LPCM, Constants.AUDIO_CODEC_DD,
+                        Constants.AUDIO_CODEC_MP3, Constants.AUDIO_CODEC_MPEG2});
+        byte[] sadsToRespond_1 = new byte[]{
+                0x01, 0x18, 0x4A,
+                0x02, 0x64, 0x5A,
+                0x04, 0x20, 0x0A,
+                0x05, 0x18, 0x4A};
+        HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+        mNativeWrapper.clearResultMessages();
+        action.processCommand(response1);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
+                new int[]{Constants.AUDIO_CODEC_AAC, Constants.AUDIO_CODEC_DTS,
+                        Constants.AUDIO_CODEC_ATRAC, Constants.AUDIO_CODEC_DDP});
+        byte[] sadsToRespond_2 = new byte[]{
+                0x06, 0x64, 0x5A,
+                0x07, 0x4B, 0x00,
+                0x08, 0x20, 0x0A,
+                0x09, 0x18, 0x4A};
+        HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+        mNativeWrapper.clearResultMessages();
+        action.processCommand(response2);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+                mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
+                new int[]{Constants.AUDIO_CODEC_DTSHD, Constants.AUDIO_CODEC_TRUEHD,
+                        Constants.AUDIO_CODEC_DST, Constants.AUDIO_CODEC_MAX});
+        byte[] sadsToRespond_3 = new byte[]{
+                0x0B, 0x4B, 0x00,
+                0x0C, 0x20, 0x0A,
+                0x0D, 0x18, 0x4A,
+                0x0F, 0x4B, 0x00};
+        HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+                Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+        mNativeWrapper.clearResultMessages();
+        action.processCommand(response3);
+        mTestLooper.dispatchAll();
+
+        assertThat(mSupportedSads.size()).isEqualTo(12);
+        assertThat(Arrays.equals(sadsToRespond_1,
+                concatenateSads(mSupportedSads.subList(0, 4)))).isTrue();
+        assertThat(Arrays.equals(sadsToRespond_2,
+                concatenateSads(mSupportedSads.subList(4, 8)))).isTrue();
+        assertThat(Arrays.equals(sadsToRespond_3,
+                concatenateSads(mSupportedSads.subList(8, 12)))).isTrue();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locales/FakePackageConfigurationUpdater.java b/services/tests/servicestests/src/com/android/server/locales/FakePackageConfigurationUpdater.java
index 1613b11..6cdae53 100644
--- a/services/tests/servicestests/src/com/android/server/locales/FakePackageConfigurationUpdater.java
+++ b/services/tests/servicestests/src/com/android/server/locales/FakePackageConfigurationUpdater.java
@@ -43,7 +43,9 @@
     }
 
     @Override
-    public void commit() {}
+    public boolean commit() {
+        return mLocales != null;
+    }
 
     /**
      * Returns the locales that were stored during the test run. Returns {@code null} if no locales
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 76f233c..f91661b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -35,9 +35,13 @@
 import android.content.pm.UserInfo;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedActivityImpl;
 import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedInstrumentationImpl;
 import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedIntentInfoImpl;
 import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedProviderImpl;
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
@@ -82,9 +86,17 @@
     private static final int DUMMY_OVERLAY_APPID = 10756;
     private static final int SYSTEM_USER = 0;
     private static final int SECONDARY_USER = 10;
+    private static final int ADDED_USER = 11;
     private static final int[] USER_ARRAY = {SYSTEM_USER, SECONDARY_USER};
-    private static final UserInfo[] USER_INFO_LIST = Arrays.stream(USER_ARRAY).mapToObj(
-            id -> new UserInfo(id, Integer.toString(id), 0)).toArray(UserInfo[]::new);
+    private static final int[] USER_ARRAY_WITH_ADDED = {SYSTEM_USER, SECONDARY_USER, ADDED_USER};
+    private static final UserInfo[] USER_INFO_LIST = toUserInfos(USER_ARRAY);
+    private static final UserInfo[] USER_INFO_LIST_WITH_ADDED = toUserInfos(USER_ARRAY_WITH_ADDED);
+
+    private static UserInfo[] toUserInfos(int[] userIds) {
+        return Arrays.stream(userIds)
+                .mapToObj(id -> new UserInfo(id, Integer.toString(id), 0))
+                .toArray(UserInfo[]::new);
+    }
 
     @Mock
     AppsFilter.FeatureConfig mFeatureConfigMock;
@@ -146,21 +158,22 @@
     }
 
     private static ParsedActivity createActivity(String packageName, IntentFilter[] filters) {
-        ParsedActivity activity = new ParsedActivity();
+        ParsedActivityImpl activity = new ParsedActivityImpl();
         activity.setPackageName(packageName);
         for (IntentFilter filter : filters) {
-            final ParsedIntentInfo info = new ParsedIntentInfo();
+            final ParsedIntentInfoImpl info = new ParsedIntentInfoImpl();
+            final IntentFilter intentInfoFilter = info.getIntentFilter();
             if (filter.countActions() > 0) {
-                filter.actionsIterator().forEachRemaining(info::addAction);
+                filter.actionsIterator().forEachRemaining(intentInfoFilter::addAction);
             }
             if (filter.countCategories() > 0) {
-                filter.actionsIterator().forEachRemaining(info::addAction);
+                filter.actionsIterator().forEachRemaining(intentInfoFilter::addAction);
             }
             if (filter.countDataAuthorities() > 0) {
-                filter.authoritiesIterator().forEachRemaining(info::addDataAuthority);
+                filter.authoritiesIterator().forEachRemaining(intentInfoFilter::addDataAuthority);
             }
             if (filter.countDataSchemes() > 0) {
-                filter.schemesIterator().forEachRemaining(info::addDataScheme);
+                filter.schemesIterator().forEachRemaining(intentInfoFilter::addDataScheme);
             }
             activity.addIntent(info);
             activity.setExported(true);
@@ -170,13 +183,13 @@
 
     private static ParsingPackage pkgWithInstrumentation(
             String packageName, String instrumentationTargetPackage) {
-        ParsedInstrumentation instrumentation = new ParsedInstrumentation();
+        ParsedInstrumentationImpl instrumentation = new ParsedInstrumentationImpl();
         instrumentation.setTargetPackage(instrumentationTargetPackage);
         return pkg(packageName).addInstrumentation(instrumentation);
     }
 
     private static ParsingPackage pkgWithProvider(String packageName, String authority) {
-        ParsedProvider provider = new ParsedProvider();
+        ParsedProviderImpl provider = new ParsedProviderImpl();
         provider.setPackageName(packageName);
         provider.setExported(true);
         provider.setAuthority(authority);
@@ -318,6 +331,64 @@
     }
 
     @Test
+    public void testOnUserUpdated_FilterMatches() throws Exception {
+        final AppsFilter appsFilter =
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
+        simulateAddBasicAndroid(appsFilter);
+
+        appsFilter.onSystemReady();
+
+        PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID);
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkgQueriesProvider("com.some.other.package", "com.some.authority"),
+                DUMMY_CALLING_APPID);
+
+        for (int subjectUserId : USER_ARRAY) {
+            for (int otherUserId : USER_ARRAY) {
+                assertFalse(appsFilter.shouldFilterApplication(
+                        UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target,
+                        otherUserId));
+            }
+        }
+
+        // adds new user
+        doAnswer(invocation -> {
+            ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0))
+                    .currentState(mExisting, USER_INFO_LIST_WITH_ADDED);
+            return new Object();
+        }).when(mStateProvider)
+                .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class));
+        appsFilter.onUserCreated(ADDED_USER);
+
+        for (int subjectUserId : USER_ARRAY_WITH_ADDED) {
+            for (int otherUserId : USER_ARRAY_WITH_ADDED) {
+                assertFalse(appsFilter.shouldFilterApplication(
+                        UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target,
+                        otherUserId));
+            }
+        }
+
+        // delete user
+        doAnswer(invocation -> {
+            ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0))
+                    .currentState(mExisting, USER_INFO_LIST);
+            return new Object();
+        }).when(mStateProvider)
+                .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class));
+        appsFilter.onUserDeleted(ADDED_USER);
+
+        for (int subjectUserId : USER_ARRAY) {
+            for (int otherUserId : USER_ARRAY) {
+                assertFalse(appsFilter.shouldFilterApplication(
+                        UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target,
+                        otherUserId));
+            }
+        }
+    }
+
+    @Test
     public void testQueriesDifferentProvider_Filters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index c85b2e8..cbd1aa3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -45,14 +45,24 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedActivityImpl;
 import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedInstrumentationImpl;
 import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedIntentInfoImpl;
 import android.content.pm.parsing.component.ParsedPermission;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedPermissionGroupImpl;
+import android.content.pm.parsing.component.ParsedPermissionImpl;
+import android.content.pm.parsing.component.ParsedPermissionUtils;
 import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedProviderImpl;
 import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedServiceImpl;
 import android.content.pm.parsing.component.ParsedUsesPermission;
+import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
+import android.content.pm.permission.CompatibilityPermissionInfo;
 import android.content.pm.pkg.PackageUserState;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -522,7 +532,7 @@
             final ParsedPackage pkg = new TestPackageParser2()
                     .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
             final List<String> compatPermissions =
-                    Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+                    Arrays.stream(COMPAT_PERMS).map(CompatibilityPermissionInfo::getName)
                             .collect(toList());
             assertWithMessage(
                     "Compatibility permissions shouldn't be added into uses permissions.")
@@ -548,19 +558,19 @@
                     .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
             assertWithMessage(
                     "Compatibility permissions should be added into uses permissions.")
-                    .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+                    .that(Arrays.stream(COMPAT_PERMS).map(CompatibilityPermissionInfo::getName)
                             .allMatch(pkg.getUsesPermissions().stream()
                                     .map(ParsedUsesPermission::getName)
                             .collect(toList())::contains))
                     .isTrue();
             assertWithMessage(
                     "Compatibility permissions should be added into requested permissions.")
-                    .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+                    .that(Arrays.stream(COMPAT_PERMS).map(CompatibilityPermissionInfo::getName)
                             .allMatch(pkg.getRequestedPermissions()::contains))
                     .isTrue();
             assertWithMessage(
                     "Compatibility permissions should be added into implicit permissions.")
-                    .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+                    .that(Arrays.stream(COMPAT_PERMS).map(CompatibilityPermissionInfo::getName)
                             .allMatch(pkg.getImplicitPermissions()::contains))
                     .isTrue();
         } finally {
@@ -770,7 +780,8 @@
         // Verify basic flags in PermissionInfo to make sure they're consistent. We don't perform
         // a full structural equality here because the code that serializes them isn't parser
         // specific and is tested elsewhere.
-        assertEquals(a.getProtection(), b.getProtection());
+        assertEquals(ParsedPermissionUtils.getProtection(a),
+                ParsedPermissionUtils.getProtection(b));
         assertEquals(a.getGroup(), b.getGroup());
         assertEquals(a.getFlags(), b.getFlags());
 
@@ -895,8 +906,8 @@
         Bundle bundle = new Bundle();
         bundle.putString("key", "value");
 
-        ParsedPermission permission = new ParsedPermission();
-        permission.setParsedPermissionGroup(new ParsedPermissionGroup());
+        ParsedPermissionImpl permission = new ParsedPermissionImpl();
+        permission.setParsedPermissionGroup(new ParsedPermissionGroupImpl());
 
         ((ParsedPackage) pkg.setBaseRevisionCode(100)
                 .setBaseHardwareAccelerated(true)
@@ -912,13 +923,13 @@
                 .setUse32BitAbi(true)
                 .setVolumeUuid("d52ef59a-7def-4541-bf21-4c28ed4b65a0")
                 .addPermission(permission)
-                .addPermissionGroup(new ParsedPermissionGroup())
-                .addActivity(new ParsedActivity())
-                .addReceiver(new ParsedActivity())
-                .addProvider(new ParsedProvider())
-                .addService(new ParsedService())
-                .addInstrumentation(new ParsedInstrumentation())
-                .addUsesPermission(new ParsedUsesPermission("foo7", 0))
+                .addPermissionGroup(new ParsedPermissionGroupImpl())
+                .addActivity(new ParsedActivityImpl())
+                .addReceiver(new ParsedActivityImpl())
+                .addProvider(new ParsedProviderImpl())
+                .addService(new ParsedServiceImpl())
+                .addInstrumentation(new ParsedInstrumentationImpl())
+                .addUsesPermission(new ParsedUsesPermissionImpl("foo7", 0))
                 .addImplicitPermission("foo25")
                 .addProtectedBroadcast("foo8")
                 .setStaticSharedLibName("foo23")
@@ -946,7 +957,7 @@
                 .setOverlayTarget("foo21")
                 .setOverlayPriority(100)
                 .setUpgradeKeySets(new ArraySet<>())
-                .addPreferredActivityFilter("className", new ParsedIntentInfo())
+                .addPreferredActivityFilter("className", new ParsedIntentInfoImpl())
                 .addConfigPreference(new ConfigurationInfo())
                 .addReqFeature(new FeatureInfo())
                 .addFeatureGroup(new FeatureGroupInfo())
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 82bf2f4..6188bc4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -43,7 +43,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.component.ParsedUsesPermission;
+import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
 import android.content.res.TypedArray;
 import android.os.Environment;
 import android.os.UserHandle;
@@ -89,6 +89,8 @@
     PackageManagerServiceInjector mMockInjector;
     @Mock
     PackageManagerService mMockPackageManager;
+    @Mock
+    Installer mMockInstaller;
 
     @Before
     public void setupInjector() {
@@ -103,6 +105,7 @@
 
         when(mMockInjector.getDomainVerificationManagerInternal())
                 .thenReturn(domainVerificationManager);
+        when(mMockInjector.getInstaller()).thenReturn(mMockInstaller);
     }
 
     @Before
@@ -432,9 +435,11 @@
     @Test
     public void factoryTestFlagSet() throws Exception {
         final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
-                .addUsesPermission(new ParsedUsesPermission(Manifest.permission.FACTORY_TEST, 0));
+                .addUsesPermission(
+                        new ParsedUsesPermissionImpl(Manifest.permission.FACTORY_TEST, 0));
 
-        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mMockPackageManager);
+        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(
+                mMockPackageManager, mMockInjector);
         final ScanResult scanResult = scanPackageHelper.scanPackageOnlyLI(
                 createBasicScanRequestBuilder(basicPackage).build(),
                 mMockInjector,
@@ -483,7 +488,8 @@
 
     private ScanResult executeScan(
             ScanRequest scanRequest) throws PackageManagerException {
-        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mMockPackageManager);
+        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(
+                mMockPackageManager, mMockInjector);
         ScanResult result = scanPackageHelper.scanPackageOnlyLI(
                 scanRequest,
                 mMockInjector,
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 0602a55..f009488 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -334,10 +334,8 @@
             launchToken=${this.launchToken}
             lockTaskLaunchMode=${this.lockTaskLaunchMode}
             logo=${this.logo}
-            maxAspectRatio=${this.maxAspectRatio}
             maxRecents=${this.maxRecents}
             metaData=${this.metaData.dumpToString()}
-            minAspectRatio=${this.minAspectRatio}
             name=${this.name}
             nonLocalizedLabel=${
                 // Per b/184574333, v1 mistakenly trimmed the label. v2 fixed this, but for test
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 1502839..c990342 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -23,6 +23,7 @@
 
 import android.apex.ApexInfo;
 import android.content.Context;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -33,7 +34,9 @@
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Build;
@@ -63,6 +66,7 @@
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
 
@@ -409,7 +413,7 @@
 
     private void assertPermission(String name, int protectionLevel, ParsedPermission permission) {
         assertEquals(name, permission.getName());
-        assertEquals(protectionLevel, permission.getProtection());
+        assertEquals(protectionLevel, ParsedPermissionUtils.getProtection(permission));
     }
 
     private void assertMetadata(Bundle b, String... keysAndValues) {
@@ -512,12 +516,13 @@
         findAndRemoveAppDetailsActivity(p);
 
         assertEquals("Expected exactly one activity", 1, p.getActivities().size());
-        assertEquals("Expected exactly one intent filter",
-                1, p.getActivities().get(0).getIntents().size());
-        assertEquals("Expected exactly one mime group in intent filter",
-                1, p.getActivities().get(0).getIntents().get(0).countMimeGroups());
+        List<ParsedIntentInfo> intentInfos = p.getActivities().get(0).getIntents();
+        assertEquals("Expected exactly one intent filter", 1, intentInfos.size());
+        IntentFilter intentFilter = intentInfos.get(0).getIntentFilter();
+        assertEquals("Expected exactly one mime group in intent filter", 1,
+                intentFilter.countMimeGroups());
         assertTrue("Did not find expected mime group 'mime_group_1'",
-                p.getActivities().get(0).getIntents().get(0).hasMimeGroup("mime_group_1"));
+                intentFilter.hasMimeGroup("mime_group_1"));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index 4dc9a90..c4aa862 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -20,10 +20,7 @@
 import android.content.Context
 import android.content.pm.parsing.ParsingPackage
 import android.content.pm.parsing.ParsingPackageUtils
-import android.content.pm.parsing.result.ParseInput
-import android.content.pm.parsing.result.ParseInput.DeferredError
 import android.content.pm.parsing.result.ParseResult
-import android.os.Build
 import androidx.test.InstrumentationRegistry
 import com.android.frameworks.servicestests.R
 import com.google.common.truth.Truth.assertThat
@@ -54,14 +51,6 @@
 
     private val context: Context = InstrumentationRegistry.getContext()
 
-    private val inputCallback = ParseInput.Callback { changeId, _, targetSdk ->
-        when (changeId) {
-            DeferredError.MISSING_APP_TAG -> targetSdk > Build.VERSION_CODES.Q
-            DeferredError.EMPTY_INTENT_ACTION_CATEGORY -> targetSdk > Build.VERSION_CODES.Q
-            else -> throw IllegalStateException("changeId $changeId is not mocked for test")
-        }
-    }
-
     @get:Rule
     val tempFolder = TemporaryFolder(context.filesDir)
 
@@ -76,8 +65,9 @@
         assertThat(first.name).isEqualTo(TEST_ACTIVITY)
         val intents = first.intents
         assertThat(intents).hasSize(1)
-        assertThat(intents.first().hasCategory(TEST_CATEGORY)).isTrue()
-        assertThat(intents.first().hasAction(TEST_ACTION)).isTrue()
+        val intentFilter = intents.first().intentFilter
+        assertThat(intentFilter.hasCategory(TEST_CATEGORY)).isTrue()
+        assertThat(intentFilter.hasAction(TEST_ACTION)).isTrue()
     }
 
     @Test
@@ -97,7 +87,8 @@
         assertThat(first.name).isEqualTo(TEST_ACTIVITY)
         val intents = first.intents
         assertThat(intents).hasSize(1)
-        assertThat(intents.first().hasAction(TEST_ACTION)).isTrue()
+        val intentFilter = intents.first().intentFilter
+        assertThat(intentFilter.hasAction(TEST_ACTION)).isTrue()
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
index 5870a70..79f8b0e 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
@@ -34,26 +34,36 @@
 
     @Test
     public void testEquals() {
-        GeolocationTimeZoneSuggestion one = new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS1);
-        assertEquals(one, one);
+        long time1 = 1111L;
+        GeolocationTimeZoneSuggestion certain1v1 =
+                GeolocationTimeZoneSuggestion.createCertainSuggestion(time1, ARBITRARY_ZONE_IDS1);
+        assertEquals(certain1v1, certain1v1);
 
-        GeolocationTimeZoneSuggestion two = new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS1);
-        assertEquals(one, two);
-        assertEquals(two, one);
-
-        GeolocationTimeZoneSuggestion nullZone = new GeolocationTimeZoneSuggestion(null);
-        assertNotEquals(one, nullZone);
-        assertNotEquals(nullZone, one);
-        assertEquals(nullZone, nullZone);
-
-        GeolocationTimeZoneSuggestion three =
-                new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS2);
-        assertNotEquals(one, three);
-        assertNotEquals(three, one);
+        GeolocationTimeZoneSuggestion certain1v2 =
+                GeolocationTimeZoneSuggestion.createCertainSuggestion(time1, ARBITRARY_ZONE_IDS1);
+        assertEquals(certain1v1, certain1v2);
+        assertEquals(certain1v2, certain1v1);
 
         // DebugInfo must not be considered in equals().
-        one.addDebugInfo("Debug info 1");
-        two.addDebugInfo("Debug info 2");
-        assertEquals(one, two);
+        certain1v1.addDebugInfo("Debug info 1");
+        certain1v2.addDebugInfo("Debug info 2");
+        assertEquals(certain1v1, certain1v2);
+
+        long time2 = 2222L;
+        GeolocationTimeZoneSuggestion certain2 =
+                GeolocationTimeZoneSuggestion.createCertainSuggestion(time2, ARBITRARY_ZONE_IDS1);
+        assertNotEquals(certain1v1, certain2);
+        assertNotEquals(certain2, certain1v1);
+
+        GeolocationTimeZoneSuggestion uncertain =
+                GeolocationTimeZoneSuggestion.createUncertainSuggestion(time1);
+        assertNotEquals(certain1v1, uncertain);
+        assertNotEquals(uncertain, certain1v1);
+        assertEquals(uncertain, uncertain);
+
+        GeolocationTimeZoneSuggestion certain3 =
+                GeolocationTimeZoneSuggestion.createCertainSuggestion(time1, ARBITRARY_ZONE_IDS2);
+        assertNotEquals(certain1v1, certain3);
+        assertNotEquals(certain3, certain1v1);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
index 918babc..5864620 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -35,6 +35,7 @@
 @RunWith(AndroidJUnit4.class)
 public class TimeZoneDetectorInternalImplTest {
 
+    private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
     private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList("TestZoneId");
 
     private Context mMockContext;
@@ -99,6 +100,7 @@
     }
 
     private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
-        return new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS);
+        return GeolocationTimeZoneSuggestion.createCertainSuggestion(
+                ARBITRARY_ELAPSED_REALTIME_MILLIS, ARBITRARY_ZONE_IDS);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 28838ae..773abf8 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -50,11 +50,14 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Arrays;
+import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 public class TimeZoneDetectorServiceTest {
 
     private static final int ARBITRARY_USER_ID = 9999;
+    private static final List<String> ARBITRARY_TIME_ZONE_IDS = Arrays.asList("TestZoneId");
+    private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
 
     private Context mMockContext;
     private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
@@ -374,7 +377,8 @@
     }
 
     private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
-        return new GeolocationTimeZoneSuggestion(Arrays.asList("TestZoneId"));
+        return GeolocationTimeZoneSuggestion.createCertainSuggestion(
+                ARBITRARY_ELAPSED_REALTIME_MILLIS, ARBITRARY_TIME_ZONE_IDS);
     }
 
     private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 331f76c..e2e8755 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -33,10 +33,13 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.time.TimeZoneConfiguration;
@@ -171,6 +174,8 @@
     private FakeEnvironment mFakeEnvironment;
     private MockConfigChangeListener mMockConfigChangeListener;
 
+    // A fake source of time for suggestions. This will typically be incremented after every use.
+    @ElapsedRealtimeLong private long mElapsedRealtimeMillis;
 
     @Before
     public void setUp() {
@@ -487,7 +492,7 @@
          */
 
         // Each test case will have the same or lower score than the last.
-        List<TelephonyTestCase> descendingCasesByScore = list(TELEPHONY_TEST_CASES);
+        List<TelephonyTestCase> descendingCasesByScore = Arrays.asList(TELEPHONY_TEST_CASES);
         Collections.reverse(descendingCasesByScore);
 
         for (TelephonyTestCase testCase : descendingCasesByScore) {
@@ -750,7 +755,7 @@
         Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
-        GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeoLocationSuggestion();
+        GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeolocationSuggestion();
 
         script.simulateGeolocationTimeZoneSuggestion(uncertainSuggestion)
                 .verifyTimeZoneNotChanged();
@@ -766,7 +771,7 @@
                 .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
-        GeolocationTimeZoneSuggestion noZonesSuggestion = createGeoLocationSuggestion(list());
+        GeolocationTimeZoneSuggestion noZonesSuggestion = createCertainGeolocationSuggestion();
 
         script.simulateGeolocationTimeZoneSuggestion(noZonesSuggestion)
                 .verifyTimeZoneNotChanged();
@@ -778,7 +783,7 @@
     @Test
     public void testGeoSuggestion_oneZone() {
         GeolocationTimeZoneSuggestion suggestion =
-                createGeoLocationSuggestion(list("Europe/London"));
+                createCertainGeolocationSuggestion("Europe/London");
 
         Script script = new Script()
                 .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
@@ -799,11 +804,11 @@
     @Test
     public void testGeoSuggestion_multiZone() {
         GeolocationTimeZoneSuggestion londonOnlySuggestion =
-                createGeoLocationSuggestion(list("Europe/London"));
+                createCertainGeolocationSuggestion("Europe/London");
         GeolocationTimeZoneSuggestion londonOrParisSuggestion =
-                createGeoLocationSuggestion(list("Europe/Paris", "Europe/London"));
+                createCertainGeolocationSuggestion("Europe/Paris", "Europe/London");
         GeolocationTimeZoneSuggestion parisOnlySuggestion =
-                createGeoLocationSuggestion(list("Europe/Paris"));
+                createCertainGeolocationSuggestion("Europe/Paris");
 
         Script script = new Script()
                 .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
@@ -836,7 +841,7 @@
     @Test
     public void testGeoSuggestion_togglingGeoDetectionClearsLastSuggestion() {
         GeolocationTimeZoneSuggestion suggestion =
-                createGeoLocationSuggestion(list("Europe/London"));
+                createCertainGeolocationSuggestion("Europe/London");
 
         Script script = new Script()
                 .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
@@ -863,7 +868,7 @@
     @Test
     public void testChangingGeoDetectionEnabled() {
         GeolocationTimeZoneSuggestion geolocationSuggestion =
-                createGeoLocationSuggestion(list("Europe/London"));
+                createCertainGeolocationSuggestion("Europe/London");
         TelephonyTimeZoneSuggestion telephonySuggestion = createTelephonySuggestion(
                 SLOT_INDEX1, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
                 "Europe/Paris");
@@ -960,7 +965,7 @@
                 createTelephonySuggestion(0 /* slotIndex */, MATCH_TYPE_NETWORK_COUNTRY_ONLY,
                         QUALITY_SINGLE_ZONE, "Zone2");
         GeolocationTimeZoneSuggestion geolocationTimeZoneSuggestion =
-                createGeoLocationSuggestion(Arrays.asList("Zone3", "Zone2"));
+                createCertainGeolocationSuggestion("Zone3", "Zone2");
         script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
                 .verifyTimeZoneNotChanged()
                 .simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
@@ -1052,13 +1057,18 @@
         return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX2).build();
     }
 
-    private static GeolocationTimeZoneSuggestion createUncertainGeoLocationSuggestion() {
-        return createGeoLocationSuggestion(null);
+    private GeolocationTimeZoneSuggestion createUncertainGeolocationSuggestion() {
+        return GeolocationTimeZoneSuggestion.createCertainSuggestion(
+                mElapsedRealtimeMillis++, null);
     }
 
-    private static GeolocationTimeZoneSuggestion createGeoLocationSuggestion(
-            @Nullable List<String> zoneIds) {
-        GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(zoneIds);
+    private GeolocationTimeZoneSuggestion createCertainGeolocationSuggestion(
+            @NonNull String... zoneIds) {
+        assertNotNull(zoneIds);
+
+        GeolocationTimeZoneSuggestion suggestion =
+                GeolocationTimeZoneSuggestion.createCertainSuggestion(
+                        mElapsedRealtimeMillis++, Arrays.asList(zoneIds));
         suggestion.addDebugInfo("Test suggestion");
         return suggestion;
     }
@@ -1321,8 +1331,4 @@
             mOnChangeCalled = false;
         }
     }
-
-    private static <T> List<T> list(T... values) {
-        return Arrays.asList(values);
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index 27d9546..7d6772e 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -33,8 +33,10 @@
 
 import static java.util.Arrays.asList;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.service.timezone.TimeZoneProviderEvent;
 import android.service.timezone.TimeZoneProviderSuggestion;
@@ -287,8 +289,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
@@ -326,8 +328,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
@@ -366,8 +368,8 @@
                 PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
@@ -395,8 +397,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // A second, identical event should not cause another suggestion.
@@ -416,8 +418,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
@@ -456,8 +458,8 @@
                 PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // A second, identical event should not cause another suggestion.
@@ -479,8 +481,8 @@
                 PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
@@ -508,8 +510,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Simulate an uncertain event being received from the primary provider. This should not
@@ -535,8 +537,8 @@
                 PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Simulate an uncertain event being received from the secondary provider. This should not
@@ -560,7 +562,8 @@
                 PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
-        mTestCallback.assertUncertainSuggestionMadeAndCommit();
+        mTestCallback.assertUncertainSuggestionMadeFromEventAndCommit(
+                USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
@@ -588,8 +591,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Uncertainty should not cause a suggestion to be made straight away, but the uncertainty
@@ -613,8 +616,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
@@ -682,8 +685,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Now signal a config change so that geo detection is disabled.
@@ -723,8 +726,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Simulate the user change (but geo detection still enabled).
@@ -789,8 +792,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
         mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Simulate uncertainty from the secondary.
@@ -896,8 +899,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Simulate uncertainty from the primary. The secondary cannot be started.
@@ -1098,8 +1101,8 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
-        mTestCallback.assertSuggestionMadeAndCommit(
-                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+        mTestCallback.assertCertainSuggestionMadeFromEventAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Trigger destroy().
@@ -1186,6 +1189,13 @@
             return UNCERTAINTY_DELAY;
         }
 
+        @Override
+        long elapsedRealtimeMillis() {
+            // The properties of the real clock will also work for tests, i.e. it doesn't go
+            // backwards.
+            return SystemClock.elapsedRealtime();
+        }
+
         void simulateConfigChange(ConfigurationInternal newConfig) {
             ConfigurationInternal oldConfig = mConfigurationInternal;
             mConfigurationInternal = Objects.requireNonNull(newConfig);
@@ -1209,19 +1219,52 @@
             mLatestSuggestion.set(suggestion);
         }
 
-        void assertSuggestionMadeAndCommit(@Nullable List<String> expectedZoneIds) {
-            mLatestSuggestion.assertHasBeenSet();
-            assertEquals(expectedZoneIds, mLatestSuggestion.getLatest().getZoneIds());
-            mLatestSuggestion.commitLatest();
+        void assertCertainSuggestionMadeFromEventAndCommit(TimeZoneProviderEvent event) {
+            // Test coding error if this fails.
+            assertEquals(TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION, event.getType());
+
+            TimeZoneProviderSuggestion suggestion = event.getSuggestion();
+            assertSuggestionMadeAndCommit(
+                    suggestion.getElapsedRealtimeMillis(),
+                    suggestion.getTimeZoneIds());
         }
 
         void assertNoSuggestionMade() {
             mLatestSuggestion.assertHasNotBeenSet();
         }
 
+        /** Asserts that an uncertain suggestion has been made from the supplied event. */
+        void assertUncertainSuggestionMadeFromEventAndCommit(TimeZoneProviderEvent event) {
+            // Test coding error if this fails.
+            assertEquals(TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN, event.getType());
+
+            assertSuggestionMadeAndCommit(event.getCreationElapsedMillis(), null);
+        }
+
+        /**
+         * Asserts that an uncertain suggestion has been made.
+         * Ignores the suggestion's effectiveFromElapsedMillis.
+         */
         void assertUncertainSuggestionMadeAndCommit() {
             // An "uncertain" suggestion has null time zone IDs.
-            assertSuggestionMadeAndCommit(null);
+            assertSuggestionMadeAndCommit(null, null);
+        }
+
+        /**
+         * Asserts that a suggestion has been made and some properties of that suggestion.
+         * When expectedEffectiveFromElapsedMillis is null then its value isn't checked.
+         */
+        private void assertSuggestionMadeAndCommit(
+                @Nullable @ElapsedRealtimeLong Long expectedEffectiveFromElapsedMillis,
+                @Nullable List<String> expectedZoneIds) {
+            mLatestSuggestion.assertHasBeenSet();
+            if (expectedEffectiveFromElapsedMillis != null) {
+                assertEquals(
+                        expectedEffectiveFromElapsedMillis.longValue(),
+                        mLatestSuggestion.getLatest().getEffectiveFromElapsedMillis());
+            }
+            assertEquals(expectedZoneIds, mLatestSuggestion.getLatest().getZoneIds());
+            mLatestSuggestion.commitLatest();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
index f361f4a..4ed4c23 100644
--- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
@@ -915,6 +915,23 @@
         }
     }
 
+    // Fill new cells in the matrix which has enlarged capacity.
+    private void fillNew(WatchedSparseBooleanMatrix matrix, int initialCapacity,
+            int newCapacity, int[] indexes) {
+        final int size = newCapacity;
+        for (int i = 0; i < size; i++) {
+            for (int j = 0; j < size; j++) {
+                if (i < initialCapacity && j < initialCapacity) {
+                    // Do not touch old cells
+                    continue;
+                }
+                final int row = indexes[i];
+                final int col = indexes[j];
+                matrix.put(row, col, cellValue(i, j));
+            }
+        }
+    }
+
     // Verify the content of a matrix.  This asserts on mismatch.  Selected indices may
     // have been deleted.
     private void verify(WatchedSparseBooleanMatrix matrix, int[] indexes, boolean[] absent) {
@@ -989,6 +1006,24 @@
         assertTrue("Matrix shrink", finalCapacity - matrix.size() < matrix.STEP);
     }
 
+    private void matrixSetCapacity(WatchedSparseBooleanMatrix matrix, int newCapacity,
+            IndexGenerator indexer) {
+        final int initialCapacity = matrix.capacity();
+        final int[] indexes = indexer.indexes(Math.max(initialCapacity, newCapacity));
+        fill(matrix, initialCapacity, indexes);
+
+        matrix.setCapacity(newCapacity);
+        fillNew(matrix, initialCapacity, newCapacity, indexes);
+
+        assertEquals(matrix.size(), indexes.length);
+        verify(matrix, indexes, null);
+        // Test the keyAt/indexOfKey methods
+        for (int i = 0; i < matrix.size(); i++) {
+            int key = indexes[i];
+            assertEquals(matrix.keyAt(matrix.indexOfKey(key)), key);
+        }
+    }
+
     @Test
     public void testWatchedSparseBooleanMatrix() {
         final String name = "WatchedSparseBooleanMatrix";
@@ -1052,6 +1087,46 @@
     }
 
     @Test
+    public void testWatchedSparseBooleanMatrix_setCapacity() {
+        final IndexGenerator indexer = new IndexGenerator(3);
+        matrixSetCapacity(new WatchedSparseBooleanMatrix(500), 1000, indexer);
+        matrixSetCapacity(new WatchedSparseBooleanMatrix(1000), 500, indexer);
+    }
+
+    @Test
+    public void testWatchedSparseBooleanMatrix_removeRangeAndShrink() {
+        final IndexGenerator indexer = new IndexGenerator(3);
+        final int initialCapacity = 500;
+        final int removeCounts = 33;
+        final WatchedSparseBooleanMatrix matrix = new WatchedSparseBooleanMatrix(initialCapacity);
+        final int[] indexes = indexer.indexes(initialCapacity);
+        final boolean[] absents = new boolean[initialCapacity];
+        fill(matrix, initialCapacity, indexes);
+        assertEquals(matrix.size(), initialCapacity);
+
+        for (int i = 0; i < initialCapacity / removeCounts; i++) {
+            final int size = matrix.size();
+            final int fromIndex = (size / 2 < removeCounts ? 0 : size / 2 - removeCounts);
+            final int toIndex = (fromIndex + removeCounts > size ? size : fromIndex + removeCounts);
+            for (int index = fromIndex; index < toIndex; index++) {
+                final int key = matrix.keyAt(index);
+                for (int j = 0; j < indexes.length; j++) {
+                    if (key == indexes[j]) {
+                        absents[j] = true;
+                        break;
+                    }
+                }
+            }
+            matrix.removeRange(fromIndex, toIndex);
+            assertEquals(matrix.size(), size - (toIndex - fromIndex));
+            verify(matrix, indexes, absents);
+
+            matrix.compact();
+            verify(matrix, indexes, absents);
+        }
+    }
+
+    @Test
     public void testNestedArrays() {
         final String name = "NestedArrays";
         WatchableTester tester;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 378304d..777e3f4 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -51,6 +51,7 @@
     private final FakeNativeWrapper mNativeWrapper;
 
     private boolean mIsAvailable = true;
+    private boolean mIsInfoLoadSuccessful = true;
     private long mLatency;
     private int mOffCount;
 
@@ -172,7 +173,7 @@
             infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency,
                     mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange,
                     mMaxAmplitudes));
-            return true;
+            return mIsInfoLoadSuccessful;
         }
 
         private void applyLatency() {
@@ -213,6 +214,14 @@
     }
 
     /**
+     * Sets the result for the method that loads the {@link VibratorInfo}, for faking a vibrator
+     * that fails to load some of the hardware data.
+     */
+    public void setVibratorInfoLoadSuccessful(boolean successful) {
+        mIsInfoLoadSuccessful = successful;
+    }
+
+    /**
      * Sets the latency this controller should fake for turning the vibrator hardware on or setting
      * it's vibration amplitude.
      */
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 5e25bc5..6118169 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -287,7 +287,26 @@
     }
 
     @Test
-    public void getVibratorInfo_withExistingVibratorId_returnsHalInfoForVibrator() {
+    public void getVibratorInfo_vibratorFailedLoadBeforeSystemReady_returnsNull() {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setVibratorInfoLoadSuccessful(false);
+        assertNull(createService().getVibratorInfo(1));
+    }
+
+    @Test
+    public void getVibratorInfo_vibratorFailedLoadAfterSystemReady_returnsInfoForVibrator() {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setVibratorInfoLoadSuccessful(false);
+        mVibratorProviders.get(1).setResonantFrequency(123.f);
+        VibratorInfo info = createSystemReadyService().getVibratorInfo(1);
+
+        assertNotNull(info);
+        assertEquals(1, info.getId());
+        assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
+    }
+
+    @Test
+    public void getVibratorInfo_vibratorSuccessfulLoadBeforeSystemReady_returnsInfoForVibrator() {
         mockVibrators(1);
         FakeVibratorControllerProvider vibrator = mVibratorProviders.get(1);
         vibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -295,7 +314,7 @@
         vibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
         vibrator.setResonantFrequency(123.f);
         vibrator.setQFactor(Float.NaN);
-        VibratorInfo info = createSystemReadyService().getVibratorInfo(1);
+        VibratorInfo info = createService().getVibratorInfo(1);
 
         assertNotNull(info);
         assertEquals(1, info.getId());
@@ -313,6 +332,24 @@
     }
 
     @Test
+    public void getVibratorInfo_vibratorFailedThenSuccessfulLoad_returnsNullThenInfo() {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setVibratorInfoLoadSuccessful(false);
+
+        VibratorManagerService service = createService();
+        assertNull(createService().getVibratorInfo(1));
+
+        mVibratorProviders.get(1).setVibratorInfoLoadSuccessful(true);
+        mVibratorProviders.get(1).setResonantFrequency(123.f);
+        service.systemReady();
+
+        VibratorInfo info = createService().getVibratorInfo(1);
+        assertNotNull(info);
+        assertEquals(1, info.getId());
+        assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
+    }
+
+    @Test
     public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 16ee1e8..5694e59 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -132,6 +132,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
@@ -181,7 +182,6 @@
 import android.util.Xml;
 import android.widget.RemoteViews;
 
-import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.app.IAppOpsService;
@@ -248,6 +248,8 @@
     @Mock
     private PackageManager mPackageManagerClient;
     @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+    @Mock
     private WindowManagerInternal mWindowManagerInternal;
     @Mock
     private PermissionHelper mPermissionHelper;
@@ -375,6 +377,8 @@
         LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.addService(ActivityManagerInternal.class, mAmi);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
         mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
@@ -1035,7 +1039,7 @@
         NotificationRecord r = generateNotificationRecord(channel);
 
         // isBlocked is only used for user blocking, not app suspension
-        assertFalse(mService.isBlocked(r, mUsageStats));
+        assertFalse(mService.isRecordBlockedLocked(r));
     }
 
     @Test
@@ -1045,8 +1049,7 @@
         NotificationChannel channel = new NotificationChannel("id", "name",
                 NotificationManager.IMPORTANCE_NONE);
         NotificationRecord r = generateNotificationRecord(channel);
-        assertTrue(mService.isBlocked(r, mUsageStats));
-        verify(mUsageStats, times(1)).registerBlocked(eq(r));
+        assertTrue(mService.isRecordBlockedLocked(r));
 
         mBinderService.createNotificationChannels(
                 PKG, new ParceledListSlice(Arrays.asList(channel)));
@@ -1143,8 +1146,7 @@
                 NotificationManager.IMPORTANCE_HIGH);
         channel.setGroup("something");
         NotificationRecord r = generateNotificationRecord(channel);
-        assertTrue(mService.isBlocked(r, mUsageStats));
-        verify(mUsageStats, times(1)).registerBlocked(eq(r));
+        assertTrue(mService.isRecordBlockedLocked(r));
     }
 
     @Test
@@ -1282,6 +1284,40 @@
     }
 
     @Test
+    public void testBlockedNotifications_blockedByUser() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_HIGH);
+        NotificationRecord r = generateNotificationRecord(channel);
+        mService.addEnqueuedNotification(r);
+
+        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(IMPORTANCE_NONE);
+
+        NotificationManagerService.PostNotificationRunnable runnable =
+                mService.new PostNotificationRunnable(r.getKey());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+    }
+
+    @Test
+    public void testEnqueueNotification_appBlocked() throws Exception {
+        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testEnqueueNotification_appBlocked", 0,
+                generateNotificationRecord(null).getNotification(), 0);
+        waitForIdle();
+        verify(mWorkerHandler, never()).post(
+                any(NotificationManagerService.EnqueueNotificationRunnable.class));
+    }
+
+    @Test
     public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testEnqueueNotificationWithTag_PopulatesGetActiveNotifications", 0,
@@ -3672,6 +3708,8 @@
 
         assertEquals(IMPORTANCE_LOW,
                 mService.getNotificationRecord(sbn.getKey()).getImportance());
+        assertEquals(IMPORTANCE_UNSPECIFIED, mBinderService.getPackageImportance(
+                sbn.getPackageName()));
 
         nb = new Notification.Builder(mContext)
                 .setContentTitle("foo")
@@ -8307,4 +8345,15 @@
     public void testMigrationDisabledByDefault() {
         assertThat(mService.mEnableAppSettingMigration).isFalse();
     }
+
+    @Test
+    public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_NONE);
+
+        assertThat(mBinderService.getNotificationChannelsBypassingDnd(PKG, mUid).getList())
+                .isEmpty();
+        verify(mPermissionHelper, never()).hasPermission(anyInt());
+        verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 36d6945..016b579 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -21,6 +21,7 @@
 import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.UserHandle.USER_SYSTEM;
@@ -76,6 +77,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
@@ -86,6 +88,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -121,6 +124,7 @@
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -159,6 +163,8 @@
     @Mock
     private PackageManager mPackageManagerClient;
     @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+    @Mock
     private WindowManagerInternal mWindowManagerInternal;
     @Mock
     private PermissionHelper mPermissionHelper;
@@ -279,6 +285,8 @@
         LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.addService(ActivityManagerInternal.class, mAmi);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
         mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
@@ -612,4 +620,62 @@
         assertEquals(PKG, captor.getValue().getPackage());
         assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
     }
+
+    @Test
+    public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
+        mService.setPreferencesHelper(mPreferencesHelper);
+
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+        assertThat(mBinderService.getNotificationChannelsBypassingDnd(PKG, mUid).getList())
+                .isEmpty();
+        verify(mPreferencesHelper, never()).getImportance(anyString(), anyInt());
+        verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid);
+    }
+
+    @Test
+    public void testBlockedNotifications_blockedByUser() throws Exception {
+        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_HIGH);
+        NotificationRecord r = generateNotificationRecord(channel);
+        mService.addEnqueuedNotification(r);
+
+        when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
+
+        NotificationManagerService.PostNotificationRunnable runnable =
+                mService.new PostNotificationRunnable(r.getKey());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+    }
+
+    @Test
+    public void testEnqueueNotification_appBlocked() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testEnqueueNotification_appBlocked", 0,
+                generateNotificationRecord(null).getNotification(), 0);
+        waitForIdle();
+        verify(mWorkerHandler, never()).post(
+                any(NotificationManagerService.EnqueueNotificationRunnable.class));
+    }
+
+    @Test
+    public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception {
+        final NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
+                PKG_N_MR1, ActivityManager.getCurrentUser(), PKG_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID);
+        defaultChannel.setImportance(IMPORTANCE_NONE);
+
+        mBinderService.updateNotificationChannelForPackage(PKG_N_MR1, mUid, defaultChannel);
+
+        verify(mPermissionHelper).setNotificationPermission(
+                PKG_N_MR1, ActivityManager.getCurrentUser(), false, true);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index f72d39a..55b12dd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -62,6 +62,7 @@
 import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -91,7 +92,8 @@
 
         Method[] allMethods = PermissionHelper.class.getDeclaredMethods();
         for (Method method : allMethods) {
-            if (Modifier.isPublic(method.getModifiers())) {
+            if (Modifier.isPublic(method.getModifiers()) &&
+                    !Objects.equals("isMigrationEnabled", method.getName())) {
                 Parameter[] params = method.getParameters();
                 List<Object> args = Lists.newArrayListWithCapacity(params.length);
                 for (int i = 0; i < params.length; i++) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index ea5de0c..d90f91a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -153,7 +153,7 @@
             Uri.parse("content://" + TEST_AUTHORITY
                     + "/internal/audio/media/10?title=Test&canonical=1");
 
-    @Mock NotificationUsageStats mUsageStats;
+    @Mock PermissionHelper mPermissionHelper;
     @Mock RankingHandler mHandler;
     @Mock PackageManager mPm;
     IContentProvider mTestIContentProvider;
@@ -269,8 +269,8 @@
 
         mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
 
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
         resetZenModeHelper();
 
         mAudioAttributes = new AudioAttributes.Builder()
@@ -1393,16 +1393,6 @@
         assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
                 user).getList().size());
 
-        // block notifications from this app
-        mHelper.setEnabled(PKG_N_MR1, user, false);
-        assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
-                user).getList().size());
-
-        // re-enable notifications from this app
-        mHelper.setEnabled(PKG_N_MR1, user, true);
-        assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
-                user).getList().size());
-
         // setBypassDnd false for some channels
         channel1.setBypassDnd(false);
         channel2.setBypassDnd(false);
@@ -1416,78 +1406,7 @@
     }
 
     @Test
-    public void testGetAppsBypassingDndCount_noAppsBypassing() throws Exception {
-        assertEquals(0, mHelper.getAppsBypassingDndCount(USER.getIdentifier()));
-    }
-
-    @Test
-    public void testGetAppsBypassingDndCount_noAppsForUserIdBypassing() throws Exception {
-        int user = 9;
-        NotificationChannel channel = new NotificationChannel("id", "name",
-                NotificationManager.IMPORTANCE_MAX);
-        channel.setBypassDnd(true);
-        mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
-
-        assertEquals(0, mHelper.getAppsBypassingDndCount(user));
-    }
-
-    @Test
-    public void testGetAppsBypassingDndCount_oneChannelBypassing_groupBlocked() {
-        int user = USER.getIdentifier();
-        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
-        NotificationChannel channel1 = new NotificationChannel("id1", "name1",
-                NotificationManager.IMPORTANCE_MAX);
-        channel1.setBypassDnd(true);
-        channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg,  /* fromTargetApp */ true);
-        mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
-
-        assertEquals(1, mHelper.getAppsBypassingDndCount(user));
-
-        // disable group
-        ncg.setBlocked(true);
-        mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg,  /* fromTargetApp */ false);
-        assertEquals(0, mHelper.getAppsBypassingDndCount(user));
-    }
-
-    @Test
-    public void testGetAppsBypassingDndCount_oneAppBypassing() {
-        int user = USER.getIdentifier();
-        NotificationChannel channel1 = new NotificationChannel("id1", "name1",
-                NotificationManager.IMPORTANCE_MAX);
-        NotificationChannel channel2 = new NotificationChannel("id2", "name2",
-                NotificationManager.IMPORTANCE_MAX);
-        NotificationChannel channel3 = new NotificationChannel("id3", "name3",
-                NotificationManager.IMPORTANCE_MAX);
-        channel1.setBypassDnd(true);
-        channel2.setBypassDnd(true);
-        channel3.setBypassDnd(true);
-        // has DND access, so can set bypassDnd attribute
-        mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
-        mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
-        mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
-        assertEquals(1, mHelper.getAppsBypassingDndCount(user));
-
-        // block notifications from this app
-        mHelper.setEnabled(PKG_N_MR1, user, false);
-        assertEquals(0, mHelper.getAppsBypassingDndCount(user)); // no apps can bypass dnd
-
-        // re-enable notifications from this app
-        mHelper.setEnabled(PKG_N_MR1, user, true);
-        assertEquals(1, mHelper.getAppsBypassingDndCount(user));
-
-        // setBypassDnd false for some channels
-        channel1.setBypassDnd(false);
-        channel2.setBypassDnd(false);
-        assertEquals(1, mHelper.getAppsBypassingDndCount(user));
-
-        // setBypassDnd false for rest of the channels
-        channel3.setBypassDnd(false);
-        assertEquals(0, mHelper.getAppsBypassingDndCount(user));
-    }
-
-    @Test
-    public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
+    public void testCreateAndDeleteCanChannelsBypassDnd_localSettings() throws Exception {
         int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
 
         // create notification channel that can't bypass dnd
@@ -1501,6 +1420,70 @@
 
         // create notification channel that can bypass dnd
         // expected result: areChannelsBypassingDnd = true
+        assertTrue(mHelper.getImportance(PKG_N_MR1, uid) != IMPORTANCE_NONE);
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
+        assertTrue(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        // delete channels
+        mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId());
+        assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
+        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId());
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testCreateAndUpdateChannelsBypassingDnd_permissionHelper() {
+        int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+        when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
+
+        // create notification channel that can't bypass dnd
+        // expected result: areChannelsBypassingDnd = false
+        // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
+        NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        // Recreate a channel & now the app has dnd access granted and can set the bypass dnd field
+        NotificationChannel update = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+        update.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, uid, update, true, true);
+
+        assertTrue(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testCreateAndDeleteCanChannelsBypassDnd_permissionHelper() throws Exception {
+        int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+        when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
+
+        // create notification channel that can't bypass dnd
+        // expected result: areChannelsBypassingDnd = false
+        // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
+        NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+        resetZenModeHelper();
+
+        // create notification channel that can bypass dnd, using local app level settings
+        // expected result: areChannelsBypassingDnd = true
         NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
         channel2.setBypassDnd(true);
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
@@ -1521,6 +1504,79 @@
     }
 
     @Test
+    public void testBlockedGroupDoesNotBypassDnd() throws Exception {
+        int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+
+        // start in a 'allowed to bypass dnd state'
+        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
+                mAppOpsManager, mStatsEventBuilderFactory);
+
+
+        // create notification channel that can bypass dnd, but app is blocked
+        // expected result: areChannelsBypassingDnd = false
+        NotificationChannelGroup group = new NotificationChannelGroup("group", "group");
+        group.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, uid, group, false);
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setGroup("group");
+        channel2.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testBlockedAppsDoNotBypassDnd_localSettings() throws Exception {
+        int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+
+        // start in a 'allowed to bypass dnd state'
+        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
+                mAppOpsManager, mStatsEventBuilderFactory);
+
+        mHelper.setImportance(PKG_N_MR1, uid, IMPORTANCE_NONE);
+        // create notification channel that can bypass dnd, but app is blocked
+        // expected result: areChannelsBypassingDnd = false
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testBlockedAppsDoNotBypassDnd_permissionHelper() throws Exception {
+        int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+        when(mPermissionHelper.hasPermission(uid)).thenReturn(false);
+        // start in a 'allowed to bypass dnd state'
+        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
+                mAppOpsManager, mStatsEventBuilderFactory);
+
+        // create notification channel that can bypass dnd, but app is blocked
+        // expected result: areChannelsBypassingDnd = false
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
     public void testUpdateCanChannelsBypassDnd() throws Exception {
         int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
 
@@ -1557,7 +1613,8 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         assertFalse(mHelper.areChannelsBypassingDnd());
         verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
@@ -1569,7 +1626,8 @@
         // start notification policy off with mAreChannelsBypassingDnd = false
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         assertFalse(mHelper.areChannelsBypassingDnd());
         verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
@@ -2355,7 +2413,8 @@
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
                 + "</package>\n"
                 + "</ranking>\n";
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadByteArrayXml(preQXml.getBytes(), true, UserHandle.USER_SYSTEM);
 
@@ -2368,7 +2427,8 @@
         mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -2465,7 +2525,8 @@
         mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -2477,7 +2538,8 @@
         mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -2490,7 +2552,8 @@
         mHelper.revokeNotificationDelegate(PKG_O, UID_O);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -2503,7 +2566,8 @@
         mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -2522,7 +2586,8 @@
         mHelper.revokeNotificationDelegate(PKG_O, UID_O);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -2541,7 +2606,8 @@
         assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -2596,7 +2662,8 @@
                 mHelper.getAppLockedFields(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -2633,7 +2700,8 @@
                 mHelper.getAppLockedFields(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
@@ -3235,7 +3303,8 @@
 
     @Test
     public void testPlaceholderConversationId_shortcutRequired() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
 
         final String xml = "<ranking version=\"1\">\n"
@@ -3254,7 +3323,8 @@
 
     @Test
     public void testNormalConversationId_shortcutRequired() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
 
         final String xml = "<ranking version=\"1\">\n"
@@ -3273,7 +3343,8 @@
 
     @Test
     public void testNoConversationId_shortcutRequired() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
 
         final String xml = "<ranking version=\"1\">\n"
@@ -3292,7 +3363,8 @@
 
     @Test
     public void testDeleted_noTime() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
 
         final String xml = "<ranking version=\"1\">\n"
@@ -3311,7 +3383,8 @@
 
     @Test
     public void testDeleted_twice() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
 
         mHelper.createNotificationChannel(
@@ -3322,7 +3395,8 @@
 
     @Test
     public void testDeleted_recentTime() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
 
         mHelper.createNotificationChannel(
@@ -3339,7 +3413,8 @@
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
                 null);
         parser.nextTag();
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
         mHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
 
@@ -3350,7 +3425,8 @@
 
     @Test
     public void testUnDelete_time() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
 
         mHelper.createNotificationChannel(
@@ -3369,7 +3445,8 @@
 
     @Test
     public void testDeleted_longTime() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
 
         long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30);
@@ -3995,6 +4072,28 @@
         assertTrue((channelA.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0);
         assertTrue((channelB.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0);
         assertTrue((channelC.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0);
+    }
 
+    @Test
+    public void testDefaultChannelUpdatesApp_preMigrationToPermissions() throws Exception {
+        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+                UID_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false);
+        defaultChannel.setImportance(IMPORTANCE_NONE);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
+    }
+
+    @Test
+    public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception {
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+                UID_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID, false);
+        defaultChannel.setImportance(IMPORTANCE_NONE);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+        assertEquals(IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 8edd111..59d9a35 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -48,6 +48,7 @@
 import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index d7702bb..1435b6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -77,6 +77,7 @@
 import static com.android.server.wm.ActivityRecord.State.STARTED;
 import static com.android.server.wm.ActivityRecord.State.STOPPED;
 import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
@@ -1751,6 +1752,11 @@
                 anyInt() /* orientation */, anyInt() /* lastRotation */);
         // Set to visible so the activity can freeze the screen.
         activity.setVisibility(true);
+        // Update the display policy to make the screen fully turned on so the freeze is allowed
+        display.getDisplayPolicy().screenTurnedOn(null);
+        display.getDisplayPolicy().finishKeyguardDrawn();
+        display.getDisplayPolicy().finishWindowsDrawn();
+        display.getDisplayPolicy().finishScreenTurningOn();
 
         display.rotateInDifferentOrientationIfNeeded(activity);
         display.setFixedRotationLaunchingAppUnchecked(activity);
@@ -3026,6 +3032,10 @@
 
         // Because the app is waiting for transition, it should not hide the surface.
         assertTrue(app.mActivityRecord.isSurfaceShowing());
+
+        // Ensure onAnimationFinished will callback when the closing animation is finished.
+        verify(app.mActivityRecord).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+                eq(null));
     }
 
     private void assertHasStartingWindow(ActivityRecord atoken) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 4c3c318..b0fb812 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -839,6 +839,30 @@
                 wpc.getConfiguration().getLocales());
     }
 
+    @Test
+    public void testPackageConfigUpdate_commitConfig_configSuccessfullyApplied() {
+        Configuration config = mAtm.getGlobalConfiguration();
+        config.setLocales(LocaleList.forLanguageTags("en-XC"));
+        mAtm.updateGlobalConfigurationLocked(config, true, true,
+                DEFAULT_USER_ID);
+        WindowProcessController wpc = createWindowProcessController(
+                DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+        mAtm.mProcessMap.put(Binder.getCallingPid(), wpc);
+        mAtm.mInternal.onProcessAdded(wpc);
+
+        ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+                mAtm.mInternal.createPackageConfigurationUpdater(DEFAULT_PACKAGE_NAME,
+                        DEFAULT_USER_ID);
+        // committing new configuration returns true;
+        assertTrue(packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+                .commit());
+        // applying the same configuration returns false.
+        assertFalse(packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+                .commit());
+        assertTrue(packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList())
+                .setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit());
+    }
+
     private WindowProcessController createWindowProcessController(String packageName,
             int userId) {
         WindowProcessListener mMockListener = Mockito.mock(WindowProcessListener.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 3982a83..1d2baab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -31,6 +31,7 @@
 import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
 
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -75,13 +76,12 @@
         final TestContextWrapper context = new TestContextWrapper(
                 mDisplayPolicy.getContext(), mDisplayPolicy.getCurrentUserResources());
         final TestableResources resources = context.getResourceMocker();
-        resources.addOverride(R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT);
-        resources.addOverride(R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT);
         resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
         resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
         resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
         resources.addOverride(R.dimen.navigation_bar_frame_height_landscape, NAV_BAR_HEIGHT);
         resources.addOverride(R.dimen.navigation_bar_frame_height, NAV_BAR_HEIGHT);
+        doReturn(STATUS_BAR_HEIGHT).when(mDisplayPolicy).getStatusBarHeightForRotation(anyInt());
         doReturn(resources.getResources()).when(mDisplayPolicy).getCurrentUserResources();
         doReturn(true).when(mDisplayPolicy).hasNavigationBar();
         doReturn(true).when(mDisplayPolicy).hasStatusBar();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index e04cff4..73e571a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -83,6 +83,8 @@
 
 import androidx.test.filters.MediumTest;
 
+import com.android.internal.policy.SystemBarUtils;
+
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
@@ -174,7 +176,9 @@
 
         // The activity should be able to accept negative x position [-150, 100 - 150, 600].
         final int dx = bounds.left + bounds.width() / 2;
-        mTask.setBounds(bounds.left - dx, bounds.top, bounds.right - dx, bounds.bottom);
+        final int dy = bounds.top + bounds.height() / 2;
+        mTask.setBounds(bounds.left - dx, bounds.top - dy, bounds.right - dx, bounds.bottom - dy);
+        // expected:<Rect(-150, 100 - 150, 600)> but was:<Rect(-150, 0 - 150, 500)>
         assertEquals(mTask.getBounds(), mActivity.getBounds());
 
         final int density = mActivity.getConfiguration().densityDpi;
@@ -1119,6 +1123,98 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
+        // In this test, the activity's orientation isn't fixed to portrait, therefore the override
+        // isn't applied.
+
+        setUpDisplaySizeWithApp(1000, 1200);
+
+        // Create a size compat activity on the same task.
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        // The per-package override should have no effect
+        assertEquals(1200, activity.getBounds().height());
+        assertEquals(1000, activity.getBounds().width());
+
+        // After changing the orientation to portrait the override should be applied.
+        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        activity.clearSizeCompatMode();
+
+        // The per-package override forces the activity into a 3:2 aspect ratio
+        assertEquals(1200, activity.getBounds().height());
+        assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+                activity.getBounds().width(), 0.5);
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
+        // In this test, the activity's orientation isn't fixed to portrait, therefore the override
+        // isn't applied.
+
+        setUpDisplaySizeWithApp(1000, 1200);
+
+        // Create a size compat activity on the same task.
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        // The per-package override should have no effect
+        assertEquals(1200, activity.getBounds().height());
+        assertEquals(1000, activity.getBounds().width());
+
+        // After changing the orientation to portrait the override should be applied.
+        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        activity.clearSizeCompatMode();
+
+        // The per-package override forces the activity into a 3:2 aspect ratio
+        assertEquals(1200, activity.getBounds().height());
+        assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+                activity.getBounds().width(), 0.5);
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
+        setUpDisplaySizeWithApp(1000, 1200);
+
+        // Create a size compat activity on the same task.
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        // The per-package override forces the activity into a 3:2 aspect ratio
+        assertEquals(1200, activity.getBounds().height());
+        assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+                activity.getBounds().width(), 0.5);
+
+        // After changing the orientation to landscape the override shouldn't be applied.
+        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+        activity.clearSizeCompatMode();
+
+        // The per-package override should have no effect
+        assertEquals(1200, activity.getBounds().height());
+        assertEquals(1000, activity.getBounds().width());
+    }
+
+    @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioWithoutGlobalOverride() {
         // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
@@ -1850,7 +1946,7 @@
                 // At launch.
                 /* fixedOrientationLetterbox */ new Rect(0, 0, 700, 1400),
                 // After 90 degree rotation.
-                /* sizeCompatUnscaled */ new Rect(0, 0, 700, 1400),
+                /* sizeCompatUnscaled */ new Rect(0, 700, 700, 2100),
                 // After the display is resized to (700, 1400).
                 /* sizeCompatScaled */ new Rect(0, 0, 350, 700));
     }
@@ -1863,7 +1959,7 @@
                 // At launch.
                 /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
                 // After 90 degree rotation.
-                /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
+                /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
                 // After the display is resized to (700, 1400).
                 /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
     }
@@ -1878,7 +1974,7 @@
                 // At launch.
                 /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
                 // After 90 degree rotation.
-                /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
+                /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
                 // After the display is resized to (700, 1400).
                 /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
 
@@ -1888,7 +1984,7 @@
                 // At launch.
                 /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
                 // After 90 degree rotation.
-                /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
+                /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
                 // After the display is resized to (700, 1400).
                 /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
     }
@@ -1901,7 +1997,7 @@
                 // At launch.
                 /* fixedOrientationLetterbox */ new Rect(2100, 0, 2800, 1400),
                 // After 90 degree rotation.
-                /* sizeCompatUnscaled */ new Rect(700, 0, 1400, 1400),
+                /* sizeCompatUnscaled */ new Rect(700, 700, 1400, 2100),
                 // After the display is resized to (700, 1400).
                 /* sizeCompatScaled */ new Rect(1050, 0, 1400, 700));
     }
@@ -2093,7 +2189,7 @@
 
         assertTrue(mActivity.inSizeCompatMode());
         // Activity is in size compat mode but not scaled.
-        assertEquals(new Rect(0, 0, 1400, 700), mActivity.getBounds());
+        assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds());
     }
 
     private static WindowState addWindowToActivity(ActivityRecord activity) {
@@ -2126,8 +2222,7 @@
                 displayContent.mWmService, mock(Session.class), new TestIWindow(), attrs, token);
         token.addWindow(statusBar);
         statusBar.setRequestedSize(displayContent.mBaseDisplayWidth,
-                displayContent.getDisplayUiContext().getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.status_bar_height));
+                SystemBarUtils.getStatusBarHeight(displayContent.getDisplayUiContext()));
 
         displayPolicy.addWindowLw(statusBar, attrs);
         displayPolicy.layoutWindowLw(statusBar, null, displayContent.mDisplayFrames);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 7bac3e7..420ea8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -62,7 +62,7 @@
     public void testTrivialSyncCallback() {
         TestWindowContainer mockWC = new TestWindowContainer(mWm, false /* waiter */);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
@@ -90,7 +90,7 @@
     public void testWaitingSyncCallback() {
         TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
@@ -114,7 +114,7 @@
     public void testInvisibleSyncCallback() {
         TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
@@ -142,7 +142,7 @@
         parentWC.addChild(childWC, POSITION_TOP);
         parentWC.addChild(childWC2, POSITION_TOP);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
@@ -175,7 +175,7 @@
         TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */);
         parentWC.addChild(childWC, POSITION_TOP);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
@@ -206,7 +206,7 @@
         parentWC.addChild(topChildWC, POSITION_TOP);
         parentWC.addChild(botChildWC, POSITION_BOTTOM);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
@@ -238,7 +238,7 @@
         parentWC.addChild(topChildWC, POSITION_TOP);
         parentWC.addChild(botChildWC, POSITION_BOTTOM);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
@@ -273,7 +273,7 @@
         parentWC.addChild(topChildWC, POSITION_TOP);
         nonMemberParentWC.addChild(botChildWC, POSITION_BOTTOM);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
@@ -312,7 +312,7 @@
         parentWC.addChild(topChildWC, POSITION_TOP);
         parentWC.addChild(botChildWC, POSITION_BOTTOM);
 
-        BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
 
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 16d75ca..9001578 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -128,10 +128,6 @@
     }
 
     @Override
-    public void setKeyguardCandidateLw(WindowState win) {
-    }
-
-    @Override
     public Animation createHiddenByKeyguardExit(boolean onWallpaper,
             boolean goingToNotificationShade, boolean subtleAnimation) {
         return null;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index a1c24c2..4e77fa7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -21,7 +21,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -44,6 +43,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
 import android.window.ITransitionPlayer;
 import android.window.TransitionInfo;
@@ -53,9 +53,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Build/Install/Run:
- *  atest WmTests:TransitionRecordTests
+ *  atest WmTests:TransitionTests
  */
 @SmallTest
 @Presubmit
@@ -64,13 +67,13 @@
 
     private Transition createTestTransition(int transitType) {
         TransitionController controller = mock(TransitionController.class);
-        BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
-        return new Transition(transitType, 0 /* flags */, controller, sync);
+        final BLASTSyncEngine sync = createTestBLASTSyncEngine();
+        return new Transition(transitType, 0 /* flags */, 0 /* timeoutMs */, controller, sync);
     }
 
     @Test
     public void testCreateInfo_NewTask() {
-        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
         ArraySet<WindowContainer> participants = transition.mParticipants;
 
@@ -88,7 +91,7 @@
         closing.mVisibleRequested = false;
         opening.mVisibleRequested = true;
 
-        int transit = TRANSIT_OLD_TASK_OPEN;
+        final int transit = transition.mType;
         int flags = 0;
 
         // Check basic both tasks participating
@@ -127,7 +130,7 @@
 
     @Test
     public void testCreateInfo_NestedTasks() {
-        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
         ArraySet<WindowContainer> participants = transition.mParticipants;
 
@@ -152,7 +155,7 @@
         opening.mVisibleRequested = true;
         opening2.mVisibleRequested = true;
 
-        int transit = TRANSIT_OLD_TASK_OPEN;
+        final int transit = transition.mType;
         int flags = 0;
 
         // Check full promotion from leaf
@@ -177,7 +180,7 @@
 
     @Test
     public void testCreateInfo_DisplayArea() {
-        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
         ArraySet<WindowContainer> participants = transition.mParticipants;
         final Task showTask = createTask(mDisplayContent);
@@ -199,7 +202,7 @@
         showing.mVisibleRequested = true;
         showing2.mVisibleRequested = true;
 
-        int transit = TRANSIT_OLD_TASK_OPEN;
+        final int transit = transition.mType;
         int flags = 0;
 
         // Check promotion to DisplayArea
@@ -228,7 +231,7 @@
 
     @Test
     public void testCreateInfo_existenceChange() {
-        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
 
         final Task openTask = createTask(mDisplayContent);
         final ActivityRecord opening = createActivityRecord(openTask);
@@ -258,7 +261,7 @@
 
     @Test
     public void testCreateInfo_ordering() {
-        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
         // pick some number with a high enough chance of being out-of-order when added to set.
         final int taskCount = 6;
 
@@ -294,7 +297,7 @@
 
     @Test
     public void testCreateInfo_wallpaper() {
-        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
         // pick some number with a high enough chance of being out-of-order when added to set.
         final int taskCount = 4;
         final int showWallpaperTask = 2;
@@ -345,7 +348,7 @@
 
     @Test
     public void testTargets_noIntermediatesToWallpaper() {
-        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
 
         final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
@@ -419,7 +422,7 @@
         openInOpen.mVisibleRequested = true;
         openInChange.mVisibleRequested = true;
 
-        int transit = TRANSIT_OLD_TASK_OPEN;
+        final int transit = transition.mType;
         int flags = 0;
 
         // Check full promotion from leaf
@@ -450,6 +453,22 @@
     }
 
     @Test
+    public void testTimeout() {
+        final TransitionController controller = new TransitionController(mAtm,
+                mock(TaskSnapshotController.class));
+        final BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
+        final CountDownLatch latch = new CountDownLatch(1);
+        // When the timeout is reached, it will finish the sync-group and notify transaction ready.
+        new Transition(TRANSIT_OPEN, 0 /* flags */, 10 /* timeoutMs */, controller, sync) {
+            @Override
+            public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
+                latch.countDown();
+            }
+        };
+        assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS)));
+    }
+
+    @Test
     public void testIntermediateVisibility() {
         final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
         final TransitionController controller = new TransitionController(mAtm, snapshotController);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index a8a9188..ca2b4ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -78,6 +78,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -563,7 +564,7 @@
         final WindowState child = createWindow(w, TYPE_APPLICATION_PANEL, "child");
 
         assertTrue(w.hasCompatScale());
-        assertFalse(child.hasCompatScale());
+        assertTrue(child.hasCompatScale());
 
         makeWindowVisible(w, child);
         w.setRequestedSize(100, 200);
@@ -574,21 +575,26 @@
         w.mAttrs.gravity = Gravity.TOP | Gravity.LEFT;
         child.mAttrs.gravity = Gravity.CENTER;
         DisplayContentTests.performLayout(mDisplayContent);
+        final Rect parentFrame = w.getFrame();
+        final Rect childFrame = child.getFrame();
 
         // Frame on screen = 200x400 (200, 200 - 400, 600). Compat frame on client = 100x200.
         final Rect unscaledCompatFrame = new Rect(w.getWindowFrames().mCompatFrame);
         unscaledCompatFrame.scale(overrideScale);
-        final Rect parentFrame = w.getFrame();
-        assertEquals(w.getWindowFrames().mFrame, unscaledCompatFrame);
+        assertEquals(parentFrame, unscaledCompatFrame);
 
-        final Rect childFrame = child.getFrame();
-        assertEquals(childFrame, child.getWindowFrames().mCompatFrame);
-        // Child frame = 50x100 (225, 250 - 275, 350) according to Gravity.CENTER.
-        final int childX = parentFrame.left + child.mRequestedWidth / 2;
-        final int childY = parentFrame.top + child.mRequestedHeight / 2;
-        final Rect expectedChildFrame = new Rect(childX, childY, childX + child.mRequestedWidth,
-                childY + child.mRequestedHeight);
-        assertEquals(expectedChildFrame, childFrame);
+        // Frame on screen = 100x200 (250, 300 - 350, 500). Compat frame on client = 50x100.
+        unscaledCompatFrame.set(child.getWindowFrames().mCompatFrame);
+        unscaledCompatFrame.scale(overrideScale);
+        assertEquals(childFrame, unscaledCompatFrame);
+
+        // The position of child is relative to parent. So the local coordinates should be scaled.
+        final Point expectedChildPos = new Point(
+                (int) ((childFrame.left - parentFrame.left) / overrideScale),
+                (int) ((childFrame.top - parentFrame.top) / overrideScale));
+        final Point childPos = new Point();
+        child.transformFrameToSurfacePosition(childFrame.left, childFrame.top, childPos);
+        assertEquals(expectedChildPos, childPos);
 
         // Surface should apply the scale.
         w.prepareSurfaces();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index b2d4eea..ac61bb1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -789,6 +789,15 @@
         };
     }
 
+    BLASTSyncEngine createTestBLASTSyncEngine() {
+        return new BLASTSyncEngine(mWm) {
+            @Override
+            void scheduleTimeout(SyncGroup s, long timeoutMs) {
+                // Disable timeout.
+            }
+        };
+    }
+
     /**
      * Avoids rotating screen disturbed by some conditions. It is usually used for the default
      * display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions).
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 22ea3d5..6c3a1f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -24,6 +24,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
@@ -39,10 +40,12 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -401,6 +404,30 @@
     }
 
     @Test
+    public void testAssignWindowLayers_ForImeOnAppWithRecentsAnimating() {
+        final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION,
+                mAppWindow.mActivityRecord, "imeAppTarget");
+        mDisplayContent.setImeInputTarget(imeAppTarget);
+        mDisplayContent.setImeLayeringTarget(imeAppTarget);
+        mDisplayContent.updateImeParent();
+
+        // Simulate the ime layering target task is animating with recents animation.
+        final Task imeAppTargetTask = imeAppTarget.getTask();
+        final SurfaceAnimator imeTargetTaskAnimator = imeAppTargetTask.mSurfaceAnimator;
+        spyOn(imeTargetTaskAnimator);
+        doReturn(ANIMATION_TYPE_RECENTS).when(imeTargetTaskAnimator).getAnimationType();
+        doReturn(true).when(imeTargetTaskAnimator).isAnimating();
+
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        // Ime should on top of the application window when in recents animation and keep
+        // attached on app.
+        assertTrue(mDisplayContent.shouldImeAttachedToApp());
+        assertWindowHigher(mImeWindow, imeAppTarget);
+    }
+
+
+    @Test
     public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() {
         // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
         // then we can drop all negative layering on the windowing side.
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 339249b..ad042dd 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usage;
 
+import android.annotation.Nullable;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
@@ -788,7 +789,7 @@
     public interface StatCombiner<T> {
 
         /**
-         * Implementations should extract interesting from <code>stats</code> and add it
+         * Implementations should extract interesting information from <code>stats</code> and add it
          * to the <code>accumulatedResult</code> list.
          *
          * If the <code>stats</code> object is mutable, <code>mutable</code> will be true,
@@ -805,29 +806,24 @@
     /**
      * Find all {@link IntervalStats} for the given range and interval type.
      */
+    @Nullable
     public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime,
             StatCombiner<T> combiner) {
+        // mIntervalDirs is final. Accessing its size without holding the lock should be fine.
+        if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
+            throw new IllegalArgumentException("Bad interval type " + intervalType);
+        }
+
+        if (endTime <= beginTime) {
+            if (DEBUG) {
+                Slog.d(TAG, "endTime(" + endTime + ") <= beginTime(" + beginTime + ")");
+            }
+            return null;
+        }
+
         synchronized (mLock) {
-            if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
-                throw new IllegalArgumentException("Bad interval type " + intervalType);
-            }
-
             final TimeSparseArray<AtomicFile> intervalStats = mSortedStatFiles[intervalType];
 
-            if (endTime <= beginTime) {
-                if (DEBUG) {
-                    Slog.d(TAG, "endTime(" + endTime + ") <= beginTime(" + beginTime + ")");
-                }
-                return null;
-            }
-
-            int startIndex = intervalStats.closestIndexOnOrBefore(beginTime);
-            if (startIndex < 0) {
-                // All the stats available have timestamps after beginTime, which means they all
-                // match.
-                startIndex = 0;
-            }
-
             int endIndex = intervalStats.closestIndexOnOrBefore(endTime);
             if (endIndex < 0) {
                 // All the stats start after this range ends, so nothing matches.
@@ -849,6 +845,13 @@
                 }
             }
 
+            int startIndex = intervalStats.closestIndexOnOrBefore(beginTime);
+            if (startIndex < 0) {
+                // All the stats available have timestamps after beginTime, which means they all
+                // match.
+                startIndex = 0;
+            }
+
             final ArrayList<T> results = new ArrayList<>();
             for (int i = startIndex; i <= endIndex; i++) {
                 final AtomicFile f = intervalStats.valueAt(i);
@@ -985,7 +988,6 @@
         }
     }
 
-
     private static long parseBeginTime(AtomicFile file) throws IOException {
         return parseBeginTime(file.getBaseFile());
     }
@@ -1233,7 +1235,6 @@
         }
     }
 
-
     /* Backup/Restore Code */
     byte[] getBackupPayload(String key) {
         return getBackupPayload(key, BACKUP_VERSION);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 5aa1e27..339a5bd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -197,7 +197,7 @@
         testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun runPresubmitAssertion() {
         flickerRule.checkPresubmitAssertions()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index be919cd..2f5a389 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -88,12 +87,12 @@
     }
 
     /** {@inheritDoc} */
-    @Postsubmit
+    @Presubmit
     @Test
     override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
 
     /** {@inheritDoc} */
-    @Postsubmit
+    @Presubmit
     @Test
     override fun appWindowReplacesLauncherAsTopWindow() =
         super.appWindowReplacesLauncherAsTopWindow()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index cf10c53..ad0da5b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -203,7 +203,7 @@
      * Checks that the screen is locked at the start of the transition ([colorFadComponent])
      * layer is visible
      */
-    @Postsubmit
+    @Presubmit
     @Test
     fun screenLockedStart() {
         testSpec.assertLayersStart {
@@ -216,7 +216,7 @@
      * it cannot use the regular assertion (check over time), because on lock screen neither
      * the app not the launcher are visible, and there is no top visible window.
      */
-    @Postsubmit
+    @Presubmit
     @Test
     override fun appWindowReplacesLauncherAsTopWindow() {
         testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index fd8abc6..0bde8a0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -95,7 +95,7 @@
             }
         }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun runPresubmitAssertion() {
         flickerRule.checkPresubmitAssertions()
diff --git a/tests/HwAccelerationTest/Android.bp b/tests/HwAccelerationTest/Android.bp
index 7606322..e618ed1 100644
--- a/tests/HwAccelerationTest/Android.bp
+++ b/tests/HwAccelerationTest/Android.bp
@@ -25,7 +25,13 @@
 
 android_test {
     name: "HwAccelerationTest",
-    srcs: ["**/*.java"],
+    jni_libs: [
+        "libhwaccelerationtest_jni",
+    ],
+    srcs: [
+        "**/*.java",
+        "**/*.kt",
+    ],
     platform_apis: true,
     certificate: "platform",
 }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 04a55d6..269c575 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -436,6 +436,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".PenStylusActivity"
+                  android:label="Pen (BUGGED)/Draw"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="GLTextureViewActivity"
              android:label="TextureView/OpenGL"
              android:exported="true">
diff --git a/tests/HwAccelerationTest/jni/Android.bp b/tests/HwAccelerationTest/jni/Android.bp
new file mode 100644
index 0000000..8edddab
--- /dev/null
+++ b/tests/HwAccelerationTest/jni/Android.bp
@@ -0,0 +1,44 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test_library {
+
+    name: "libhwaccelerationtest_jni",
+
+    cflags: [
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+    ],
+
+    gtest: false,
+
+    srcs: [
+        "native-lib.cpp",
+    ],
+
+    shared_libs: [
+        "libnativehelper",
+        "libandroid",
+        "liblog",
+    ],
+
+    stl: "c++_static",
+
+    sdk_version: "current",
+
+}
diff --git a/tests/HwAccelerationTest/jni/native-lib.cpp b/tests/HwAccelerationTest/jni/native-lib.cpp
new file mode 100644
index 0000000..d6b1c49
--- /dev/null
+++ b/tests/HwAccelerationTest/jni/native-lib.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_jni.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#include <android/surface_control.h>
+#include <jni.h>
+
+struct MyWrapper {
+    MyWrapper(ANativeWindow* parent) {
+        surfaceControl = ASurfaceControl_createFromWindow(parent, "PenLayer");
+    }
+
+    ~MyWrapper() { ASurfaceControl_release(surfaceControl); }
+
+    void setBuffer(AHardwareBuffer* buffer) {
+        ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+        ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer);
+        ASurfaceTransaction_setVisibility(transaction, surfaceControl,
+                                          ASURFACE_TRANSACTION_VISIBILITY_SHOW);
+        ASurfaceTransaction_apply(transaction);
+        ASurfaceTransaction_delete(transaction);
+    }
+
+    ASurfaceControl* surfaceControl = nullptr;
+};
+
+extern "C" JNIEXPORT jlong JNICALL
+Java_com_android_test_hwui_FrontBufferedLayer_nCreate(JNIEnv* env, jobject jSurface) {
+    ANativeWindow* window = ANativeWindow_fromSurface(env, jSurface);
+    MyWrapper* wrapper = new MyWrapper(window);
+    ANativeWindow_release(window);
+    return reinterpret_cast<jlong>(wrapper);
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_test_hwui_FrontBufferedLayer_nDestroy(jlong ptr) {
+    MyWrapper* wrapper = reinterpret_cast<MyWrapper*>(ptr);
+    delete wrapper;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_test_hwui_FrontBufferedLayer_nUpdateBuffer(
+        JNIEnv* env, jlong ptr, jobject jbuffer) {
+    MyWrapper* wrapper = reinterpret_cast<MyWrapper*>(ptr);
+    AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, jbuffer);
+    wrapper->setBuffer(buffer);
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/res/layout/pen_stylus.xml b/tests/HwAccelerationTest/res/layout/pen_stylus.xml
new file mode 100644
index 0000000..37aafed
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/pen_stylus.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<com.android.test.hwui.FrontBufferedLayer
+    xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+/>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt
new file mode 100644
index 0000000..ebec22e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt
@@ -0,0 +1,132 @@
+/*
+ * 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.test.hwui
+
+import android.content.Context
+import android.graphics.BlendMode
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.util.AttributeSet
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.Surface
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+
+class FrontBufferedLayer : SurfaceView, SurfaceHolder.Callback {
+    var mRenderer: PenStylusActivity.SingleBufferedCanvas? = null
+
+    constructor(context: Context) : super(context)
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+    init {
+        holder.addCallback(this)
+        setZOrderOnTop(true)
+    }
+
+    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+        nDestroy(mNativePtr)
+        mNativePtr = nCreate(holder.surface)
+        mRenderer = PenStylusActivity.SingleBufferedCanvas(width, height)
+        clearOverlay()
+
+        if ((false)) {
+            val canvas = holder.lockCanvas()
+            canvas.drawColor(Color.LTGRAY)
+            holder.unlockCanvasAndPost(canvas)
+        }
+    }
+
+    override fun surfaceDestroyed(holder: SurfaceHolder) {
+        mRenderer = null
+        nDestroy(mNativePtr)
+        mNativePtr = 0
+    }
+
+    override fun surfaceCreated(holder: SurfaceHolder) {
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+        requestUnbufferedDispatch(InputDevice.SOURCE_CLASS_POINTER)
+        this.windowInsetsController?.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT)
+        this.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
+        this.windowInsetsController?.hide(WindowInsets.Type.statusBars())
+    }
+
+    private fun clearOverlay() {
+        mRenderer?.let {
+            it.update(null) {
+                drawColor(Color.WHITE, BlendMode.SRC)
+            }
+            nUpdateBuffer(mNativePtr, it.mHardwareBuffer)
+        }
+    }
+
+    private var prevX: Float = 0f
+    private var prevY: Float = 0f
+    private val paint = Paint().also {
+        it.color = Color.BLACK
+        it.strokeWidth = 10f
+        it.isAntiAlias = true
+    }
+
+    override fun onTouchEvent(event: MotionEvent): Boolean {
+        if (!event.isFromSource(InputDevice.SOURCE_STYLUS)) {
+            if (event.action == MotionEvent.ACTION_DOWN) {
+                clearOverlay()
+            }
+            return true
+        }
+        val action = event.actionMasked
+        if (action == MotionEvent.ACTION_DOWN ||
+                action == MotionEvent.ACTION_MOVE) {
+            mRenderer?.let {
+                val left = minOf(prevX, event.x).toInt() - 10
+                val top = minOf(prevY, event.y).toInt() - 10
+                val right = maxOf(prevX, event.x).toInt() + 10
+                val bottom = maxOf(prevY, event.y).toInt() + 10
+                it.update(Rect(left, top, right, bottom)) {
+                    if (action == MotionEvent.ACTION_MOVE) {
+                        drawLine(prevX, prevY, event.x, event.y, paint)
+                    }
+                    drawCircle(event.x, event.y, 5f, paint)
+                }
+                nUpdateBuffer(mNativePtr, it.mHardwareBuffer)
+            }
+            prevX = event.x
+            prevY = event.y
+        }
+        return true
+    }
+
+    private var mNativePtr: Long = 0
+
+    private external fun nCreate(surface: Surface): Long
+    private external fun nDestroy(ptr: Long)
+    private external fun nUpdateBuffer(ptr: Long, buffer: HardwareBuffer)
+
+    companion object {
+        init {
+            System.loadLibrary("hwaccelerationtest_jni")
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt
new file mode 100644
index 0000000..1445b1d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.test.hwui
+
+import android.app.Activity
+import android.os.Bundle
+import android.hardware.HardwareBuffer
+import android.graphics.Canvas
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.media.ImageReader
+import android.view.Surface
+import java.lang.IllegalArgumentException
+
+const val USAGE_HWC = 0x800L
+
+class PenStylusActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContentView(FrontBufferedLayer(this))
+    }
+
+    class SingleBufferedCanvas : AutoCloseable {
+        val mHardwareBuffer: HardwareBuffer
+        private var mCanvas: Canvas?
+        private val mImageReader: ImageReader
+        private val mSurface: Surface
+
+        constructor(width: Int, height: Int) {
+            mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1,
+                HardwareBuffer.USAGE_CPU_READ_RARELY or HardwareBuffer.USAGE_CPU_WRITE_RARELY or
+                        HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or USAGE_HWC)
+
+            mSurface = mImageReader.surface
+            mSurface.unlockCanvasAndPost(mSurface.lockCanvas(null))
+            val image = mImageReader.acquireNextImage()
+            mHardwareBuffer = image.hardwareBuffer!!
+            image.close()
+            mCanvas = mSurface.lockCanvas(null)
+        }
+
+        fun lockCanvas(rect: Rect?): Canvas {
+            if (mCanvas != null) {
+                unlockCanvas(mCanvas!!)
+            }
+            mCanvas = mSurface.lockCanvas(rect)
+            return mCanvas!!
+        }
+
+        fun unlockCanvas(canvas: Canvas) {
+            if (this.mCanvas !== canvas) throw IllegalArgumentException()
+            mSurface.unlockCanvasAndPost(canvas)
+            this.mCanvas = null
+            mImageReader.acquireNextImage().close()
+        }
+
+        inline fun update(area: Rect?, func: Canvas.() -> Unit) {
+            val canvas = lockCanvas(area)
+            func(canvas)
+            unlockCanvas(canvas)
+        }
+
+        override fun close() {
+            mHardwareBuffer.close()
+            mSurface.unlockCanvasAndPost(mCanvas)
+            mImageReader.close()
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 4512876..ffc8f47 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -58,6 +58,7 @@
 public class StagedRollbackTest {
     private static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT =
             "watchdog_trigger_failure_count";
+    private static final String REBOOTLESS_APEX_NAME = "test.apex.rebootless";
 
     /**
      * Adopts common shell permissions needed for rollback tests.
@@ -242,7 +243,7 @@
 
     @Test
     public void testRollbackRebootlessApex() throws Exception {
-        final String packageName = "test.apex.rebootless";
+        final String packageName = REBOOTLESS_APEX_NAME;
         assertThat(InstallUtils.getInstalledVersion(packageName)).isEqualTo(1);
 
         // install
@@ -268,6 +269,28 @@
     }
 
     @Test
+    public void testNativeWatchdogTriggersRebootlessApexRollback_Phase1_Install() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(REBOOTLESS_APEX_NAME)).isEqualTo(1);
+
+        TestApp apex2 = new TestApp("TestRebootlessApexV2", REBOOTLESS_APEX_NAME, 2,
+                /* isApex= */ true, "test.rebootless_apex_v2.apex");
+        Install.single(apex2).setEnableRollback(PackageManager.ROLLBACK_DATA_POLICY_RETAIN)
+                .commit();
+        Install.single(TestApp.A1).commit();
+        Install.single(TestApp.A2).setEnableRollback().commit();
+
+        RollbackUtils.waitForAvailableRollback(TestApp.A);
+        RollbackUtils.waitForAvailableRollback(REBOOTLESS_APEX_NAME);
+    }
+
+    @Test
+    public void testNativeWatchdogTriggersRebootlessApexRollback_Phase2_Verify() throws Exception {
+        // Check only rebootless apex is rolled back. Other rollbacks should remain unchanged.
+        assertThat(RollbackUtils.getCommittedRollback(REBOOTLESS_APEX_NAME)).isNotNull();
+        assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNotNull();
+    }
+
+    @Test
     public void hasMainlineModule() throws Exception {
         String pkgName = getModuleMetadataPackageName();
         boolean existed =  InstrumentationRegistry.getInstrumentation().getContext()
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 3576a78..1ab59a8 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -192,6 +192,18 @@
     }
 
     /**
+     * Tests only rebootless apex (if any) is rolled back when native crash happens
+     */
+    @Test
+    public void testNativeWatchdogTriggersRebootlessApexRollback() throws Exception {
+        pushTestApex("test.rebootless_apex_v1.apex");
+        runPhase("testNativeWatchdogTriggersRebootlessApexRollback_Phase1_Install");
+        crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
+        getDevice().waitForDeviceAvailable();
+        runPhase("testNativeWatchdogTriggersRebootlessApexRollback_Phase2_Verify");
+    }
+
+    /**
      * Tests that packages are monitored across multiple reboots.
      */
     @Test
@@ -253,4 +265,18 @@
             return false;
         }
     }
+
+    private void crashProcess(String processName, int numberOfCrashes) throws Exception {
+        String pid = "";
+        String lastPid = "invalid";
+        for (int i = 0; i < numberOfCrashes; ++i) {
+            // This condition makes sure before we kill the process, the process is running AND
+            // the last crash was finished.
+            while ("".equals(pid) || lastPid.equals(pid)) {
+                pid = getDevice().executeShellCommand("pidof " + processName);
+            }
+            getDevice().executeShellCommand("kill " + pid);
+            lastPid = pid;
+        }
+    }
 }
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index df444ba..7103944 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -16,6 +16,9 @@
 
 #include "Debug.h"
 
+#include <androidfw/TypeWrappers.h>
+#include <format/binary/ResChunkPullParser.h>
+
 #include <algorithm>
 #include <map>
 #include <memory>
@@ -23,17 +26,16 @@
 #include <set>
 #include <vector>
 
-#include "android-base/logging.h"
-#include "android-base/stringprintf.h"
-
 #include "ResourceTable.h"
+#include "ResourceUtils.h"
 #include "ResourceValues.h"
 #include "ValueVisitor.h"
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "idmap2/Policies.h"
 #include "text/Printer.h"
 #include "util/Util.h"
 
-#include "idmap2/Policies.h"
-
 using ::aapt::text::Printer;
 using ::android::StringPiece;
 using ::android::base::StringPrintf;
@@ -584,4 +586,260 @@
   }
 }
 
+namespace {
+
+using namespace android;
+
+class ChunkPrinter {
+ public:
+  ChunkPrinter(const void* data, size_t len, Printer* printer, IDiagnostics* diag)
+      : data_(data), data_len_(len), printer_(printer), diag_(diag) {
+  }
+
+  void PrintChunkHeader(const ResChunk_header* chunk) {
+    switch (util::DeviceToHost16(chunk->type)) {
+      case RES_STRING_POOL_TYPE:
+        printer_->Print("[RES_STRING_POOL_TYPE]");
+        break;
+      case RES_TABLE_LIBRARY_TYPE:
+        printer_->Print("[RES_TABLE_LIBRARY_TYPE]");
+        break;
+      case RES_TABLE_TYPE:
+        printer_->Print("[ResTable_header]");
+        break;
+      case RES_TABLE_PACKAGE_TYPE:
+        printer_->Print("[ResTable_package]");
+        break;
+      case RES_TABLE_TYPE_TYPE:
+        printer_->Print("[ResTable_type]");
+        break;
+      case RES_TABLE_TYPE_SPEC_TYPE:
+        printer_->Print("[RES_TABLE_TYPE_SPEC_TYPE]");
+        break;
+      default:
+        break;
+    }
+
+    printer_->Print(StringPrintf(" chunkSize: %u", util::DeviceToHost32(chunk->size)));
+    printer_->Print(StringPrintf(" headerSize: %u", util::DeviceToHost32(chunk->headerSize)));
+  }
+
+  bool PrintTable(const ResTable_header* chunk) {
+    printer_->Print(
+        StringPrintf(" Package count: %u\n", util::DeviceToHost32(chunk->packageCount)));
+
+    // Print the chunks contained within the table
+    printer_->Indent();
+    bool success = PrintChunk(
+        ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header)));
+    printer_->Undent();
+    return success;
+  }
+
+  void PrintResValue(const Res_value* value, const ConfigDescription& config,
+                     const ResourceType* type) {
+    printer_->Print("[Res_value]");
+    printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(value->size)));
+    printer_->Print(StringPrintf(" dataType: 0x%02x", util::DeviceToHost32(value->dataType)));
+    printer_->Print(StringPrintf(" data: 0x%08x", util::DeviceToHost32(value->data)));
+
+    if (type) {
+      auto item =
+          ResourceUtils::ParseBinaryResValue(*type, config, value_pool_, *value, &out_pool_);
+      printer_->Print(" (");
+      item->PrettyPrint(printer_);
+      printer_->Print(")");
+    }
+
+    printer_->Print("\n");
+  }
+
+  bool PrintTableType(const ResTable_type* chunk) {
+    printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id)));
+    printer_->Print(StringPrintf(
+        " name: %s", util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1).c_str()));
+    printer_->Print(StringPrintf(" flags: 0x%02x", util::DeviceToHost32(chunk->flags)));
+    printer_->Print(StringPrintf(" entryCount: %u", util::DeviceToHost32(chunk->entryCount)));
+    printer_->Print(StringPrintf(" entryStart: %u", util::DeviceToHost32(chunk->entriesStart)));
+
+    ConfigDescription config;
+    config.copyFromDtoH(chunk->config);
+    printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str()));
+
+    const ResourceType* type =
+        ParseResourceType(util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1));
+
+    printer_->Indent();
+
+    TypeVariant tv(chunk);
+    for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
+      const ResTable_entry* entry = *it;
+      if (!entry) {
+        continue;
+      }
+
+      printer_->Print((entry->flags & ResTable_entry::FLAG_COMPLEX) ? "[ResTable_map_entry]"
+                                                                    : "[ResTable_entry]");
+      printer_->Print(StringPrintf(" id: 0x%04x", it.index()));
+      printer_->Print(StringPrintf(
+          " name: %s", util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)).c_str()));
+      printer_->Print(StringPrintf(" keyIndex: %u", util::DeviceToHost32(entry->key.index)));
+      printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(entry->size)));
+      printer_->Print(StringPrintf(" flags: 0x%04x", util::DeviceToHost32(entry->flags)));
+
+      printer_->Indent();
+
+      if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
+        auto map_entry = (const ResTable_map_entry*)entry;
+        printer_->Print(StringPrintf(" count: 0x%04x", util::DeviceToHost32(map_entry->count)));
+        printer_->Print(
+            StringPrintf(" parent: 0x%08x\n", util::DeviceToHost32(map_entry->parent.ident)));
+
+        // Print the name and value mappings
+        auto maps =
+            (const ResTable_map*)((const uint8_t*)entry + util::DeviceToHost32(entry->size));
+        for (size_t i = 0, count = util::DeviceToHost32(map_entry->count); i < count; i++) {
+          PrintResValue(&(maps[i].value), config, type);
+
+          printer_->Print(StringPrintf(
+              " name: %s name-id:%d\n",
+              util::GetString(key_pool_, util::DeviceToHost32(maps[i].name.ident)).c_str(),
+              util::DeviceToHost32(maps[i].name.ident)));
+        }
+      } else {
+        printer_->Print("\n");
+
+        // Print the value of the entry
+        auto value = (const Res_value*)((const uint8_t*)entry + util::DeviceToHost32(entry->size));
+        PrintResValue(value, config, type);
+      }
+
+      printer_->Undent();
+    }
+
+    printer_->Undent();
+    return true;
+  }
+
+  void PrintStringPool(const ResStringPool_header* chunk) {
+    // Initialize the string pools
+
+    ResStringPool* pool;
+    if (value_pool_.getError() == NO_INIT) {
+      pool = &value_pool_;
+    } else if (type_pool_.getError() == NO_INIT) {
+      pool = &type_pool_;
+    } else if (key_pool_.getError() == NO_INIT) {
+      pool = &key_pool_;
+    } else {
+      return;
+    }
+
+    pool->setTo(chunk,
+                util::DeviceToHost32((reinterpret_cast<const ResChunk_header*>(chunk))->size));
+
+    printer_->Print("\n");
+
+    for (size_t i = 0; i < pool->size(); i++) {
+      printer_->Print(StringPrintf("#%zd : %s\n", i, util::GetString(*pool, i).c_str()));
+    }
+  }
+
+  bool PrintPackage(const ResTable_package* chunk) {
+    printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id)));
+
+    size_t len = strnlen16((const char16_t*)chunk->name, std::size(chunk->name));
+    std::u16string package_name(len, u'\0');
+    package_name.resize(len);
+    for (size_t i = 0; i < len; i++) {
+      package_name[i] = util::DeviceToHost16(chunk->name[i]);
+    }
+
+    printer_->Print(StringPrintf("name: %s", String8(package_name.c_str()).c_str()));
+    printer_->Print(StringPrintf(" typeStrings: %u", util::DeviceToHost32(chunk->typeStrings)));
+    printer_->Print(
+        StringPrintf(" lastPublicType: %u", util::DeviceToHost32(chunk->lastPublicType)));
+    printer_->Print(StringPrintf(" keyStrings: %u", util::DeviceToHost32(chunk->keyStrings)));
+    printer_->Print(StringPrintf(" lastPublicKey: %u", util::DeviceToHost32(chunk->lastPublicKey)));
+    printer_->Print(StringPrintf(" typeIdOffset: %u\n", util::DeviceToHost32(chunk->typeIdOffset)));
+
+    // Print the chunks contained within the table
+    printer_->Indent();
+    bool success = PrintChunk(
+        ResChunkPullParser(GetChunkData(&chunk->header), GetChunkDataLen(&chunk->header)));
+    printer_->Undent();
+    return success;
+  }
+
+  bool PrintChunk(ResChunkPullParser&& parser) {
+    while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+      auto chunk = parser.chunk();
+      PrintChunkHeader(chunk);
+
+      switch (util::DeviceToHost16(chunk->type)) {
+        case RES_STRING_POOL_TYPE:
+          PrintStringPool(reinterpret_cast<const ResStringPool_header*>(chunk));
+          break;
+
+        case RES_TABLE_TYPE:
+          PrintTable(reinterpret_cast<const ResTable_header*>(chunk));
+          break;
+
+        case RES_TABLE_PACKAGE_TYPE:
+          type_pool_.uninit();
+          key_pool_.uninit();
+          PrintPackage(reinterpret_cast<const ResTable_package*>(chunk));
+          break;
+
+        case RES_TABLE_TYPE_TYPE:
+          PrintTableType(reinterpret_cast<const ResTable_type*>(chunk));
+          break;
+
+        default:
+          printer_->Print("\n");
+          break;
+      }
+    }
+
+    if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
+      diag_->Error(DiagMessage(source_) << "corrupt resource table: " << parser.error());
+      return false;
+    }
+
+    return true;
+  }
+
+  void Print() {
+    PrintChunk(ResChunkPullParser(data_, data_len_));
+    printer_->Print("[End]\n");
+  }
+
+ private:
+  const Source source_;
+  const void* data_;
+  const size_t data_len_;
+  Printer* printer_;
+  IDiagnostics* diag_;
+
+  // The standard value string pool for resource values.
+  ResStringPool value_pool_;
+
+  // The string pool that holds the names of the types defined
+  // in this table.
+  ResStringPool type_pool_;
+
+  // The string pool that holds the names of the entries defined
+  // in this table.
+  ResStringPool key_pool_;
+
+  StringPool out_pool_;
+};
+
+}  // namespace
+
+void Debug::DumpChunks(const void* data, size_t len, Printer* printer, IDiagnostics* diag) {
+  ChunkPrinter chunk_printer(data, len, printer, diag);
+  chunk_printer.Print();
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 9443d60..4da9204 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -40,6 +40,7 @@
   static void DumpXml(const xml::XmlResource& doc, text::Printer* printer);
   static void DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer);
   static void DumpOverlayable(const ResourceTable& table, text::Printer* printer);
+  static void DumpChunks(const void* data, size_t len, text::Printer* printer, IDiagnostics* diag);
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 0a1e021..bcce3e5 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -560,4 +560,21 @@
     32,  32,  32,  32,  32,  32,  32,  32,  32,  46,  32,  32,  46,  32,  32,  32,  32,  32,  32,
     32,  32,  32,  32,  32,  32,  32,  32,  32,  32,  32,  32,  32,  32,  32,  32,  32,  10};
 
+int DumpChunks::Dump(LoadedApk* apk) {
+  auto file = apk->GetFileCollection()->FindFile("resources.arsc");
+  if (!file) {
+    GetDiagnostics()->Error(DiagMessage() << "Failed to find resources.arsc in APK");
+    return 1;
+  }
+
+  auto data = file->OpenAsData();
+  if (!data) {
+    GetDiagnostics()->Error(DiagMessage() << "Failed to open resources.arsc ");
+    return 1;
+  }
+
+  Debug::DumpChunks(data->data(), data->size(), GetPrinter(), GetDiagnostics());
+  return 0;
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 52616fa..ec320ec 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -17,6 +17,9 @@
 #ifndef AAPT2_DUMP_H
 #define AAPT2_DUMP_H
 
+#include <io/FileStream.h>
+#include <io/ZipArchive.h>
+
 #include "Command.h"
 #include "Debug.h"
 #include "LoadedApk.h"
@@ -226,6 +229,16 @@
   std::vector<std::string> files_;
 };
 
+class DumpChunks : public DumpApkCommand {
+ public:
+  DumpChunks(text::Printer* printer, IDiagnostics* diag) : DumpApkCommand("chunks", printer, diag) {
+    SetDescription("Print the chunk information of the compiled resources.arsc in the APK.");
+  }
+
+  int Dump(LoadedApk* apk) override;
+};
+
+/** Prints the tree of a compiled xml in an APK. */
 class DumpXmlTreeCommand : public DumpApkCommand {
  public:
   explicit DumpXmlTreeCommand(text::Printer* printer, IDiagnostics* diag)
@@ -263,6 +276,7 @@
     AddOptionalSubcommand(util::make_unique<DumpStringsCommand>(printer, diag_));
     AddOptionalSubcommand(util::make_unique<DumpStyleParentCommand>(printer, diag_));
     AddOptionalSubcommand(util::make_unique<DumpTableCommand>(printer, diag_));
+    AddOptionalSubcommand(util::make_unique<DumpChunks>(printer, diag_));
     AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_));
     AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_));
     AddOptionalSubcommand(util::make_unique<DumpOverlayableCommand>(printer, diag_));