Merge "Add support to render app on multiple displays"
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
index 9d92e0f..b8857ec 100644
--- a/MULTIUSER_OWNERS
+++ b/MULTIUSER_OWNERS
@@ -1,5 +1,9 @@
 # OWNERS of Multiuser related files
+annabauza@google.com
 bookatz@google.com
+nykkumar@google.com
 olilan@google.com
 omakoto@google.com
+tetianameronyk@google.com
+tyk@google.com
 yamasani@google.com
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index c3fc7b1..be0e025 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -235,6 +235,15 @@
     public static final String KEY_JS_HARD_CONSUMPTION_LIMIT = "js_hard_consumption_limit";
     // TODO: Add JobScheduler modifier keys
     /** @hide */
+    public static final String KEY_JS_REWARD_APP_INSTALL_INSTANT =
+            "js_reward_app_install_instant";
+    /** @hide */
+    public static final String KEY_JS_REWARD_APP_INSTALL_ONGOING =
+            "js_reward_app_install_ongoing";
+    /** @hide */
+    public static final String KEY_JS_REWARD_APP_INSTALL_MAX =
+            "js_reward_app_install_max";
+    /** @hide */
     public static final String KEY_JS_REWARD_TOP_ACTIVITY_INSTANT =
             "js_reward_top_activity_instant";
     /** @hide */
@@ -463,6 +472,12 @@
     public static final long DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES = arcToCake(250_000);
     // TODO: add JobScheduler modifier default values
     /** @hide */
+    public static final long DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES = arcToCake(408);
+    /** @hide */
+    public static final long DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES = arcToCake(0);
+    /** @hide */
+    public static final long DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES = arcToCake(4000);
+    /** @hide */
     public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES = arcToCake(0);
     /** @hide */
     public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES = CAKE_IN_ARC / 2;
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 40d1b4c..bd475e9 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -37,6 +37,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
+import android.content.res.Resources;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -993,69 +994,69 @@
                 "pre_idle_factor_short";
         private static final String KEY_USE_WINDOW_ALARMS = "use_window_alarms";
 
-        private static final long DEFAULT_FLEX_TIME_SHORT =
+        private long mDefaultFlexTimeShort =
                 !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
-        private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
+        private long mDefaultLightIdleAfterInactiveTimeout =
                 !COMPRESS_TIME ? 4 * 60 * 1000L : 30 * 1000L;
-        private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
+        private long mDefaultLightIdleTimeout =
                 !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
-        private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
-        private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
+        private float mDefaultLightIdleFactor = 2f;
+        private long mDefaultLightMaxIdleTimeout =
                 !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
-        private static final long DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
+        private long mDefaultLightIdleMaintenanceMinBudget =
                 !COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L;
-        private static final long DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET =
+        private long mDefaultLightIdleMaintenanceMaxBudget =
                 !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
-        private static final long DEFAULT_MIN_LIGHT_MAINTENANCE_TIME =
+        private long mDefaultMinLightMaintenanceTime =
                 !COMPRESS_TIME ? 5 * 1000L : 1 * 1000L;
-        private static final long DEFAULT_MIN_DEEP_MAINTENANCE_TIME =
+        private long mDefaultMinDeepMaintenanceTime =
                 !COMPRESS_TIME ? 30 * 1000L : 5 * 1000L;
-        private static final long DEFAULT_INACTIVE_TIMEOUT =
+        private long mDefaultInactiveTimeout =
                 (30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
         private static final long DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY =
                 (15 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
-        private static final long DEFAULT_SENSING_TIMEOUT =
+        private long mDefaultSensingTimeout =
                 !COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L;
-        private static final long DEFAULT_LOCATING_TIMEOUT =
+        private long mDefaultLocatingTimeout =
                 !COMPRESS_TIME ? 30 * 1000L : 15 * 1000L;
-        private static final float DEFAULT_LOCATION_ACCURACY = 20f;
-        private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT =
+        private float mDefaultLocationAccuracy = 20f;
+        private long mDefaultMotionInactiveTimeout =
                 !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
-        private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX =
+        private long mDefaultMotionInactiveTimeoutFlex =
                 !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
-        private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT =
+        private long mDefaultIdleAfterInactiveTimeout =
                 (30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
         private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY =
                 (15 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
-        private static final long DEFAULT_IDLE_PENDING_TIMEOUT =
+        private long mDefaultIdlePendingTimeout =
                 !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
-        private static final long DEFAULT_MAX_IDLE_PENDING_TIMEOUT =
+        private long mDefaultMaxIdlePendingTimeout =
                 !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
-        private static final float DEFAULT_IDLE_PENDING_FACTOR = 2f;
-        private static final long DEFAULT_QUICK_DOZE_DELAY_TIMEOUT =
+        private float mDefaultIdlePendingFactor = 2f;
+        private long mDefaultQuickDozeDelayTimeout =
                 !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
-        private static final long DEFAULT_IDLE_TIMEOUT =
+        private long mDefaultIdleTimeout =
                 !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L;
-        private static final long DEFAULT_MAX_IDLE_TIMEOUT =
+        private long mDefaultMaxIdleTimeout =
                 !COMPRESS_TIME ? 6 * 60 * 60 * 1000L : 30 * 60 * 1000L;
-        private static final float DEFAULT_IDLE_FACTOR = 2f;
-        private static final long DEFAULT_MIN_TIME_TO_ALARM =
+        private float mDefaultIdleFactor = 2f;
+        private long mDefaultMinTimeToAlarm =
                 !COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L;
-        private static final long DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS = 5 * 60 * 1000L;
-        private static final long DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS = 60 * 1000L;
-        private static final long DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS = 20 * 1000L;
-        private static final long DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS = 30 * 1000L;
-        private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true;
-        private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f;
-        private static final float DEFAULT_PRE_IDLE_FACTOR_SHORT = .33f;
-        private static final boolean DEFAULT_USE_WINDOW_ALARMS = true;
+        private long mDefaultMaxTempAppAllowlistDurationMs = 5 * 60 * 1000L;
+        private long mDefaultMmsTempAppAllowlistDurationMs = 60 * 1000L;
+        private long mDefaultSmsTempAppAllowlistDurationMs = 20 * 1000L;
+        private long mDefaultNotificationAllowlistDurationMs = 30 * 1000L;
+        private boolean mDefaultWaitForUnlock = true;
+        private float mDefaultPreIdleFactorLong = 1.67f;
+        private float mDefaultPreIdleFactorShort = .33f;
+        private boolean mDefaultUseWindowAlarms = true;
 
         /**
          * A somewhat short alarm window size that we will tolerate for various alarm timings.
          *
          * @see #KEY_FLEX_TIME_SHORT
          */
-        public long FLEX_TIME_SHORT = DEFAULT_FLEX_TIME_SHORT;
+        public long FLEX_TIME_SHORT = mDefaultFlexTimeShort;
 
         /**
          * This is the time, after becoming inactive, that we go in to the first
@@ -1063,28 +1064,28 @@
          *
          * @see #KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
          */
-        public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+        public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultLightIdleAfterInactiveTimeout;
 
         /**
          * This is the initial time that we will run in light idle maintenance mode.
          *
          * @see #KEY_LIGHT_IDLE_TIMEOUT
          */
-        public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
+        public long LIGHT_IDLE_TIMEOUT = mDefaultLightIdleTimeout;
 
         /**
          * Scaling factor to apply to the light idle mode time each time we complete a cycle.
          *
          * @see #KEY_LIGHT_IDLE_FACTOR
          */
-        public float LIGHT_IDLE_FACTOR = DEFAULT_LIGHT_IDLE_FACTOR;
+        public float LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
 
         /**
          * This is the maximum time we will stay in light idle mode.
          *
          * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
          */
-        public long LIGHT_MAX_IDLE_TIMEOUT = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT;
+        public long LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout;
 
         /**
          * This is the minimum amount of time we want to make available for maintenance mode
@@ -1093,7 +1094,7 @@
          *
          * @see #KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
          */
-        public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+        public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget;
 
         /**
          * This is the maximum amount of time we want to make available for maintenance mode
@@ -1104,7 +1105,7 @@
          *
          * @see #KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
          */
-        public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+        public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget;
 
         /**
          * This is the minimum amount of time that we will stay in maintenance mode after
@@ -1115,7 +1116,7 @@
          *
          * @see #KEY_MIN_LIGHT_MAINTENANCE_TIME
          */
-        public long MIN_LIGHT_MAINTENANCE_TIME = DEFAULT_MIN_LIGHT_MAINTENANCE_TIME;
+        public long MIN_LIGHT_MAINTENANCE_TIME = mDefaultMinLightMaintenanceTime;
 
         /**
          * This is the minimum amount of time that we will stay in maintenance mode after
@@ -1125,7 +1126,7 @@
          * mode immediately.
          * @see #KEY_MIN_DEEP_MAINTENANCE_TIME
          */
-        public long MIN_DEEP_MAINTENANCE_TIME = DEFAULT_MIN_DEEP_MAINTENANCE_TIME;
+        public long MIN_DEEP_MAINTENANCE_TIME = mDefaultMinDeepMaintenanceTime;
 
         /**
          * This is the time, after becoming inactive, at which we start looking at the
@@ -1134,7 +1135,7 @@
          * the motion sensor whenever the screen is off.
          * @see #KEY_INACTIVE_TIMEOUT
          */
-        public long INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT;
+        public long INACTIVE_TIMEOUT = mDefaultInactiveTimeout;
 
         /**
          * If we don't receive a callback from AnyMotion in this amount of time +
@@ -1143,14 +1144,14 @@
          * will be ignored.
          * @see #KEY_SENSING_TIMEOUT
          */
-        public long SENSING_TIMEOUT = DEFAULT_SENSING_TIMEOUT;
+        public long SENSING_TIMEOUT = mDefaultSensingTimeout;
 
         /**
          * This is how long we will wait to try to get a good location fix before going in to
          * idle mode.
          * @see #KEY_LOCATING_TIMEOUT
          */
-        public long LOCATING_TIMEOUT = DEFAULT_LOCATING_TIMEOUT;
+        public long LOCATING_TIMEOUT = mDefaultLocatingTimeout;
 
         /**
          * The desired maximum accuracy (in meters) we consider the location to be good enough to go
@@ -1158,7 +1159,7 @@
          * {@link #LOCATING_TIMEOUT} expires.
          * @see #KEY_LOCATION_ACCURACY
          */
-        public float LOCATION_ACCURACY = DEFAULT_LOCATION_ACCURACY;
+        public float LOCATION_ACCURACY = mDefaultLocationAccuracy;
 
         /**
          * This is the time, after seeing motion, that we wait after becoming inactive from
@@ -1166,14 +1167,14 @@
          *
          * @see #KEY_MOTION_INACTIVE_TIMEOUT
          */
-        public long MOTION_INACTIVE_TIMEOUT = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+        public long MOTION_INACTIVE_TIMEOUT = mDefaultMotionInactiveTimeout;
 
         /**
          * This is the alarm window size we will tolerate for motion detection timings.
          *
          * @see #KEY_MOTION_INACTIVE_TIMEOUT_FLEX
          */
-        public long MOTION_INACTIVE_TIMEOUT_FLEX = DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX;
+        public long MOTION_INACTIVE_TIMEOUT_FLEX = mDefaultMotionInactiveTimeoutFlex;
 
         /**
          * This is the time, after the inactive timeout elapses, that we will wait looking
@@ -1181,7 +1182,7 @@
          *
          * @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
          */
-        public long IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
+        public long IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultIdleAfterInactiveTimeout;
 
         /**
          * This is the initial time, after being idle, that we will allow ourself to be back
@@ -1189,20 +1190,20 @@
          * idle.
          * @see #KEY_IDLE_PENDING_TIMEOUT
          */
-        public long IDLE_PENDING_TIMEOUT = DEFAULT_IDLE_PENDING_TIMEOUT;
+        public long IDLE_PENDING_TIMEOUT = mDefaultIdlePendingTimeout;
 
         /**
          * Maximum pending idle timeout (time spent running) we will be allowed to use.
          * @see #KEY_MAX_IDLE_PENDING_TIMEOUT
          */
-        public long MAX_IDLE_PENDING_TIMEOUT = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
+        public long MAX_IDLE_PENDING_TIMEOUT = mDefaultMaxIdlePendingTimeout;
 
         /**
          * Scaling factor to apply to current pending idle timeout each time we cycle through
          * that state.
          * @see #KEY_IDLE_PENDING_FACTOR
          */
-        public float IDLE_PENDING_FACTOR = DEFAULT_IDLE_PENDING_FACTOR;
+        public float IDLE_PENDING_FACTOR = mDefaultIdlePendingFactor;
 
         /**
          * This is amount of time we will wait from the point where we go into
@@ -1210,33 +1211,33 @@
          * and other current activity to finish.
          * @see #KEY_QUICK_DOZE_DELAY_TIMEOUT
          */
-        public long QUICK_DOZE_DELAY_TIMEOUT = DEFAULT_QUICK_DOZE_DELAY_TIMEOUT;
+        public long QUICK_DOZE_DELAY_TIMEOUT = mDefaultQuickDozeDelayTimeout;
 
         /**
          * This is the initial time that we want to sit in the idle state before waking up
          * again to return to pending idle and allowing normal work to run.
          * @see #KEY_IDLE_TIMEOUT
          */
-        public long IDLE_TIMEOUT = DEFAULT_IDLE_TIMEOUT;
+        public long IDLE_TIMEOUT = mDefaultIdleTimeout;
 
         /**
          * Maximum idle duration we will be allowed to use.
          * @see #KEY_MAX_IDLE_TIMEOUT
          */
-        public long MAX_IDLE_TIMEOUT = DEFAULT_MAX_IDLE_TIMEOUT;
+        public long MAX_IDLE_TIMEOUT = mDefaultMaxIdleTimeout;
 
         /**
          * Scaling factor to apply to current idle timeout each time we cycle through that state.
          * @see #KEY_IDLE_FACTOR
          */
-        public float IDLE_FACTOR = DEFAULT_IDLE_FACTOR;
+        public float IDLE_FACTOR = mDefaultIdleFactor;
 
         /**
          * This is the minimum time we will allow until the next upcoming alarm for us to
          * actually go in to idle mode.
          * @see #KEY_MIN_TIME_TO_ALARM
          */
-        public long MIN_TIME_TO_ALARM = DEFAULT_MIN_TIME_TO_ALARM;
+        public long MIN_TIME_TO_ALARM = mDefaultMinTimeToAlarm;
 
         /**
          * Max amount of time to temporarily whitelist an app when it receives a high priority
@@ -1244,48 +1245,49 @@
          *
          * @see #KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS
          */
-        public long MAX_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS;
+        public long MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMaxTempAppAllowlistDurationMs;
 
         /**
          * Amount of time we would like to whitelist an app that is receiving an MMS.
          * @see #KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS
          */
-        public long MMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS;
+        public long MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMmsTempAppAllowlistDurationMs;
 
         /**
          * Amount of time we would like to whitelist an app that is receiving an SMS.
          * @see #KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS
          */
-        public long SMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS;
+        public long SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultSmsTempAppAllowlistDurationMs;
 
         /**
          * Amount of time we would like to whitelist an app that is handling a
          * {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
          * @see #KEY_NOTIFICATION_ALLOWLIST_DURATION_MS
          */
-        public long NOTIFICATION_ALLOWLIST_DURATION_MS = DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS;
+        public long NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
 
         /**
          * Pre idle time factor use to make idle delay longer
          */
-        public float PRE_IDLE_FACTOR_LONG = DEFAULT_PRE_IDLE_FACTOR_LONG;
+        public float PRE_IDLE_FACTOR_LONG = mDefaultPreIdleFactorLong;
 
         /**
          * Pre idle time factor use to make idle delay shorter
          */
-        public float PRE_IDLE_FACTOR_SHORT = DEFAULT_PRE_IDLE_FACTOR_SHORT;
+        public float PRE_IDLE_FACTOR_SHORT = mDefaultPreIdleFactorShort;
 
-        public boolean WAIT_FOR_UNLOCK = DEFAULT_WAIT_FOR_UNLOCK;
+        public boolean WAIT_FOR_UNLOCK = mDefaultWaitForUnlock;
 
         /**
          * Whether to use window alarms. True to use window alarms (call AlarmManager.setWindow()).
          * False to use the legacy inexact alarms (call AlarmManager.set()).
          */
-        public boolean USE_WINDOW_ALARMS = DEFAULT_USE_WINDOW_ALARMS;
+        public boolean USE_WINDOW_ALARMS = mDefaultUseWindowAlarms;
 
         private final boolean mSmallBatteryDevice;
 
         public Constants() {
+            initDefault();
             mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
             if (mSmallBatteryDevice) {
                 INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY;
@@ -1297,6 +1299,132 @@
             onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
         }
 
+        private void initDefault() {
+            final Resources res = getContext().getResources();
+
+            mDefaultFlexTimeShort = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_flex_time_short_ms),
+                    mDefaultFlexTimeShort);
+            mDefaultLightIdleAfterInactiveTimeout = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_light_after_inactive_to_ms),
+                    mDefaultLightIdleAfterInactiveTimeout);
+            mDefaultLightIdleTimeout = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_light_idle_to_ms),
+                    mDefaultLightIdleTimeout);
+            mDefaultLightIdleFactor = res.getFloat(
+                    com.android.internal.R.integer.device_idle_light_idle_factor);
+            mDefaultLightMaxIdleTimeout = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_light_max_idle_to_ms),
+                    mDefaultLightMaxIdleTimeout);
+            mDefaultLightIdleMaintenanceMinBudget = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_light_idle_maintenance_min_budget_ms
+                    ), mDefaultLightIdleMaintenanceMinBudget);
+            mDefaultLightIdleMaintenanceMaxBudget = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_light_idle_maintenance_max_budget_ms
+                    ), mDefaultLightIdleMaintenanceMaxBudget);
+            mDefaultMinLightMaintenanceTime = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_min_light_maintenance_time_ms),
+                    mDefaultMinLightMaintenanceTime);
+            mDefaultMinDeepMaintenanceTime = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_min_deep_maintenance_time_ms),
+                    mDefaultMinDeepMaintenanceTime);
+            mDefaultInactiveTimeout = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_inactive_to_ms),
+                    mDefaultInactiveTimeout);
+            mDefaultSensingTimeout = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_sensing_to_ms),
+                    mDefaultSensingTimeout);
+            mDefaultLocatingTimeout = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_locating_to_ms),
+                    mDefaultLocatingTimeout);
+            mDefaultLocationAccuracy = res.getFloat(
+                    com.android.internal.R.integer.device_idle_location_accuracy);
+            mDefaultMotionInactiveTimeout = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_motion_inactive_to_ms),
+                    mDefaultMotionInactiveTimeout);
+            mDefaultMotionInactiveTimeoutFlex = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_motion_inactive_to_flex_ms),
+                    mDefaultMotionInactiveTimeoutFlex);
+            mDefaultIdleAfterInactiveTimeout = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_idle_after_inactive_to_ms),
+                    mDefaultIdleAfterInactiveTimeout);
+            mDefaultIdlePendingTimeout = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_idle_pending_to_ms),
+                    mDefaultIdlePendingTimeout);
+            mDefaultMaxIdlePendingTimeout = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_max_idle_pending_to_ms),
+                    mDefaultMaxIdlePendingTimeout);
+            mDefaultIdlePendingFactor = res.getFloat(
+                    com.android.internal.R.integer.device_idle_idle_pending_factor);
+            mDefaultQuickDozeDelayTimeout = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_quick_doze_delay_to_ms),
+                    mDefaultQuickDozeDelayTimeout);
+            mDefaultIdleTimeout = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_idle_to_ms),
+                    mDefaultIdleTimeout);
+            mDefaultMaxIdleTimeout = getTimeout(
+                    res.getInteger(com.android.internal.R.integer.device_idle_max_idle_to_ms),
+                    mDefaultMaxIdleTimeout);
+            mDefaultIdleFactor = res.getFloat(
+                    com.android.internal.R.integer.device_idle_idle_factor);
+            mDefaultMinTimeToAlarm = getTimeout(res.getInteger(
+                    com.android.internal.R.integer.device_idle_min_time_to_alarm_ms),
+                    mDefaultMinTimeToAlarm);
+            mDefaultMaxTempAppAllowlistDurationMs = res.getInteger(
+                    com.android.internal.R.integer.device_idle_max_temp_app_allowlist_duration_ms);
+            mDefaultMmsTempAppAllowlistDurationMs = res.getInteger(
+                    com.android.internal.R.integer.device_idle_mms_temp_app_allowlist_duration_ms);
+            mDefaultSmsTempAppAllowlistDurationMs = res.getInteger(
+                    com.android.internal.R.integer.device_idle_sms_temp_app_allowlist_duration_ms);
+            mDefaultNotificationAllowlistDurationMs = res.getInteger(
+                    com.android.internal.R.integer.device_idle_notification_allowlist_duration_ms);
+            mDefaultWaitForUnlock = res.getBoolean(
+                    com.android.internal.R.bool.device_idle_wait_for_unlock);
+            mDefaultPreIdleFactorLong = res.getFloat(
+                    com.android.internal.R.integer.device_idle_pre_idle_factor_long);
+            mDefaultPreIdleFactorShort = res.getFloat(
+                    com.android.internal.R.integer.device_idle_pre_idle_factor_short);
+            mDefaultUseWindowAlarms = res.getBoolean(
+                    com.android.internal.R.bool.device_idle_use_window_alarms);
+
+            FLEX_TIME_SHORT = mDefaultFlexTimeShort;
+            LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultLightIdleAfterInactiveTimeout;
+            LIGHT_IDLE_TIMEOUT = mDefaultLightIdleTimeout;
+            LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
+            LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout;
+            LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget;
+            LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget;
+            MIN_LIGHT_MAINTENANCE_TIME = mDefaultMinLightMaintenanceTime;
+            MIN_DEEP_MAINTENANCE_TIME = mDefaultMinDeepMaintenanceTime;
+            INACTIVE_TIMEOUT = mDefaultInactiveTimeout;
+            SENSING_TIMEOUT = mDefaultSensingTimeout;
+            LOCATING_TIMEOUT = mDefaultLocatingTimeout;
+            LOCATION_ACCURACY = mDefaultLocationAccuracy;
+            MOTION_INACTIVE_TIMEOUT = mDefaultMotionInactiveTimeout;
+            MOTION_INACTIVE_TIMEOUT_FLEX = mDefaultMotionInactiveTimeoutFlex;
+            IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultIdleAfterInactiveTimeout;
+            IDLE_PENDING_TIMEOUT = mDefaultIdlePendingTimeout;
+            MAX_IDLE_PENDING_TIMEOUT = mDefaultMaxIdlePendingTimeout;
+            IDLE_PENDING_FACTOR = mDefaultIdlePendingFactor;
+            QUICK_DOZE_DELAY_TIMEOUT = mDefaultQuickDozeDelayTimeout;
+            IDLE_TIMEOUT = mDefaultIdleTimeout;
+            MAX_IDLE_TIMEOUT = mDefaultMaxIdleTimeout;
+            IDLE_FACTOR = mDefaultIdleFactor;
+            MIN_TIME_TO_ALARM = mDefaultMinTimeToAlarm;
+            MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMaxTempAppAllowlistDurationMs;
+            MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMmsTempAppAllowlistDurationMs;
+            SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultSmsTempAppAllowlistDurationMs;
+            NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
+            WAIT_FOR_UNLOCK = mDefaultWaitForUnlock;
+            PRE_IDLE_FACTOR_LONG = mDefaultPreIdleFactorLong;
+            PRE_IDLE_FACTOR_SHORT = mDefaultPreIdleFactorShort;
+            USE_WINDOW_ALARMS = mDefaultUseWindowAlarms;
+        }
+
+        private long getTimeout(long defTimeout, long compTimeout) {
+            return (!COMPRESS_TIME || defTimeout < compTimeout) ? defTimeout : compTimeout;
+        }
+
 
         @Override
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
@@ -1308,147 +1436,147 @@
                     switch (name) {
                         case KEY_FLEX_TIME_SHORT:
                             FLEX_TIME_SHORT = properties.getLong(
-                                    KEY_FLEX_TIME_SHORT, DEFAULT_FLEX_TIME_SHORT);
+                                    KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
                             break;
                         case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
                             LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
                                     KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
-                                    DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+                                    mDefaultLightIdleAfterInactiveTimeout);
                             break;
                         case KEY_LIGHT_IDLE_TIMEOUT:
                             LIGHT_IDLE_TIMEOUT = properties.getLong(
-                                    KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
+                                    KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
                             break;
                         case KEY_LIGHT_IDLE_FACTOR:
                             LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
-                                    KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR));
+                                    KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
                             break;
                         case KEY_LIGHT_MAX_IDLE_TIMEOUT:
                             LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
-                                    KEY_LIGHT_MAX_IDLE_TIMEOUT, DEFAULT_LIGHT_MAX_IDLE_TIMEOUT);
+                                    KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
                             break;
                         case KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET:
                             LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = properties.getLong(
                                     KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
-                                    DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
+                                    mDefaultLightIdleMaintenanceMinBudget);
                             break;
                         case KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET:
                             LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = properties.getLong(
                                     KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
-                                    DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET);
+                                    mDefaultLightIdleMaintenanceMaxBudget);
                             break;
                         case KEY_MIN_LIGHT_MAINTENANCE_TIME:
                             MIN_LIGHT_MAINTENANCE_TIME = properties.getLong(
                                     KEY_MIN_LIGHT_MAINTENANCE_TIME,
-                                    DEFAULT_MIN_LIGHT_MAINTENANCE_TIME);
+                                    mDefaultMinLightMaintenanceTime);
                             break;
                         case KEY_MIN_DEEP_MAINTENANCE_TIME:
                             MIN_DEEP_MAINTENANCE_TIME = properties.getLong(
                                     KEY_MIN_DEEP_MAINTENANCE_TIME,
-                                    DEFAULT_MIN_DEEP_MAINTENANCE_TIME);
+                                    mDefaultMinDeepMaintenanceTime);
                             break;
                         case KEY_INACTIVE_TIMEOUT:
                             final long defaultInactiveTimeout = mSmallBatteryDevice
                                     ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
-                                    : DEFAULT_INACTIVE_TIMEOUT;
+                                    : mDefaultInactiveTimeout;
                             INACTIVE_TIMEOUT = properties.getLong(
                                     KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
                             break;
                         case KEY_SENSING_TIMEOUT:
                             SENSING_TIMEOUT = properties.getLong(
-                                    KEY_SENSING_TIMEOUT, DEFAULT_SENSING_TIMEOUT);
+                                    KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
                             break;
                         case KEY_LOCATING_TIMEOUT:
                             LOCATING_TIMEOUT = properties.getLong(
-                                    KEY_LOCATING_TIMEOUT, DEFAULT_LOCATING_TIMEOUT);
+                                    KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
                             break;
                         case KEY_LOCATION_ACCURACY:
                             LOCATION_ACCURACY = properties.getFloat(
-                                    KEY_LOCATION_ACCURACY, DEFAULT_LOCATION_ACCURACY);
+                                    KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
                             break;
                         case KEY_MOTION_INACTIVE_TIMEOUT:
                             MOTION_INACTIVE_TIMEOUT = properties.getLong(
-                                    KEY_MOTION_INACTIVE_TIMEOUT, DEFAULT_MOTION_INACTIVE_TIMEOUT);
+                                    KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
                             break;
                         case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
                             MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
                                     KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
-                                    DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX);
+                                    mDefaultMotionInactiveTimeoutFlex);
                             break;
                         case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
                             final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
                                     ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
-                                    : DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
+                                    : mDefaultIdleAfterInactiveTimeout;
                             IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
                                     KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
                                     defaultIdleAfterInactiveTimeout);
                             break;
                         case KEY_IDLE_PENDING_TIMEOUT:
                             IDLE_PENDING_TIMEOUT = properties.getLong(
-                                    KEY_IDLE_PENDING_TIMEOUT, DEFAULT_IDLE_PENDING_TIMEOUT);
+                                    KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
                             break;
                         case KEY_MAX_IDLE_PENDING_TIMEOUT:
                             MAX_IDLE_PENDING_TIMEOUT = properties.getLong(
-                                    KEY_MAX_IDLE_PENDING_TIMEOUT, DEFAULT_MAX_IDLE_PENDING_TIMEOUT);
+                                    KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
                             break;
                         case KEY_IDLE_PENDING_FACTOR:
                             IDLE_PENDING_FACTOR = properties.getFloat(
-                                    KEY_IDLE_PENDING_FACTOR, DEFAULT_IDLE_PENDING_FACTOR);
+                                    KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
                             break;
                         case KEY_QUICK_DOZE_DELAY_TIMEOUT:
                             QUICK_DOZE_DELAY_TIMEOUT = properties.getLong(
-                                    KEY_QUICK_DOZE_DELAY_TIMEOUT, DEFAULT_QUICK_DOZE_DELAY_TIMEOUT);
+                                    KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
                             break;
                         case KEY_IDLE_TIMEOUT:
                             IDLE_TIMEOUT = properties.getLong(
-                                    KEY_IDLE_TIMEOUT, DEFAULT_IDLE_TIMEOUT);
+                                    KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
                             break;
                         case KEY_MAX_IDLE_TIMEOUT:
                             MAX_IDLE_TIMEOUT = properties.getLong(
-                                    KEY_MAX_IDLE_TIMEOUT, DEFAULT_MAX_IDLE_TIMEOUT);
+                                    KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
                             break;
                         case KEY_IDLE_FACTOR:
-                            IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, DEFAULT_IDLE_FACTOR);
+                            IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, mDefaultIdleFactor);
                             break;
                         case KEY_MIN_TIME_TO_ALARM:
                             MIN_TIME_TO_ALARM = properties.getLong(
-                                    KEY_MIN_TIME_TO_ALARM, DEFAULT_MIN_TIME_TO_ALARM);
+                                    KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
                             break;
                         case KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS:
                             MAX_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
                                     KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
-                                    DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS);
+                                    mDefaultMaxTempAppAllowlistDurationMs);
                             break;
                         case KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS:
                             MMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
                                     KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
-                                    DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS);
+                                    mDefaultMmsTempAppAllowlistDurationMs);
                             break;
                         case KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS:
                             SMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
                                     KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
-                                    DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS);
+                                    mDefaultSmsTempAppAllowlistDurationMs);
                             break;
                         case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
                             NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
                                     KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
-                                    DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS);
+                                    mDefaultNotificationAllowlistDurationMs);
                             break;
                         case KEY_WAIT_FOR_UNLOCK:
                             WAIT_FOR_UNLOCK = properties.getBoolean(
-                                    KEY_WAIT_FOR_UNLOCK, DEFAULT_WAIT_FOR_UNLOCK);
+                                    KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
                             break;
                         case KEY_PRE_IDLE_FACTOR_LONG:
                             PRE_IDLE_FACTOR_LONG = properties.getFloat(
-                                    KEY_PRE_IDLE_FACTOR_LONG, DEFAULT_PRE_IDLE_FACTOR_LONG);
+                                    KEY_PRE_IDLE_FACTOR_LONG, mDefaultPreIdleFactorLong);
                             break;
                         case KEY_PRE_IDLE_FACTOR_SHORT:
                             PRE_IDLE_FACTOR_SHORT = properties.getFloat(
-                                    KEY_PRE_IDLE_FACTOR_SHORT, DEFAULT_PRE_IDLE_FACTOR_SHORT);
+                                    KEY_PRE_IDLE_FACTOR_SHORT, mDefaultPreIdleFactorShort);
                             break;
                         case KEY_USE_WINDOW_ALARMS:
                             USE_WINDOW_ALARMS = properties.getBoolean(
-                                    KEY_USE_WINDOW_ALARMS, DEFAULT_USE_WINDOW_ALARMS);
+                                    KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
                             break;
                         default:
                             Slog.e(TAG, "Unknown configuration key: " + name);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 3bbc5a3..d284a99 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -69,6 +69,11 @@
      */
     private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
 
+    @GuardedBy("mLock")
+    private Boolean mLastReportedStatsdBatteryNotLow = null;
+    @GuardedBy("mLock")
+    private Boolean mLastReportedStatsdStablePower = null;
+
     public BatteryController(JobSchedulerService service,
             FlexibilityController flexibilityController) {
         super(service);
@@ -176,12 +181,25 @@
             Slog.d(TAG, "maybeReportNewChargingStateLocked: "
                     + powerConnected + "/" + stablePower + "/" + batteryNotLow);
         }
+
+        if (mLastReportedStatsdStablePower == null
+                || mLastReportedStatsdStablePower != stablePower) {
+            logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_CHARGING, stablePower);
+            mLastReportedStatsdStablePower = stablePower;
+        }
+        if (mLastReportedStatsdBatteryNotLow == null
+                || mLastReportedStatsdBatteryNotLow != stablePower) {
+            logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_BATTERY_NOT_LOW,
+                    batteryNotLow);
+            mLastReportedStatsdBatteryNotLow = batteryNotLow;
+        }
+
         final long nowElapsed = sElapsedRealtimeClock.millis();
 
         mFlexibilityController.setConstraintSatisfied(
                 JobStatus.CONSTRAINT_CHARGING, mService.isBatteryCharging(), nowElapsed);
         mFlexibilityController.setConstraintSatisfied(
-                        JobStatus.CONSTRAINT_BATTERY_NOT_LOW, batteryNotLow, nowElapsed);
+                JobStatus.CONSTRAINT_BATTERY_NOT_LOW, batteryNotLow, nowElapsed);
 
         for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
             final JobStatus ts = mTrackedTasks.valueAt(i);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index f6de109..abbe177 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -153,6 +153,8 @@
                 changed = true;
             }
             mDeviceIdleMode = enabled;
+            logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_DEVICE_NOT_DOZING,
+                    !mDeviceIdleMode);
             if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
             mDeviceIdleUpdateFunctor.prepare();
             if (enabled) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index dd06217..926cfc1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -96,6 +96,8 @@
     @Override
     public void reportNewIdleState(boolean isIdle) {
         synchronized (mLock) {
+            logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_IDLE, isIdle);
+
             final long nowElapsed = sElapsedRealtimeClock.millis();
             mFlexibilityController.setConstraintSatisfied(
                     JobStatus.CONSTRAINT_IDLE, isIdle, nowElapsed);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 4320db0..999a3c0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -170,13 +170,12 @@
      */
     private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
             | CONSTRAINT_DEADLINE
-            | CONSTRAINT_IDLE
             | CONSTRAINT_PREFETCH
             | CONSTRAINT_TARE_WEALTH
             | CONSTRAINT_TIMING_DELAY
             | CONSTRAINT_WITHIN_QUOTA;
 
-    // TODO(b/129954980)
+    // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot
     private static final boolean STATS_LOG_ENABLED = false;
 
     // No override.
@@ -1982,7 +1981,7 @@
     }
 
     /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */
-    private int getProtoConstraint(int constraint) {
+    static int getProtoConstraint(int constraint) {
         switch (constraint) {
             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
                 return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
@@ -1998,10 +1997,16 @@
                 return JobServerProtoEnums.CONSTRAINT_DEADLINE;
             case CONSTRAINT_DEVICE_NOT_DOZING:
                 return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING;
+            case CONSTRAINT_FLEXIBLE:
+                return JobServerProtoEnums.CONSTRAINT_FLEXIBILITY;
             case CONSTRAINT_IDLE:
                 return JobServerProtoEnums.CONSTRAINT_IDLE;
+            case CONSTRAINT_PREFETCH:
+                return JobServerProtoEnums.CONSTRAINT_PREFETCH;
             case CONSTRAINT_STORAGE_NOT_LOW:
                 return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW;
+            case CONSTRAINT_TARE_WEALTH:
+                return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH;
             case CONSTRAINT_TIMING_DELAY:
                 return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY;
             case CONSTRAINT_WITHIN_QUOTA:
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 2a2d602..8453e53 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -26,6 +26,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobSchedulerService.Constants;
 import com.android.server.job.StateChangedListener;
@@ -165,6 +166,15 @@
         return mService.areComponentsInPlaceLocked(jobStatus);
     }
 
+    protected void logDeviceWideConstraintStateToStatsd(int constraint, boolean satisfied) {
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED,
+                JobStatus.getProtoConstraint(constraint),
+                satisfied
+                        ? FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
+                        : FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
+    }
+
     public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate);
     public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index b79cc5e4..7391bcf 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -120,6 +120,7 @@
             REWARD_NOTIFICATION_INTERACTION,
             REWARD_WIDGET_INTERACTION,
             REWARD_OTHER_USER_INTERACTION,
+            JobSchedulerEconomicPolicy.REWARD_APP_INSTALL,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UtilityReward {
@@ -430,6 +431,8 @@
                 return "REWARD_WIDGET_INTERACTION";
             case REWARD_OTHER_USER_INTERACTION:
                 return "REWARD_OTHER_USER_INTERACTION";
+            case JobSchedulerEconomicPolicy.REWARD_APP_INSTALL:
+                return "REWARD_JOB_APP_INSTALL";
         }
         return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
index da544bb..fcb3e67 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
@@ -17,9 +17,12 @@
 package com.android.server.tare;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
 import android.content.pm.PackageInfo;
-import android.os.UserHandle;
+import android.os.RemoteException;
 
 /** POJO to cache only the information about installed packages that TARE cares about. */
 class InstalledPackageInfo {
@@ -28,11 +31,21 @@
     public final int uid;
     public final String packageName;
     public final boolean hasCode;
+    @Nullable
+    public final String installerPackageName;
 
     InstalledPackageInfo(@NonNull PackageInfo packageInfo) {
         final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
-        this.uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
-        this.packageName = packageInfo.packageName;
-        this.hasCode = applicationInfo != null && applicationInfo.hasCode();
+        uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
+        packageName = packageInfo.packageName;
+        hasCode = applicationInfo != null && applicationInfo.hasCode();
+        InstallSourceInfo installSourceInfo = null;
+        try {
+            installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName);
+        } catch (RemoteException e) {
+            // Shouldn't happen.
+        }
+        installerPackageName =
+                installSourceInfo == null ? null : installSourceInfo.getInstallingPackageName();
     }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 59d4ded..c13e1dd9 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -524,10 +524,15 @@
             mPackageToUidCache.add(userId, pkgName, uid);
         }
         synchronized (mLock) {
-            mPkgCache.add(userId, pkgName, new InstalledPackageInfo(packageInfo));
+            final InstalledPackageInfo ipo = new InstalledPackageInfo(packageInfo);
+            mPkgCache.add(userId, pkgName, ipo);
             mUidToPackageCache.add(uid, pkgName);
             // TODO: only do this when the user first launches the app (app leaves stopped state)
             mAgent.grantBirthrightLocked(userId, pkgName);
+            if (ipo.installerPackageName != null) {
+                mAgent.noteInstantaneousEventLocked(userId, ipo.installerPackageName,
+                        JobSchedulerEconomicPolicy.REWARD_APP_INSTALL, null);
+            }
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index cbb88c0..55cc352 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -43,6 +43,9 @@
 import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES;
 import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES;
@@ -85,6 +88,9 @@
 import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_ONGOING;
 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
 import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING;
@@ -137,6 +143,8 @@
     public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JS | 9;
     public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JS | 10;
 
+    public static final int REWARD_APP_INSTALL = TYPE_REWARD | POLICY_JS | 0;
+
     private static final int[] COST_MODIFIERS = new int[]{
             COST_MODIFIER_CHARGING,
             COST_MODIFIER_DEVICE_IDLE,
@@ -374,6 +382,17 @@
                         getConstantAsCake(mParser, properties,
                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
+        mRewards.put(REWARD_APP_INSTALL,
+                new Reward(REWARD_APP_INSTALL,
+                        getConstantAsCake(mParser, properties,
+                                KEY_JS_REWARD_APP_INSTALL_INSTANT,
+                                DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES),
+                        getConstantAsCake(mParser, properties,
+                                KEY_JS_REWARD_APP_INSTALL_ONGOING,
+                                DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES),
+                        getConstantAsCake(mParser, properties,
+                                KEY_JS_REWARD_APP_INSTALL_MAX,
+                                DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES)));
     }
 
     @Override
diff --git a/core/api/current.txt b/core/api/current.txt
index 4d25ad7..f554e71 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52951,7 +52951,7 @@
 package android.view.inputmethod {
 
   public class BaseInputConnection implements android.view.inputmethod.InputConnection {
-    ctor public BaseInputConnection(android.view.View, boolean);
+    ctor public BaseInputConnection(@NonNull android.view.View, boolean);
     method public boolean beginBatchEdit();
     method public boolean clearMetaKeyStates(int);
     method @CallSuper public void closeConnection();
@@ -52963,24 +52963,24 @@
     method public boolean deleteSurroundingTextInCodePoints(int, int);
     method public boolean endBatchEdit();
     method public boolean finishComposingText();
-    method public static int getComposingSpanEnd(android.text.Spannable);
-    method public static int getComposingSpanStart(android.text.Spannable);
+    method public static int getComposingSpanEnd(@NonNull android.text.Spannable);
+    method public static int getComposingSpanStart(@NonNull android.text.Spannable);
     method public int getCursorCapsMode(int);
-    method public android.text.Editable getEditable();
-    method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
-    method public android.os.Handler getHandler();
-    method public CharSequence getSelectedText(int);
+    method @Nullable public android.text.Editable getEditable();
+    method @Nullable public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
+    method @Nullable public android.os.Handler getHandler();
+    method @Nullable public CharSequence getSelectedText(int);
     method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
     method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
     method public boolean performContextMenuAction(int);
     method public boolean performEditorAction(int);
     method public boolean performPrivateCommand(String, android.os.Bundle);
-    method public static final void removeComposingSpans(android.text.Spannable);
+    method public static final void removeComposingSpans(@NonNull android.text.Spannable);
     method public boolean reportFullscreenMode(boolean);
     method public boolean requestCursorUpdates(int);
     method public boolean sendKeyEvent(android.view.KeyEvent);
     method public boolean setComposingRegion(int, int);
-    method public static void setComposingSpans(android.text.Spannable);
+    method public static void setComposingSpans(@NonNull android.text.Spannable);
     method public boolean setComposingText(CharSequence, int);
     method public boolean setSelection(int, int);
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f09244a..619e9fd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1318,6 +1318,7 @@
     method @NonNull public java.time.Instant getStartTime();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
+    field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3
     field public static final int EVENT_COUGH = 1; // 0x1
     field public static final int EVENT_SNORE = 2; // 0x2
     field public static final int EVENT_UNKNOWN = 0; // 0x0
@@ -10347,6 +10348,7 @@
     field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
     field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
     field public static final String NAMESPACE_AUTOFILL = "autofill";
+    field public static final String NAMESPACE_BACKUP_AND_RESTORE = "backup_and_restore";
     field public static final String NAMESPACE_BATTERY_SAVER = "battery_saver";
     field public static final String NAMESPACE_BIOMETRICS = "biometrics";
     field public static final String NAMESPACE_BLOBSTORE = "blobstore";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index deb5aca..f780e6f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2709,7 +2709,7 @@
 
   public class SparseArrayMap<K, V> {
     ctor public SparseArrayMap();
-    method public void add(int, @NonNull K, @Nullable V);
+    method public V add(int, @NonNull K, @Nullable V);
     method public void clear();
     method public boolean contains(int, @NonNull K);
     method public void delete(int);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8f5457a..d899dab 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4216,23 +4216,13 @@
         }
     }*/
 
-    /** @hide
-     * Determines whether the given UID can access unexported components
-     * @param uid the calling UID
-     * @return true if the calling UID is ROOT or SYSTEM
-     */
-    public static boolean canAccessUnexportedComponents(int uid) {
-        final int appId = UserHandle.getAppId(uid);
-        return (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID);
-    }
-
     /** @hide */
     @UnsupportedAppUsage
     public static int checkComponentPermission(String permission, int uid,
             int owningUid, boolean exported) {
         // Root, system server get to do everything.
         final int appId = UserHandle.getAppId(uid);
-        if (canAccessUnexportedComponents(uid)) {
+        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
             return PackageManager.PERMISSION_GRANTED;
         }
         // Isolated processes don't get any permissions.
diff --git a/core/java/android/app/AppOpInfo.java b/core/java/android/app/AppOpInfo.java
index 979c910..5268ec4 100644
--- a/core/java/android/app/AppOpInfo.java
+++ b/core/java/android/app/AppOpInfo.java
@@ -26,6 +26,7 @@
  * Information about a particular app op.
  */
 class AppOpInfo {
+
     /**
      * A unique constant identifying this app op.
      */
@@ -91,6 +92,11 @@
      */
     public final boolean restrictRead;
 
+    /**
+     * Whether to collect noteOp instances, and send them to callbacks.
+     */
+    public final boolean forceCollectNotes;
+
     AppOpInfo(int code,
             int switchCode,
             @NonNull String name,
@@ -100,7 +106,8 @@
             AppOpsManager.RestrictionBypass allowSystemRestrictionBypass,
             int defaultMode,
             boolean disableReset,
-            boolean restrictRead) {
+            boolean restrictRead,
+            boolean forceCollectNotes) {
         if (code < OP_NONE) throw new IllegalArgumentException();
         if (switchCode < OP_NONE) throw new IllegalArgumentException();
         Objects.requireNonNull(name);
@@ -115,6 +122,7 @@
         this.defaultMode = defaultMode;
         this.disableReset = disableReset;
         this.restrictRead = restrictRead;
+        this.forceCollectNotes = forceCollectNotes;
     }
 
     static class Builder {
@@ -128,6 +136,7 @@
         private int mDefaultMode = AppOpsManager.MODE_DEFAULT;
         private boolean mDisableReset = false;
         private boolean mRestrictRead = false;
+        private boolean mForceCollectNotes = false;
 
         Builder(int code, @NonNull String name, @NonNull String simpleName) {
             if (code < OP_NONE) throw new IllegalArgumentException();
@@ -190,9 +199,15 @@
             return this;
         }
 
+        public Builder setForceCollectNotes(boolean value) {
+            this.mForceCollectNotes = value;
+            return this;
+        }
+
         public AppOpInfo build() {
             return new AppOpInfo(mCode, mSwitchCode, mName, mSimpleName, mPermission, mRestriction,
-                mAllowSystemRestrictionBypass, mDefaultMode, mDisableReset, mRestrictRead);
+                mAllowSystemRestrictionBypass, mDefaultMode, mDisableReset, mRestrictRead,
+                    mForceCollectNotes);
         }
     }
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index bc59d60..2a0553e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1831,6 +1831,9 @@
     })
     private @interface ShouldCollectNoteOp {}
 
+    /** Whether noting for an appop should be collected */
+    private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
+
     private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
             // RUNTIME PERMISSIONS
             // Contacts
@@ -2281,10 +2284,19 @@
                 "ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED)
             .setDisableReset(true).setRestrictRead(true).build(),
         new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
-                "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED).build()
+                "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED)
+                .setForceCollectNotes(true).build()
     };
 
     /**
+     * @hide
+     */
+    public static boolean shouldForceCollectNoteForOp(int op) {
+        Preconditions.checkArgumentInRange(op, 0, _NUM_OP - 1, "opCode");
+        return sAppOpInfos[op].forceCollectNotes;
+    }
+
+    /**
      * Mapping from an app op name to the app op code.
      */
     private static HashMap<String, Integer> sOpStrToOp = new HashMap<>();
@@ -2313,9 +2325,6 @@
     private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
             new ThreadLocal<>();
 
-    /** Whether noting for an appop should be collected */
-    private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
-
     static {
         if (sAppOpInfos.length != _NUM_OP) {
             throw new IllegalStateException("mAppOpInfos length " + sAppOpInfos.length
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index bd999fc..9c8d010 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -522,9 +522,6 @@
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
 
-    /** Enables server-side binder tracing for the calling uid. */
-    void enableBinderTracing();
-
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void suppressResizeConfigChanges(boolean suppress);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index af48fde..865e1fb 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -63,8 +63,6 @@
      * The integer indicating a double-tap event was detected.
      * For detecting this event type, there's no specific consent activity to request access, but
      * the consent is implied through the double tap toggle in the Settings app.
-     *
-     * @hide
      */
     public static final int EVENT_BACK_DOUBLE_TAP = 3;
 
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
index 6b0f78f..0f66fcb 100644
--- a/core/java/android/ddm/DdmHandleViewDebug.java
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -16,12 +16,16 @@
 
 package android.ddm;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import android.util.Log;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewRootImpl;
 import android.view.WindowManagerGlobal;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import org.apache.harmony.dalvik.ddmc.Chunk;
 import org.apache.harmony.dalvik.ddmc.ChunkHandler;
 import org.apache.harmony.dalvik.ddmc.DdmServer;
@@ -34,6 +38,7 @@
 import java.lang.reflect.Method;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
 
 /**
  * Handle various requests related to profiling / debugging of the view system.
@@ -123,14 +128,15 @@
         }
 
         if (type == CHUNK_VURT) {
-            if (op == VURT_DUMP_HIERARCHY)
+            if (op == VURT_DUMP_HIERARCHY) {
                 return dumpHierarchy(rootView, in);
-            else if (op == VURT_CAPTURE_LAYERS)
+            } else if (op == VURT_CAPTURE_LAYERS) {
                 return captureLayers(rootView);
-            else if (op == VURT_DUMP_THEME)
+            } else if (op == VURT_DUMP_THEME) {
                 return dumpTheme(rootView);
-            else
+            } else {
                 return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op);
+            }
         }
 
         final View targetView = getTargetView(rootView, in);
@@ -207,9 +213,9 @@
     /**
      * Returns the view hierarchy and/or view properties starting at the provided view.
      * Based on the input options, the return data may include:
-     *  - just the view hierarchy
-     *  - view hierarchy & the properties for each of the views
-     *  - just the view properties for a specific view.
+     * - just the view hierarchy
+     * - view hierarchy & the properties for each of the views
+     * - just the view properties for a specific view.
      *  TODO: Currently this only returns views starting at the root, need to fix so that
      *  it can return properties of any view.
      */
@@ -220,7 +226,7 @@
 
         long start = System.currentTimeMillis();
 
-        ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024);
+        ByteArrayOutputStream b = new ByteArrayOutputStream(2 * 1024 * 1024);
         try {
             if (v2) {
                 ViewDebug.dumpv2(rootView, b);
@@ -304,17 +310,47 @@
      * Invokes provided method on the view.
      * The method name and its arguments are passed in as inputs via the byte buffer.
      * The buffer contains:<ol>
-     *  <li> len(method name) </li>
-     *  <li> method name </li>
-     *  <li> # of args </li>
-     *  <li> arguments: Each argument comprises of a type specifier followed by the actual argument.
-     *          The type specifier is a single character as used in JNI:
-     *          (Z - boolean, B - byte, C - char, S - short, I - int, J - long,
-     *          F - float, D - double). <p>
-     *          The type specifier is followed by the actual value of argument.
-     *          Booleans are encoded via bytes with 0 indicating false.</li>
+     * <li> len(method name) </li>
+     * <li> method name (encoded as UTF-16 2-byte characters) </li>
+     * <li> # of args </li>
+     * <li> arguments: Each argument comprises of a type specifier followed by the actual argument.
+     * The type specifier is one character modelled after JNI signatures:
+     *          <ul>
+     *              <li>[ - array<br>
+     *                This is followed by a second character according to this spec, indicating the
+     *                array type, then the array length as an Int, followed by a repeated encoding
+     *                of the actual data.
+     *                WARNING: Only <b>byte[]</b> is supported currently.
+     *              </li>
+     *              <li>Z - boolean<br>
+     *                 Booleans are encoded via bytes with 0 indicating false</li>
+     *              <li>B - byte</li>
+     *              <li>C - char</li>
+     *              <li>S - short</li>
+     *              <li>I - int</li>
+     *              <li>J - long</li>
+     *              <li>F - float</li>
+     *              <li>D - double</li>
+     *              <li>V - void<br>
+     *                NOT followed by a value. Only used for return types</li>
+     *              <li>R - String (not a real JNI type, but added for convenience)<br>
+     *                Strings are encoded as an unsigned short of the number of <b>bytes</b>,
+     *                followed by the actual UTF-8 encoded bytes.
+     *                WARNING: This is the same encoding as produced by
+     *                ViewHierarchyEncoder#writeString. However, note that this encoding is
+     *                different to what DdmHandle#getString() expects, which is used in other places
+     *                in this class.
+     *                WARNING: Since the length is the number of UTF-8 encoded bytes, Strings can
+     *                contain up to 64k ASCII characters, yet depending on the actual data, the true
+     *                maximum might be as little as 21844 unicode characters.
+     *                <b>null</b> String objects are encoded as an empty string
+     *              </li>
+     *            </ul>
+     *   </li>
      * </ol>
      * Methods that take no arguments need only specify the method name.
+     *
+     * The return value is encoded the same way as a single parameter (type + value)
      */
     private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) {
         int l = in.getInt();
@@ -327,54 +363,17 @@
             args = new Object[0];
         } else {
             int nArgs = in.getInt();
-
             argTypes = new Class<?>[nArgs];
             args = new Object[nArgs];
 
-            for (int i = 0; i < nArgs; i++) {
-                char c = in.getChar();
-                switch (c) {
-                    case 'Z':
-                        argTypes[i] = boolean.class;
-                        args[i] = in.get() == 0 ? false : true;
-                        break;
-                    case 'B':
-                        argTypes[i] = byte.class;
-                        args[i] = in.get();
-                        break;
-                    case 'C':
-                        argTypes[i] = char.class;
-                        args[i] = in.getChar();
-                        break;
-                    case 'S':
-                        argTypes[i] = short.class;
-                        args[i] = in.getShort();
-                        break;
-                    case 'I':
-                        argTypes[i] = int.class;
-                        args[i] = in.getInt();
-                        break;
-                    case 'J':
-                        argTypes[i] = long.class;
-                        args[i] = in.getLong();
-                        break;
-                    case 'F':
-                        argTypes[i] = float.class;
-                        args[i] = in.getFloat();
-                        break;
-                    case 'D':
-                        argTypes[i] = double.class;
-                        args[i] = in.getDouble();
-                        break;
-                    default:
-                        Log.e(TAG, "arg " + i + ", unrecognized type: " + c);
-                        return createFailChunk(ERR_INVALID_PARAM,
-                                "Unsupported parameter type (" + c + ") to invoke view method.");
-                }
+            try {
+                deserializeMethodParameters(args, argTypes, in);
+            } catch (ViewMethodInvocationSerializationException e) {
+                return createFailChunk(ERR_INVALID_PARAM, e.getMessage());
             }
         }
 
-        Method method = null;
+        Method method;
         try {
             method = targetView.getClass().getMethod(methodName, argTypes);
         } catch (NoSuchMethodException e) {
@@ -384,7 +383,10 @@
         }
 
         try {
-            ViewDebug.invokeViewMethod(targetView, method, args);
+            Object result = ViewDebug.invokeViewMethod(targetView, method, args);
+            Class<?> returnType = method.getReturnType();
+            byte[] returnValue = serializeReturnValue(returnType, returnType.cast(result));
+            return new Chunk(CHUNK_VUOP, returnValue, 0, returnValue.length);
         } catch (Exception e) {
             Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage());
             String msg = e.getCause().getMessage();
@@ -393,8 +395,6 @@
             }
             return createFailChunk(ERR_EXCEPTION, msg);
         }
-
-        return null;
     }
 
     private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) {
@@ -406,7 +406,7 @@
         } catch (Exception e) {
             Log.e(TAG, "Exception setting layout parameter: " + e);
             return createFailChunk(ERR_EXCEPTION, "Error accessing field "
-                        + param + ":" + e.getMessage());
+                    + param + ":" + e.getMessage());
         }
 
         return null;
@@ -431,4 +431,175 @@
         byte[] data = b.toByteArray();
         return new Chunk(CHUNK_VUOP, data, 0, data.length);
     }
+
+    /**
+     * Deserializes parameters according to the VUOP_INVOKE_VIEW_METHOD protocol the {@code in}
+     * buffer.
+     *
+     * The length of {@code args} determines how many arguments are read. The {@code argTypes} must
+     * be the same length, and will be set to the argument types of the data read.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static void deserializeMethodParameters(
+            Object[] args, Class<?>[] argTypes, ByteBuffer in) throws
+            ViewMethodInvocationSerializationException {
+        checkArgument(args.length == argTypes.length);
+
+        for (int i = 0; i < args.length; i++) {
+            char typeSignature = in.getChar();
+            boolean isArray = typeSignature == SIG_ARRAY;
+            if (isArray) {
+                char arrayType = in.getChar();
+                if (arrayType != SIG_BYTE) {
+                    // This implementation only supports byte-arrays for now.
+                    throw new ViewMethodInvocationSerializationException(
+                            "Unsupported array parameter type (" + typeSignature
+                                    + ") to invoke view method @argument " + i);
+                }
+
+                int arrayLength = in.getInt();
+                if (arrayLength > in.remaining()) {
+                    // The sender did not actually sent the specified amount of bytes. This
+                    // avoids a malformed packet to trigger an out-of-memory error.
+                    throw new BufferUnderflowException();
+                }
+
+                byte[] byteArray = new byte[arrayLength];
+                in.get(byteArray);
+
+                argTypes[i] = byte[].class;
+                args[i] = byteArray;
+            } else {
+                switch (typeSignature) {
+                    case SIG_BOOLEAN:
+                        argTypes[i] = boolean.class;
+                        args[i] = in.get() != 0;
+                        break;
+                    case SIG_BYTE:
+                        argTypes[i] = byte.class;
+                        args[i] = in.get();
+                        break;
+                    case SIG_CHAR:
+                        argTypes[i] = char.class;
+                        args[i] = in.getChar();
+                        break;
+                    case SIG_SHORT:
+                        argTypes[i] = short.class;
+                        args[i] = in.getShort();
+                        break;
+                    case SIG_INT:
+                        argTypes[i] = int.class;
+                        args[i] = in.getInt();
+                        break;
+                    case SIG_LONG:
+                        argTypes[i] = long.class;
+                        args[i] = in.getLong();
+                        break;
+                    case SIG_FLOAT:
+                        argTypes[i] = float.class;
+                        args[i] = in.getFloat();
+                        break;
+                    case SIG_DOUBLE:
+                        argTypes[i] = double.class;
+                        args[i] = in.getDouble();
+                        break;
+                    case SIG_STRING: {
+                        argTypes[i] = String.class;
+                        int stringUtf8ByteCount = Short.toUnsignedInt(in.getShort());
+                        byte[] rawStringBuffer = new byte[stringUtf8ByteCount];
+                        in.get(rawStringBuffer);
+                        args[i] = new String(rawStringBuffer, StandardCharsets.UTF_8);
+                        break;
+                    }
+                    default:
+                        Log.e(TAG, "arg " + i + ", unrecognized type: " + typeSignature);
+                        throw new ViewMethodInvocationSerializationException(
+                                "Unsupported parameter type (" + typeSignature
+                                        + ") to invoke view method.");
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Serializes {@code value} to the wire protocol of VUOP_INVOKE_VIEW_METHOD.
+     * @hide
+     */
+    @VisibleForTesting
+    public static byte[] serializeReturnValue(Class<?> type, Object value)
+            throws ViewMethodInvocationSerializationException, IOException {
+        ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(1024);
+        DataOutputStream dos = new DataOutputStream(byteOutStream);
+
+        if (type.isArray()) {
+            if (!type.equals(byte[].class)) {
+                // Only byte arrays are supported currently.
+                throw new ViewMethodInvocationSerializationException(
+                        "Unsupported array return type (" + type + ")");
+            }
+            byte[] byteArray = (byte[]) value;
+            dos.writeChar(SIG_ARRAY);
+            dos.writeChar(SIG_BYTE);
+            dos.writeInt(byteArray.length);
+            dos.write(byteArray);
+        } else if (boolean.class.equals(type)) {
+            dos.writeChar(SIG_BOOLEAN);
+            dos.write((boolean) value ? 1 : 0);
+        } else if (byte.class.equals(type)) {
+            dos.writeChar(SIG_BYTE);
+            dos.writeByte((byte) value);
+        } else if (char.class.equals(type)) {
+            dos.writeChar(SIG_CHAR);
+            dos.writeChar((char) value);
+        } else if (short.class.equals(type)) {
+            dos.writeChar(SIG_SHORT);
+            dos.writeShort((short) value);
+        } else if (int.class.equals(type)) {
+            dos.writeChar(SIG_INT);
+            dos.writeInt((int) value);
+        } else if (long.class.equals(type)) {
+            dos.writeChar(SIG_LONG);
+            dos.writeLong((long) value);
+        } else if (double.class.equals(type)) {
+            dos.writeChar(SIG_DOUBLE);
+            dos.writeDouble((double) value);
+        } else if (float.class.equals(type)) {
+            dos.writeChar(SIG_FLOAT);
+            dos.writeFloat((float) value);
+        } else if (String.class.equals(type)) {
+            dos.writeChar(SIG_STRING);
+            dos.writeUTF(value != null ? (String) value : "");
+        } else {
+            dos.writeChar(SIG_VOID);
+        }
+
+        return byteOutStream.toByteArray();
+    }
+
+    // Prefixes for simple primitives. These match the JNI definitions.
+    private static final char SIG_ARRAY = '[';
+    private static final char SIG_BOOLEAN = 'Z';
+    private static final char SIG_BYTE = 'B';
+    private static final char SIG_SHORT = 'S';
+    private static final char SIG_CHAR = 'C';
+    private static final char SIG_INT = 'I';
+    private static final char SIG_LONG = 'J';
+    private static final char SIG_FLOAT = 'F';
+    private static final char SIG_DOUBLE = 'D';
+    private static final char SIG_VOID = 'V';
+    // Prefixes for some commonly used objects
+    private static final char SIG_STRING = 'R';
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static class ViewMethodInvocationSerializationException extends Exception {
+        ViewMethodInvocationSerializationException(String message) {
+            super(message);
+        }
+    }
 }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 4f26ad2..d2e5f9e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -22,7 +22,6 @@
 import android.app.AppOpsManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ExceptionUtils;
-import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 
@@ -144,9 +143,6 @@
      */
     private static volatile boolean sStackTrackingEnabled = false;
 
-    private static final Object sTracingUidsWriteLock = new Object();
-    private static volatile IntArray sTracingUidsImmutable = new IntArray();
-
     /**
      * Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
      * {@link TransactionTracker}.
@@ -167,17 +163,6 @@
     }
 
     /**
-     * @hide
-     */
-    public static void enableTracingForUid(int uid) {
-        synchronized (sTracingUidsWriteLock) {
-            final IntArray copy = sTracingUidsImmutable.clone();
-            copy.add(uid);
-            sTracingUidsImmutable = copy;
-        }
-    }
-
-    /**
      * Check if binder transaction stack tracking is enabled.
      *
      * @hide
@@ -187,13 +172,6 @@
     }
 
     /**
-     * @hide
-     */
-    public static boolean isTracingEnabled(int callingUid) {
-        return sTracingUidsImmutable.indexOf(callingUid) != -1;
-    }
-
-    /**
      * Get the binder transaction tracker for this process.
      *
      * @hide
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 0a6a405..2c0be87 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -541,7 +541,8 @@
         }
 
         public void postUpdateSlider(int volume, int lastAudibleVolume, boolean mute) {
-            obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, new Boolean(mute)).sendToTarget();
+            obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, Boolean.valueOf(mute))
+                    .sendToTarget();
         }
     }
 
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index d125cbb..fe46c9e 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -783,6 +783,14 @@
     @TestApi
     public static final String NAMESPACE_INPUT_METHOD_MANAGER = "input_method_manager";
 
+    /**
+     * Namespace for backup and restore service related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_BACKUP_AND_RESTORE = "backup_and_restore";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/security/keymaster/OWNERS b/core/java/android/security/keymaster/OWNERS
index 65129a4..c4d605c 100644
--- a/core/java/android/security/keymaster/OWNERS
+++ b/core/java/android/security/keymaster/OWNERS
@@ -1,5 +1,5 @@
 # Bug component: 189335
 
 swillden@google.com
-jdanis@google.com
+eranm@google.com
 jbires@google.com
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 267b2ff..4d33bfd 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -91,6 +91,12 @@
     public final int icon;
 
     /**
+     * The maximum string length for any string contained in this condition.
+     * @hide
+     */
+    public static final int MAX_STRING_LENGTH = 1000;
+
+    /**
      * An object representing the current state of a {@link android.app.AutomaticZenRule}.
      * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
      * @param summary a user visible description of the rule state.
@@ -104,16 +110,19 @@
         if (id == null) throw new IllegalArgumentException("id is required");
         if (summary == null) throw new IllegalArgumentException("summary is required");
         if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
-        this.id = id;
-        this.summary = summary;
-        this.line1 = line1;
-        this.line2 = line2;
+        this.id = getTrimmedUri(id);
+        this.summary = getTrimmedString(summary);
+        this.line1 = getTrimmedString(line1);
+        this.line2 = getTrimmedString(line2);
         this.icon = icon;
         this.state = state;
         this.flags = flags;
     }
 
     public Condition(Parcel source) {
+        // This constructor passes all fields directly into the constructor that takes all the
+        // fields as arguments; that constructor will trim each of the input strings to
+        // max length if necessary.
         this((Uri)source.readParcelable(Condition.class.getClassLoader(), android.net.Uri.class),
                 source.readString(),
                 source.readString(),
@@ -240,4 +249,25 @@
             return new Condition[size];
         }
     };
+
+    /**
+     * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH.
+     */
+    private static String getTrimmedString(String input) {
+        if (input != null && input.length() > MAX_STRING_LENGTH) {
+            return input.substring(0, MAX_STRING_LENGTH);
+        }
+        return input;
+    }
+
+    /**
+     * Returns a truncated copy of the Uri by trimming the string representation to the maximum
+     * string length.
+     */
+    private static Uri getTrimmedUri(Uri input) {
+        if (input != null && input.toString().length() > MAX_STRING_LENGTH) {
+            return Uri.parse(getTrimmedString(input.toString()));
+        }
+        return input;
+    }
 }
diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java
index e5bb9f45..1a2c4df 100644
--- a/core/java/android/util/SparseArrayMap.java
+++ b/core/java/android/util/SparseArrayMap.java
@@ -34,14 +34,20 @@
 public class SparseArrayMap<K, V> {
     private final SparseArray<ArrayMap<K, V>> mData = new SparseArray<>();
 
-    /** Add an entry associating obj with the int-K pair. */
-    public void add(int key, @NonNull K mapKey, @Nullable V obj) {
+    /**
+     * Add an entry associating obj with the int-K pair.
+     *
+     * @return the previous value associated with key, or null if there was no mapping for key.
+     * (A null return can also indicate that the map previously associated null with key, if the
+     * implementation supports null values.)
+     */
+    public V add(int key, @NonNull K mapKey, @Nullable V obj) {
         ArrayMap<K, V> data = mData.get(key);
         if (data == null) {
             data = new ArrayMap<>();
             mData.put(key, data);
         }
-        data.put(mapKey, obj);
+        return data.put(mapKey, obj);
     }
 
     /** Remove all entries from the map. */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 94ea206..b698316 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -24512,8 +24512,9 @@
     /**
      * Set the current default focus highlight.
      * @param highlight the highlight drawable, or {@code null} if it's no longer needed.
+     * @hide
      */
-    private void setDefaultFocusHighlight(Drawable highlight) {
+    void setDefaultFocusHighlight(Drawable highlight) {
         mDefaultFocusHighlight = highlight;
         mDefaultFocusHighlightSizeChanged = true;
         if (highlight != null) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 074cbe5..89a1557 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3815,6 +3815,13 @@
                 if (mAttachInfo.mTooltipHost != null) {
                     mAttachInfo.mTooltipHost.hideTooltip();
                 }
+                if (!hasWindowFocus) {
+                    // Clear focus highlight if its window lost focus.
+                    final View focused = mView.findFocus();
+                    if (focused != null) {
+                        focused.setDefaultFocusHighlight(null);
+                    }
+                }
             }
 
             // Note: must be done after the focus change callbacks,
@@ -5846,7 +5853,13 @@
             // be when the window is first being added, and mFocused isn't
             // set yet.
             final View focused = mView.findFocus();
-            if (focused != null && !focused.isFocusableInTouchMode()) {
+            if (focused == null) {
+                return false;
+            }
+
+            // Clear default focus highlight if it entered touch mode.
+            focused.setDefaultFocusHighlight(null);
+            if (!focused.isFocusableInTouchMode()) {
                 final ViewGroup ancestorToTakeFocus = findAncestorToTakeFocusInTouchMode(focused);
                 if (ancestorToTakeFocus != null) {
                     // there is an ancestor that wants focus after its
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 624937f..8d75072 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -20,6 +20,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -61,8 +62,15 @@
     static final Object COMPOSING = new ComposingText();
 
     /** @hide */
-    protected final InputMethodManager mIMM;
-    final View mTargetView;
+    @NonNull protected final InputMethodManager mIMM;
+
+    /**
+     * Target view for the input connection.
+     *
+     * <p>This could be null for a fallback input connection.
+     */
+    @Nullable final View mTargetView;
+
     final boolean mFallbackMode;
 
     private Object[] mDefaultComposingSpans;
@@ -70,20 +78,25 @@
     Editable mEditable;
     KeyCharacterMap mKeyCharacterMap;
 
-    BaseInputConnection(InputMethodManager mgr, boolean fullEditor) {
+    BaseInputConnection(@NonNull InputMethodManager mgr, boolean fullEditor) {
         mIMM = mgr;
         mTargetView = null;
         mFallbackMode = !fullEditor;
     }
 
-    public BaseInputConnection(View targetView, boolean fullEditor) {
+    public BaseInputConnection(@NonNull View targetView, boolean fullEditor) {
         mIMM = (InputMethodManager)targetView.getContext().getSystemService(
                 Context.INPUT_METHOD_SERVICE);
         mTargetView = targetView;
         mFallbackMode = !fullEditor;
     }
 
-    public static final void removeComposingSpans(Spannable text) {
+    /**
+     * Removes the composing spans from the given text if any.
+     *
+     * @param text the spannable text to remove composing spans
+     */
+    public static final void removeComposingSpans(@NonNull Spannable text) {
         text.removeSpan(COMPOSING);
         Object[] sps = text.getSpans(0, text.length(), Object.class);
         if (sps != null) {
@@ -96,12 +109,17 @@
         }
     }
 
-    public static void setComposingSpans(Spannable text) {
+    /**
+     * Removes the composing spans from the given text if any.
+     *
+     * @param text the spannable text to remove composing spans
+     */
+    public static void setComposingSpans(@NonNull Spannable text) {
         setComposingSpans(text, 0, text.length());
     }
 
     /** @hide */
-    public static void setComposingSpans(Spannable text, int start, int end) {
+    public static void setComposingSpans(@NonNull Spannable text, int start, int end) {
         final Object[] sps = text.getSpans(start, end, Object.class);
         if (sps != null) {
             for (int i=sps.length-1; i>=0; i--) {
@@ -114,7 +132,10 @@
                 final int fl = text.getSpanFlags(o);
                 if ((fl & (Spanned.SPAN_COMPOSING | Spanned.SPAN_POINT_MARK_MASK))
                         != (Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
-                    text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
+                    text.setSpan(
+                            o,
+                            text.getSpanStart(o),
+                            text.getSpanEnd(o),
                             (fl & ~Spanned.SPAN_POINT_MARK_MASK)
                                     | Spanned.SPAN_COMPOSING
                                     | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -126,20 +147,24 @@
                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
     }
 
-    public static int getComposingSpanStart(Spannable text) {
+    /** Return the beginning of the range of composing text, or -1 if there's no composing text. */
+    public static int getComposingSpanStart(@NonNull Spannable text) {
         return text.getSpanStart(COMPOSING);
     }
 
-    public static int getComposingSpanEnd(Spannable text) {
+    /** Return the end of the range of composing text, or -1 if there's no composing text. */
+    public static int getComposingSpanEnd(@NonNull Spannable text) {
         return text.getSpanEnd(COMPOSING);
     }
 
     /**
-     * Return the target of edit operations.  The default implementation
-     * returns its own fake editable that is just used for composing text;
-     * subclasses that are real text editors should override this and
-     * supply their own.
+     * Return the target of edit operations. The default implementation returns its own fake
+     * editable that is just used for composing text; subclasses that are real text editors should
+     * override this and supply their own.
+     *
+     * <p>Subclasses could override this method to turn null.
      */
+    @Nullable
     public Editable getEditable() {
         if (mEditable == null) {
             mEditable = Editable.Factory.getInstance().newEditable("");
@@ -148,16 +173,14 @@
         return mEditable;
     }
 
-    /**
-     * Default implementation does nothing.
-     */
+    /** Default implementation does nothing. */
+    @Override
     public boolean beginBatchEdit() {
         return false;
     }
 
-    /**
-     * Default implementation does nothing.
-     */
+    /** Default implementation does nothing. */
+    @Override
     public boolean endBatchEdit() {
         return false;
     }
@@ -165,29 +188,29 @@
     /**
      * Called after only the composing region is modified (so it isn't called if the text also
      * changes).
-     * <p>
-     * Default implementation does nothing.
+     *
+     * <p>Default implementation does nothing.
      *
      * @hide
      */
-    public void endComposingRegionEditInternal() {
-    }
+    public void endComposingRegionEditInternal() {}
 
     /**
-     * Default implementation calls {@link #finishComposingText()} and
-     * {@code setImeConsumesInput(false)}.
+     * Default implementation calls {@link #finishComposingText()} and {@code
+     * setImeConsumesInput(false)}.
      */
     @CallSuper
+    @Override
     public void closeConnection() {
         finishComposingText();
         setImeConsumesInput(false);
     }
 
     /**
-     * Default implementation uses
-     * {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
+     * Default implementation uses {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
      * MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state.
      */
+    @Override
     public boolean clearMetaKeyStates(int states) {
         final Editable content = getEditable();
         if (content == null) return false;
@@ -195,25 +218,24 @@
         return true;
     }
 
-    /**
-     * Default implementation does nothing and returns false.
-     */
+    /** Default implementation does nothing and returns false. */
+    @Override
     public boolean commitCompletion(CompletionInfo text) {
         return false;
     }
 
-    /**
-     * Default implementation does nothing and returns false.
-     */
+    /** Default implementation does nothing and returns false. */
+    @Override
     public boolean commitCorrection(CorrectionInfo correctionInfo) {
         return false;
     }
 
     /**
-     * Default implementation replaces any existing composing text with
-     * the given text.  In addition, only if fallback mode, a key event is
-     * sent for the new text and the current editable buffer cleared.
+     * Default implementation replaces any existing composing text with the given text. In addition,
+     * only if fallback mode, a key event is sent for the new text and the current editable buffer
+     * cleared.
      */
+    @Override
     public boolean commitText(CharSequence text, int newCursorPosition) {
         if (DEBUG) Log.v(TAG, "commitText " + text);
         replaceText(text, newCursorPosition, false);
@@ -226,21 +248,19 @@
      * editable text.
      *
      * @param beforeLength The number of characters before the cursor to be deleted, in code unit.
-     *        If this is greater than the number of existing characters between the beginning of the
-     *        text and the cursor, then this method does not fail but deletes all the characters in
-     *        that range.
-     * @param afterLength The number of characters after the cursor to be deleted, in code unit.
-     *        If this is greater than the number of existing characters between the cursor and
-     *        the end of the text, then this method does not fail but deletes all the characters in
-     *        that range.
-     *
-     * @return {@code true} when selected text is deleted, {@code false} when either the
-     *         selection is invalid or not yet attached (i.e. selection start or end is -1),
-     *         or the editable text is {@code null}.
+     *     If this is greater than the number of existing characters between the beginning of the
+     *     text and the cursor, then this method does not fail but deletes all the characters in
+     *     that range.
+     * @param afterLength The number of characters after the cursor to be deleted, in code unit. If
+     *     this is greater than the number of existing characters between the cursor and the end of
+     *     the text, then this method does not fail but deletes all the characters in that range.
+     * @return {@code true} when selected text is deleted, {@code false} when either the selection
+     *     is invalid or not yet attached (i.e. selection start or end is -1), or the editable text
+     *     is {@code null}.
      */
+    @Override
     public boolean deleteSurroundingText(int beforeLength, int afterLength) {
-        if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
-                + " / " + afterLength);
+        if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength + " / " + afterLength);
         final Editable content = getEditable();
         if (content == null) return false;
 
@@ -389,7 +409,7 @@
                 continue;
             }
             if (java.lang.Character.isLowSurrogate(c)) {
-                return INVALID_INDEX;  // A invalid surrogate pair is found.
+                return INVALID_INDEX; // A invalid surrogate pair is found.
             }
             waitingLowSurrogate = true;
             ++currentIndex;
@@ -399,18 +419,18 @@
     /**
      * The default implementation performs the deletion around the current selection position of the
      * editable text.
+     *
      * @param beforeLength The number of characters before the cursor to be deleted, in code points.
-     *        If this is greater than the number of existing characters between the beginning of the
-     *        text and the cursor, then this method does not fail but deletes all the characters in
-     *        that range.
+     *     If this is greater than the number of existing characters between the beginning of the
+     *     text and the cursor, then this method does not fail but deletes all the characters in
+     *     that range.
      * @param afterLength The number of characters after the cursor to be deleted, in code points.
-     *        If this is greater than the number of existing characters between the cursor and
-     *        the end of the text, then this method does not fail but deletes all the characters in
-     *        that range.
+     *     If this is greater than the number of existing characters between the cursor and the end
+     *     of the text, then this method does not fail but deletes all the characters in that range.
      */
+    @Override
     public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
-        if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
-                + " / " + afterLength);
+        if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength + " / " + afterLength);
         final Editable content = getEditable();
         if (content == null) return false;
 
@@ -466,10 +486,11 @@
     }
 
     /**
-     * The default implementation removes the composing state from the
-     * current editable text.  In addition, only if fallback mode, a key event is
-     * sent for the new text and the current editable buffer cleared.
+     * The default implementation removes the composing state from the current editable text. In
+     * addition, only if fallback mode, a key event is sent for the new text and the current
+     * editable buffer cleared.
      */
+    @Override
     public boolean finishComposingText() {
         if (DEBUG) Log.v(TAG, "finishComposingText");
         final Editable content = getEditable();
@@ -485,10 +506,11 @@
     }
 
     /**
-     * The default implementation uses TextUtils.getCapsMode to get the
-     * cursor caps mode for the current selection position in the editable
-     * text, unless in fallback mode in which case 0 is always returned.
+     * The default implementation uses TextUtils.getCapsMode to get the cursor caps mode for the
+     * current selection position in the editable text, unless in fallback mode in which case 0 is
+     * always returned.
      */
+    @Override
     public int getCursorCapsMode(int reqModes) {
         if (mFallbackMode) return 0;
 
@@ -507,17 +529,18 @@
         return TextUtils.getCapsMode(content, a, reqModes);
     }
 
-    /**
-     * The default implementation always returns null.
-     */
+    /** The default implementation always returns null. */
+    @Override
+    @Nullable
     public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
         return null;
     }
 
     /**
-     * The default implementation returns the given amount of text from the
-     * current cursor position in the buffer.
+     * The default implementation returns the given amount of text from the current cursor position
+     * in the buffer.
      */
+    @Override
     @Nullable
     public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
         Preconditions.checkArgumentNonnegative(length);
@@ -549,9 +572,10 @@
     }
 
     /**
-     * The default implementation returns the text currently selected, or null if none is
-     * selected.
+     * The default implementation returns the text currently selected, or null if none is selected.
      */
+    @Override
+    @Nullable
     public CharSequence getSelectedText(int flags) {
         final Editable content = getEditable();
         if (content == null) return null;
@@ -574,9 +598,10 @@
     }
 
     /**
-     * The default implementation returns the given amount of text from the
-     * current cursor position in the buffer.
+     * The default implementation returns the given amount of text from the current cursor position
+     * in the buffer.
      */
+    @Override
     @Nullable
     public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
         Preconditions.checkArgumentNonnegative(length);
@@ -602,7 +627,6 @@
             length = content.length() - b;
         }
 
-
         if ((flags&GET_TEXT_WITH_STYLES) != 0) {
             return content.subSequence(b, b + length);
         }
@@ -613,9 +637,10 @@
      * The default implementation returns the given amount of text around the current cursor
      * position in the buffer.
      */
+    @Override
     @Nullable
     public SurroundingText getSurroundingText(
-            @IntRange(from = 0) int beforeLength, @IntRange(from = 0)  int afterLength, int flags) {
+            @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
         Preconditions.checkArgumentNonnegative(beforeLength);
         Preconditions.checkArgumentNonnegative(afterLength);
 
@@ -659,60 +684,75 @@
                 surroundingText, selStart - startPos, selEnd - startPos, startPos);
     }
 
-    /**
-     * The default implementation turns this into the enter key.
-     */
+    /** The default implementation turns this into the enter key. */
+    @Override
     public boolean performEditorAction(int actionCode) {
         long eventTime = SystemClock.uptimeMillis();
-        sendKeyEvent(new KeyEvent(eventTime, eventTime,
-                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
-                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
-                | KeyEvent.FLAG_EDITOR_ACTION));
-        sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
-                KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
-                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
-                | KeyEvent.FLAG_EDITOR_ACTION));
+        sendKeyEvent(
+                new KeyEvent(
+                        eventTime,
+                        eventTime,
+                        KeyEvent.ACTION_DOWN,
+                        KeyEvent.KEYCODE_ENTER,
+                        0,
+                        0,
+                        KeyCharacterMap.VIRTUAL_KEYBOARD,
+                        0,
+                        KeyEvent.FLAG_SOFT_KEYBOARD
+                                | KeyEvent.FLAG_KEEP_TOUCH_MODE
+                                | KeyEvent.FLAG_EDITOR_ACTION));
+        sendKeyEvent(
+                new KeyEvent(
+                        SystemClock.uptimeMillis(),
+                        eventTime,
+                        KeyEvent.ACTION_UP,
+                        KeyEvent.KEYCODE_ENTER,
+                        0,
+                        0,
+                        KeyCharacterMap.VIRTUAL_KEYBOARD,
+                        0,
+                        KeyEvent.FLAG_SOFT_KEYBOARD
+                                | KeyEvent.FLAG_KEEP_TOUCH_MODE
+                                | KeyEvent.FLAG_EDITOR_ACTION));
         return true;
     }
 
-    /**
-     * The default implementation does nothing.
-     */
+    /** The default implementation does nothing. */
+    @Override
     public boolean performContextMenuAction(int id) {
         return false;
     }
 
-    /**
-     * The default implementation does nothing.
-     */
+    /** The default implementation does nothing. */
+    @Override
     public boolean performPrivateCommand(String action, Bundle data) {
         return false;
     }
 
-    /**
-     * The default implementation does nothing.
-     */
+    /** The default implementation does nothing. */
+    @Override
     public boolean requestCursorUpdates(int cursorUpdateMode) {
         return false;
     }
 
+    @Override
+    @Nullable
     public Handler getHandler() {
         return null;
     }
 
     /**
-     * The default implementation places the given text into the editable,
-     * replacing any existing composing text.  The new text is marked as
-     * in a composing state with the composing style.
+     * The default implementation places the given text into the editable, replacing any existing
+     * composing text. The new text is marked as in a composing state with the composing style.
      */
+    @Override
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
         if (DEBUG) Log.v(TAG, "setComposingText " + text);
         replaceText(text, newCursorPosition, true);
         return true;
     }
 
+    @Override
     public boolean setComposingRegion(int start, int end) {
         final Editable content = getEditable();
         if (content != null) {
@@ -735,7 +775,10 @@
             ensureDefaultComposingSpans();
             if (mDefaultComposingSpans != null) {
                 for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
-                    content.setSpan(mDefaultComposingSpans[i], a, b,
+                    content.setSpan(
+                            mDefaultComposingSpans[i],
+                            a,
+                            b,
                             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
                 }
             }
@@ -751,10 +794,8 @@
         return true;
     }
 
-    /**
-     * The default implementation changes the selection position in the
-     * current editable text.
-     */
+    /** The default implementation changes the selection position in the current editable text. */
+    @Override
     public boolean setSelection(int start, int end) {
         if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end);
         final Editable content = getEditable();
@@ -779,17 +820,17 @@
     }
 
     /**
-     * Provides standard implementation for sending a key event to the window
-     * attached to the input connection's view.
+     * Provides standard implementation for sending a key event to the window attached to the input
+     * connection's view.
      */
+    @Override
     public boolean sendKeyEvent(KeyEvent event) {
         mIMM.dispatchKeyEventFromInputMethod(mTargetView, event);
         return false;
     }
 
-    /**
-     * Updates InputMethodManager with the current fullscreen mode.
-     */
+    /** Updates InputMethodManager with the current fullscreen mode. */
+    @Override
     public boolean reportFullscreenMode(boolean enabled) {
         return true;
     }
@@ -934,8 +975,7 @@
             newCursorPosition += a;
         }
         if (newCursorPosition < 0) newCursorPosition = 0;
-        if (newCursorPosition > content.length())
-            newCursorPosition = content.length();
+        if (newCursorPosition > content.length()) newCursorPosition = content.length();
         Selection.setSelection(content, newCursorPosition);
 
         content.replace(a, b, text);
@@ -950,11 +990,16 @@
     }
 
     /**
-     * Default implementation which invokes {@link View#performReceiveContent} on the target
-     * view if the view {@link View#getReceiveContentMimeTypes allows} content insertion;
-     * otherwise returns false without any side effects.
+     * Default implementation which invokes {@link View#performReceiveContent} on the target view if
+     * the view {@link View#getReceiveContentMimeTypes allows} content insertion; otherwise returns
+     * false without any side effects.
      */
+    @Override
     public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+        if (mTargetView == null) {
+            return false;
+        }
+
         ClipDescription description = inputContentInfo.getDescription();
         if (mTargetView.getReceiveContentMimeTypes() == null) {
             if (DEBUG) {
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 50dcca64..664aeee 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -98,6 +98,18 @@
             native_getValues(mNativeObject, array);
         }
 
+        /**
+         * Combines contained values into a smaller array by aggregating them
+         * according to an index map.
+         */
+        public boolean combineValues(long[] array, int[] indexMap) {
+            if (indexMap.length != mLength) {
+                throw new IllegalArgumentException(
+                        "Wrong index map size " + indexMap.length + ", expected " + mLength);
+            }
+            return native_combineValues(mNativeObject, array, indexMap);
+        }
+
         @Override
         public String toString() {
             final long[] array = new long[mLength];
@@ -116,6 +128,10 @@
 
         @FastNative
         private native void native_getValues(long nativeObject, long[] array);
+
+        @FastNative
+        private native boolean native_combineValues(long nativeObject, long[] array,
+                int[] indexMap);
     }
 
     private static final NativeAllocationRegistry sRegistry =
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index ea5f0b2..b1e7d15 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -1001,24 +1001,16 @@
     }
 
     /**
-     * This will enable jdwp by default for all apps. It is OK to cache this property
-     * because we expect to reboot the system whenever this property changes
-     */
-    private static final boolean ENABLE_JDWP = SystemProperties.get(
-                          "persist.debuggable.dalvik.vm.jdwp.enabled").equals("1");
-
-    /**
      * Applies debugger system properties to the zygote arguments.
      *
-     * For eng builds all apps are debuggable. On userdebug and user builds
-     * if persist.debuggable.dalvik.vm.jdwp.enabled is 1 all apps are
-     * debuggable. Otherwise, the debugger state is specified via the
-     * "--enable-jdwp" flag in the spawn request.
+     * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
+     * the debugger state is specified via the "--enable-jdwp" flag
+     * in the spawn request.
      *
      * @param args non-null; zygote spawner args
      */
     static void applyDebuggerSystemProperty(ZygoteArguments args) {
-        if (Build.IS_ENG || ENABLE_JDWP) {
+        if (RoSystemProperties.DEBUGGABLE) {
             args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
         }
     }
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index f24c666..746f88c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2179,7 +2179,7 @@
             break;
         }
 
-        nAudioMix->mCriteria.add(nCriterion);
+        nAudioMix->mCriteria.push_back(nCriterion);
         env->DeleteLocalRef(jCriterion);
     }
 
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 5b946d5..a95b6e3 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -244,6 +244,38 @@
     std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get());
 }
 
+static jboolean native_combineValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr,
+                                                        jlongArray jarray, jintArray jindexMap) {
+    std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
+    ScopedLongArrayRW scopedArray(env, jarray);
+    ScopedIntArrayRO scopedIndexMap(env, jindexMap);
+
+    const uint64_t *data = vector->data();
+    uint64_t *array = reinterpret_cast<uint64_t *>(scopedArray.get());
+    const uint8_t size = scopedArray.size();
+
+    for (int i = 0; i < size; i++) {
+        array[i] = 0;
+    }
+
+    bool nonZero = false;
+    for (int i = 0; i < vector->size(); i++) {
+        jint index = scopedIndexMap[i];
+        if (index < 0 || index >= size) {
+            jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
+                                 "Index %d is out of bounds: [0, %d]", index, size - 1);
+            return false;
+        }
+
+        if (data[i] != 0L) {
+            array[index] += data[i];
+            nonZero = true;
+        }
+    }
+
+    return nonZero;
+}
+
 static const JNINativeMethod g_LongArrayContainer_methods[] = {
         // @CriticalNative
         {"native_init", "(I)J", (void *)native_init_LongArrayContainer},
@@ -253,6 +285,8 @@
         {"native_setValues", "(J[J)V", (void *)native_setValues_LongArrayContainer},
         // @FastNative
         {"native_getValues", "(J[J)V", (void *)native_getValues_LongArrayContainer},
+        // @FastNative
+        {"native_combineValues", "(J[J[I)Z", (void *)native_combineValues_LongArrayContainer},
 };
 
 int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv *env) {
diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml
new file mode 100644
index 0000000..8ed58f3
--- /dev/null
+++ b/core/res/res/values/config_device_idle.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds.  Do not translate.
+
+     NOTE: The naming convention is "config_camelCaseValue". Some legacy
+     entries do not follow the convention, but all new entries should. -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Default for DeviceIdleController.Constants.FLEX_TIME_SHORT -->
+    <integer name="device_idle_flex_time_short_ms">60000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT -->
+    <integer name="device_idle_light_after_inactive_to_ms">180000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_TIMEOUT -->
+    <integer name="device_idle_light_idle_to_ms">300000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FACTOR -->
+    <item name="device_idle_light_idle_factor" format="float" type="integer">2.0</item>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_MAX_IDLE_TIMEOUT -->
+    <integer name="device_idle_light_max_idle_to_ms">900000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET -->
+    <integer name="device_idle_light_idle_maintenance_min_budget_ms">60000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET -->
+    <integer name="device_idle_light_idle_maintenance_max_budget_ms">300000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MIN_LIGHT_MAINTENANCE_TIME -->
+    <integer name="device_idle_min_light_maintenance_time_ms">5000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MIN_DEEP_MAINTENANCE_TIME -->
+    <integer name="device_idle_min_deep_maintenance_time_ms">30000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.INACTIVE_TIMEOUT -->
+    <integer name="device_idle_inactive_to_ms">1800000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.SENSING_TIMEOUT -->
+    <integer name="device_idle_sensing_to_ms">240000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LOCATING_TIMEOUT -->
+    <integer name="device_idle_locating_to_ms">30000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.LOCATION_ACCURACY -->
+    <item name="device_idle_location_accuracy" format="float" type="integer">20.0</item>
+
+    <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT -->
+    <integer name="device_idle_motion_inactive_to_ms">600000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT_FLEX -->
+    <integer name="device_idle_motion_inactive_to_flex_ms">60000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.IDLE_AFTER_INACTIVE_TIMEOUT -->
+    <integer name="device_idle_idle_after_inactive_to_ms">1800000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_TIMEOUT -->
+    <integer name="device_idle_idle_pending_to_ms">300000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MAX_IDLE_PENDING_TIMEOUT -->
+    <integer name="device_idle_max_idle_pending_to_ms">600000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_FACTOR -->
+    <item name="device_idle_idle_pending_factor" format="float" type="integer">2.0</item>
+
+    <!-- Default for DeviceIdleController.Constants.QUICK_DOZE_DELAY_TIMEOUT -->
+    <integer name="device_idle_quick_doze_delay_to_ms">60000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.IDLE_TIMEOUT -->
+    <integer name="device_idle_idle_to_ms">3600000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MAX_IDLE_TIMEOUT -->
+    <integer name="device_idle_max_idle_to_ms">21600000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.IDLE_FACTOR -->
+    <item name="device_idle_idle_factor" format="float" type="integer">2.0</item>
+
+    <!-- Default for DeviceIdleController.Constants.MIN_TIME_TO_ALARM -->
+    <integer name="device_idle_min_time_to_alarm_ms">3600000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS -->
+    <integer name="device_idle_max_temp_app_allowlist_duration_ms">300000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.MMS_TEMP_APP_ALLOWLIST_DURATION_MS -->
+    <integer name="device_idle_mms_temp_app_allowlist_duration_ms">60000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.SMS_TEMP_APP_ALLOWLIST_DURATION_MS -->
+    <integer name="device_idle_sms_temp_app_allowlist_duration_ms">20000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.NOTIFICATION_ALLOWLIST_DURATION_MS -->
+    <integer name="device_idle_notification_allowlist_duration_ms">30000</integer>
+
+    <!-- Default for DeviceIdleController.Constants.WAIT_FOR_UNLOCK -->
+    <bool name="device_idle_wait_for_unlock">true</bool>
+
+    <!-- Default for DeviceIdleController.Constants.PRE_IDLE_FACTOR_LONG -->
+    <item name="device_idle_pre_idle_factor_long" format="float" type="integer">1.67</item>
+
+    <!-- Default for DeviceIdleController.Constants.PRE_IDLE_FACTOR_SHORT -->
+    <item name="device_idle_pre_idle_factor_short" format="float" type="integer">0.33</item>
+
+    <!-- Default for DeviceIdleController.Constants.USE_WINDOW_ALARMS -->
+    <bool name="device_idle_use_window_alarms">true</bool>
+</resources>
+
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 1327d96..ea2b988 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -17,12 +17,6 @@
 <resources>
     <!-- This file defines Android telephony related resources -->
 
-    <!-- Whether force disabling telephony new data stack or not.
-         This flag and the old data stack code will be deleted in Android 14.
-    -->
-    <bool name="config_force_disable_telephony_new_data_stack">false</bool>
-    <java-symbol type="bool" name="config_force_disable_telephony_new_data_stack" />
-
     <!-- Configure tcp buffer sizes per network type in the form:
          network-type:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a535c50..7e8423a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4414,6 +4414,40 @@
 
   <java-symbol type="array" name="config_notificationMsgPkgsAllowedAsConvos" />
 
+  <!-- To config device idle -->
+  <java-symbol type="integer" name="device_idle_flex_time_short_ms" />
+  <java-symbol type="integer" name="device_idle_light_after_inactive_to_ms" />
+  <java-symbol type="integer" name="device_idle_light_idle_to_ms" />
+  <java-symbol type="integer" name="device_idle_light_idle_factor" />
+  <java-symbol type="integer" name="device_idle_light_max_idle_to_ms" />
+  <java-symbol type="integer" name="device_idle_light_idle_maintenance_min_budget_ms" />
+  <java-symbol type="integer" name="device_idle_light_idle_maintenance_max_budget_ms" />
+  <java-symbol type="integer" name="device_idle_min_light_maintenance_time_ms" />
+  <java-symbol type="integer" name="device_idle_min_deep_maintenance_time_ms" />
+  <java-symbol type="integer" name="device_idle_inactive_to_ms" />
+  <java-symbol type="integer" name="device_idle_sensing_to_ms" />
+  <java-symbol type="integer" name="device_idle_locating_to_ms" />
+  <java-symbol type="integer" name="device_idle_location_accuracy" />
+  <java-symbol type="integer" name="device_idle_motion_inactive_to_ms" />
+  <java-symbol type="integer" name="device_idle_motion_inactive_to_flex_ms" />
+  <java-symbol type="integer" name="device_idle_idle_after_inactive_to_ms" />
+  <java-symbol type="integer" name="device_idle_idle_pending_to_ms" />
+  <java-symbol type="integer" name="device_idle_max_idle_pending_to_ms" />
+  <java-symbol type="integer" name="device_idle_idle_pending_factor" />
+  <java-symbol type="integer" name="device_idle_quick_doze_delay_to_ms" />
+  <java-symbol type="integer" name="device_idle_idle_to_ms" />
+  <java-symbol type="integer" name="device_idle_max_idle_to_ms" />
+  <java-symbol type="integer" name="device_idle_idle_factor" />
+  <java-symbol type="integer" name="device_idle_min_time_to_alarm_ms" />
+  <java-symbol type="integer" name="device_idle_max_temp_app_allowlist_duration_ms" />
+  <java-symbol type="integer" name="device_idle_mms_temp_app_allowlist_duration_ms" />
+  <java-symbol type="integer" name="device_idle_sms_temp_app_allowlist_duration_ms" />
+  <java-symbol type="integer" name="device_idle_notification_allowlist_duration_ms" />
+  <java-symbol type="bool" name="device_idle_wait_for_unlock" />
+  <java-symbol type="integer" name="device_idle_pre_idle_factor_long" />
+  <java-symbol type="integer" name="device_idle_pre_idle_factor_short" />
+  <java-symbol type="bool" name="device_idle_use_window_alarms" />
+
   <!-- Binder heavy hitter watcher configs -->
   <java-symbol type="bool" name="config_defaultBinderHeavyHitterWatcherEnabled" />
   <java-symbol type="integer" name="config_defaultBinderHeavyHitterWatcherBatchSize" />
diff --git a/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java b/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java
new file mode 100644
index 0000000..7248983
--- /dev/null
+++ b/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ddm;
+
+import static android.ddm.DdmHandleViewDebug.deserializeMethodParameters;
+import static android.ddm.DdmHandleViewDebug.serializeReturnValue;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.ddm.DdmHandleViewDebug.ViewMethodInvocationSerializationException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public final class DdmHandleViewDebugTest {
+    // true
+    private static final byte[] SERIALIZED_BOOLEAN_TRUE = {0x00, 0x5A, 1};
+
+    @Test
+    public void serializeReturnValue_booleanTrue() throws Exception {
+        assertArrayEquals(SERIALIZED_BOOLEAN_TRUE, serializeReturnValue(boolean.class, true));
+    }
+
+    @Test
+    public void deserializeMethodParameters_booleanTrue() throws Exception {
+        expectDeserializedArgument(boolean.class, true, SERIALIZED_BOOLEAN_TRUE);
+    }
+
+    // false
+    private static final byte[] SERIALIZED_BOOLEAN_FALSE = {0x00, 0x5A, 0};
+
+    @Test
+    public void serializeReturnValue_booleanFalse() throws Exception {
+        assertArrayEquals(SERIALIZED_BOOLEAN_FALSE, serializeReturnValue(boolean.class, false));
+    }
+
+    @Test
+    public void deserializeMethodParameters_booleanFalse() throws Exception {
+        expectDeserializedArgument(boolean.class, false, SERIALIZED_BOOLEAN_FALSE);
+    }
+
+    // (byte) 42
+    private static final byte[] SERIALIZED_BYTE = {0x00, 0x42, 42};
+
+    @Test
+    public void serializeReturnValue_byte() throws Exception {
+        assertArrayEquals(SERIALIZED_BYTE, serializeReturnValue(byte.class, (byte) 42));
+    }
+
+    @Test
+    public void deserializeMethodParameters_byte() throws Exception {
+        expectDeserializedArgument(byte.class, (byte) 42, SERIALIZED_BYTE);
+    }
+
+    // '\u1122'
+    private static final byte[] SERIALIZED_CHAR = {0x00, 0x43, 0x11, 0x22};
+
+    @Test
+    public void serializeReturnValue_char() throws Exception {
+        assertArrayEquals(SERIALIZED_CHAR, serializeReturnValue(char.class, '\u1122'));
+    }
+
+    @Test
+    public void deserializeMethodParameters_char() throws Exception {
+        expectDeserializedArgument(char.class, '\u1122', SERIALIZED_CHAR);
+    }
+
+    // (short) 0x1011
+    private static final byte[] SERIALIZED_SHORT = {0x00, 0x53, 0x10, 0x11};
+
+    @Test
+    public void serializeReturnValue_short() throws Exception {
+        assertArrayEquals(SERIALIZED_SHORT,
+                serializeReturnValue(short.class, (short) 0x1011));
+    }
+
+    @Test
+    public void deserializeMethodParameters_short() throws Exception {
+        expectDeserializedArgument(short.class, (short) 0x1011, SERIALIZED_SHORT);
+    }
+
+    // 0x11223344
+    private static final byte[] SERIALIZED_INT = {0x00, 0x49, 0x11, 0x22, 0x33, 0x44};
+
+    @Test
+    public void serializeReturnValue_int() throws Exception {
+        assertArrayEquals(SERIALIZED_INT,
+                serializeReturnValue(int.class, 0x11223344));
+    }
+
+    @Test
+    public void deserializeMethodParameters_int() throws Exception {
+        expectDeserializedArgument(int.class, 0x11223344, SERIALIZED_INT);
+    }
+
+    // 0x0011223344556677L
+    private static final byte[] SERIALIZED_LONG =
+            {0x00, 0x4a, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+
+    @Test
+    public void serializeReturnValue_long() throws Exception {
+        assertArrayEquals(SERIALIZED_LONG,
+                serializeReturnValue(long.class, 0x0011223344556677L));
+    }
+
+    @Test
+    public void deserializeMethodParameters_long() throws Exception {
+        expectDeserializedArgument(long.class, 0x0011223344556677L, SERIALIZED_LONG);
+    }
+
+    // 3.141d
+    private static final byte[] SERIALIZED_DOUBLE =
+            {0x00, 0x44, (byte) 0x40, (byte) 0x09, (byte) 0x20, (byte) 0xc4, (byte) 0x9b,
+                    (byte) 0xa5, (byte) 0xe3, (byte) 0x54};
+
+    @Test
+    public void serializeReturnValue_double() throws Exception {
+        assertArrayEquals(
+                SERIALIZED_DOUBLE,
+                serializeReturnValue(double.class, 3.141d));
+    }
+
+    @Test
+    public void deserializeMethodParameters_double() throws Exception {
+        expectDeserializedArgument(double.class, 3.141d, SERIALIZED_DOUBLE);
+    }
+
+    // 3.141f
+    private static final byte[] SERIALIZED_FLOAT =
+            {0x00, 0x46, (byte) 0x40, (byte) 0x49, (byte) 0x06, (byte) 0x25};
+
+    @Test
+    public void serializeReturnValue_float() throws Exception {
+        assertArrayEquals(SERIALIZED_FLOAT,
+                serializeReturnValue(float.class, 3.141f));
+    }
+
+    @Test
+    public void deserializeMethodParameters_float() throws Exception {
+        expectDeserializedArgument(float.class, 3.141f, SERIALIZED_FLOAT);
+    }
+
+    // "foo"
+    private static final byte[] SERIALIZED_ASCII_STRING = {0x00, 0x52, 0, 3, 0x66, 0x6f, 0x6f};
+
+    @Test
+    public void serializeReturnValue_asciiString() throws Exception {
+        assertArrayEquals(SERIALIZED_ASCII_STRING,
+                serializeReturnValue(String.class, "foo"));
+    }
+
+    @Test
+    public void deserializeMethodParameters_asciiString() throws Exception {
+        expectDeserializedArgument(String.class, "foo", SERIALIZED_ASCII_STRING);
+    }
+
+    // "\u1122"
+    private static final byte[] SERIALIZED_NON_ASCII_STRING =
+            {0x00, 0x52, 0, 3, (byte) 0xe1, (byte) 0x84, (byte) 0xa2};
+
+    @Test
+    public void serializeReturnValue_nonAsciiString_encodesAsUtf8() throws Exception {
+        assertArrayEquals(SERIALIZED_NON_ASCII_STRING,
+                serializeReturnValue(String.class, "\u1122"));
+    }
+
+    @Test
+    public void deserializeMethodParameters_decodesFromUtf8() throws Exception {
+        expectDeserializedArgument(String.class, "\u1122", SERIALIZED_NON_ASCII_STRING);
+    }
+
+    // ""
+    private static final byte[] SERIALIZED_EMPTY_STRING = {0x00, 0x52, 0, 0};
+
+    @Test
+    public void serializeReturnValue_emptyString() throws Exception {
+        assertArrayEquals(SERIALIZED_EMPTY_STRING, serializeReturnValue(String.class, ""));
+    }
+
+    @Test
+    public void deserializeMethodParameters_emptyString() throws Exception {
+        expectDeserializedArgument(String.class, "", SERIALIZED_EMPTY_STRING);
+    }
+
+    @Test
+    public void serializeReturnValue_nullString_encodesAsEmptyString() throws Exception {
+        assertArrayEquals(new byte[]{0x00, 0x52, 0, 0}, serializeReturnValue(String.class, null));
+    }
+
+    // Illegal - string length exceeding actual bytes
+    private static final byte[] SERIALIZED_INVALID_STRING =
+            {0x00, 0x52, 0, 3, 0x66};
+
+    @Test
+    public void deserializeMethodParameters_stringPayloadMissing_throws() throws Exception {
+        Object[] args = new Object[1];
+        Class<?>[] argTypes = new Class<?>[1];
+        assertThrows(BufferUnderflowException.class,
+                () -> deserializeMethodParameters(args, argTypes,
+                        ByteBuffer.wrap(SERIALIZED_INVALID_STRING)));
+    }
+
+    @Test
+    public void serializeAndDeserialize_handlesStringsUpTo64k() throws Exception {
+        char[] chars = new char[65535];
+        Arrays.fill(chars, 'a');
+        String original = new String(chars);
+        byte[] serialized = serializeReturnValue(String.class, original);
+
+        // 2 bytes for the R signature char, 2 bytes char string byte count, 2^16-1 bytes ASCII
+        // payload
+        assertEquals(2 + 2 + 65535, serialized.length);
+
+        // length is unsigned short
+        assertArrayEquals(new byte[]{0x00, 0x52, (byte) 0xff, (byte) 0xff},
+                Arrays.copyOfRange(serialized, 0, 4));
+
+        // length of string must be interpreted as unsigned short, returning original content
+        expectDeserializedArgument(String.class, original, serialized);
+    }
+
+    private static final byte[] SERIALIZED_VOID = {0x00, 0x56};
+
+    @Test
+    public void serializeReturnValue_void() throws Exception {
+        assertArrayEquals(SERIALIZED_VOID, serializeReturnValue(void.class, null));
+    }
+
+    @Test
+    public void deserializeMethodParameters_void_throws() throws Exception {
+        Object[] args = new Object[1];
+        Class<?>[] argTypes = new Class<?>[1];
+        assertThrows(ViewMethodInvocationSerializationException.class,
+                () -> deserializeMethodParameters(args, argTypes,
+                        ByteBuffer.wrap(SERIALIZED_VOID)));
+    }
+
+    // new byte[]{}
+    private static final byte[] SERIALIZED_EMPTY_BYTE_ARRAY = {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 0};
+
+    @Test
+    public void serializeReturnValue_emptyByteArray() throws Exception {
+        assertArrayEquals(SERIALIZED_EMPTY_BYTE_ARRAY,
+                serializeReturnValue(byte[].class, new byte[]{}));
+    }
+
+    @Test
+    public void deserializeMethodParameters_emptyByteArray() throws Exception {
+        expectDeserializedArgument(byte[].class, new byte[]{}, SERIALIZED_EMPTY_BYTE_ARRAY);
+    }
+
+    // new byte[]{0, 42}
+    private static final byte[] SERIALIZED_SIMPLE_BYTE_ARRAY =
+            {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 2, 0, 42};
+
+    @Test
+    public void serializeReturnValue_byteArray() throws Exception {
+        assertArrayEquals(SERIALIZED_SIMPLE_BYTE_ARRAY,
+                serializeReturnValue(byte[].class, new byte[]{0, 42}));
+    }
+
+    @Test
+    public void deserializeMethodParameters_byteArray() throws Exception {
+        expectDeserializedArgument(byte[].class, new byte[]{0, 42}, SERIALIZED_SIMPLE_BYTE_ARRAY);
+    }
+
+    @Test
+    public void serializeReturnValue_largeByteArray_encodesSizeCorrectly() throws Exception {
+        byte[] result = serializeReturnValue(byte[].class, new byte[0x012233]);
+        // 2 bytes for the each [Z signature char, 4 bytes int array length, 0x012233 bytes payload
+        assertEquals(2 + 2 + 4 + 74291, result.length);
+
+        assertArrayEquals(new byte[]{0x00, 0x5b, 0x00, 0x42, 0x00, 0x01, 0x22, 0x33},
+                Arrays.copyOfRange(result, 0, 8));
+    }
+
+    // Illegal - declared size exceeds remaining buffer length
+    private static final byte[] SERIALIZED_INVALID_BYTE_ARRAY =
+            {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 3, 0, 42};
+
+    @Test
+    public void deserializeMethodParameters_sizeExceedsBuffer_throws() throws Exception {
+        Object[] args = new Object[1];
+        Class<?>[] argTypes = new Class<?>[1];
+        assertThrows(BufferUnderflowException.class,
+                () -> deserializeMethodParameters(args, argTypes,
+                        ByteBuffer.wrap(SERIALIZED_INVALID_BYTE_ARRAY)));
+    }
+
+    // new int[]{}
+    private static final byte[] SERIALIZED_EMPTY_INT_ARRAY = {0x00, 0x5b, 0x00, 0x49, 0, 0, 0, 0};
+
+    @Test
+    public void serializeReturnValue_nonByteArrayType_throws() throws Exception {
+        assertThrows(ViewMethodInvocationSerializationException.class,
+                () -> serializeReturnValue(int[].class, 42));
+    }
+
+    @Test
+    public void deserializeMethodParameters_nonByteArrayType_throws() throws Exception {
+        Object[] args = new Object[1];
+        Class<?>[] argTypes = new Class<?>[1];
+        assertThrows(ViewMethodInvocationSerializationException.class,
+                () -> deserializeMethodParameters(args, argTypes,
+                        ByteBuffer.wrap(SERIALIZED_EMPTY_INT_ARRAY)));
+    }
+
+    // new byte[]{0, 42}
+    private static final byte[] SERIALIZED_MULTIPLE_PARAMETERS =
+            {0x00, 0x42, 42, 0x00, 0x5A, 1};
+
+    @Test
+    public void deserializeMethodParameters_multipleParameters() throws Exception {
+        expectDeserializedArguments(new Class[]{byte.class, boolean.class},
+                new Object[]{(byte) 42, true}, SERIALIZED_MULTIPLE_PARAMETERS);
+    }
+
+    // Illegal - type 'X'
+    private static final byte[] SERIALIZED_INVALID_UNKNOWN_TYPE = {0x00, 0x58};
+
+    @Test
+    public void deserializeMethodParameters_unknownType_throws() throws Exception {
+        Object[] args = new Object[1];
+        Class<?>[] argTypes = new Class<?>[1];
+        assertThrows(ViewMethodInvocationSerializationException.class,
+                () -> deserializeMethodParameters(args, argTypes,
+                        ByteBuffer.wrap(SERIALIZED_INVALID_UNKNOWN_TYPE)));
+    }
+
+    @Test
+    public void deserializeMethodParameters_noArgumentsEmptyPacket_isNoop() throws Exception {
+        Object[] args = new Object[0];
+        Class<?>[] argTypes = new Class<?>[0];
+        deserializeMethodParameters(args, argTypes, ByteBuffer.wrap(new byte[0]));
+    }
+
+    @Test
+    public void deserializeMethodParameters_withArgumentsEmptyPacket_throws() throws Exception {
+        Object[] args = new Object[1];
+        Class<?>[] argTypes = new Class<?>[1];
+        assertThrows(BufferUnderflowException.class,
+                () -> deserializeMethodParameters(args, argTypes, ByteBuffer.wrap(new byte[0])));
+    }
+
+    private static void expectDeserializedArgument(Class<?> expectedType, Object expectedValue,
+            byte[] argumentBuffer) throws Exception {
+        expectDeserializedArguments(new Class[]{expectedType}, new Object[]{expectedValue},
+                argumentBuffer);
+    }
+
+    private static void expectDeserializedArguments(Class<?>[] expectedTypes,
+            Object[] expectedValues, byte[] argumentBuffer) throws Exception {
+        final int argCount = expectedTypes.length;
+        assertEquals("test helper not used correctly", argCount, expectedValues.length);
+        Object[] actualArgs = new Object[argCount];
+        Class<?>[] actualArgTypes = new Class<?>[argCount];
+
+        ByteBuffer buffer = ByteBuffer.wrap(argumentBuffer);
+        deserializeMethodParameters(actualArgs, actualArgTypes, buffer);
+
+        for (int i = 0; i < argCount; i++) {
+            String context = "argument " + i;
+            assertEquals(context, expectedTypes[i], actualArgTypes[i]);
+            if (byte[].class.equals(expectedTypes[i])) {
+                assertArrayEquals((byte[]) expectedValues[i], (byte[]) actualArgs[i]);
+            } else {
+                assertEquals(expectedValues[i], actualArgs[i]);
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java
new file mode 100644
index 0000000..42629ba
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
+import android.net.Uri;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ConditionTest {
+    private static final String CLASS = "android.service.notification.Condition";
+
+    @Test
+    public void testLongFields_inConstructors() {
+        String longString = Strings.repeat("A", 65536);
+        Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
+
+        // Confirm strings are truncated via short constructor
+        Condition cond1 = new Condition(longUri, longString, Condition.STATE_TRUE);
+
+        assertEquals(Condition.MAX_STRING_LENGTH, cond1.id.toString().length());
+        assertEquals(Condition.MAX_STRING_LENGTH, cond1.summary.length());
+
+        // Confirm strings are truncated via long constructor
+        Condition cond2 = new Condition(longUri, longString, longString, longString,
+                -1, Condition.STATE_TRUE, Condition.FLAG_RELEVANT_ALWAYS);
+
+        assertEquals(Condition.MAX_STRING_LENGTH, cond2.id.toString().length());
+        assertEquals(Condition.MAX_STRING_LENGTH, cond2.summary.length());
+        assertEquals(Condition.MAX_STRING_LENGTH, cond2.line1.length());
+        assertEquals(Condition.MAX_STRING_LENGTH, cond2.line2.length());
+    }
+
+    @Test
+    public void testLongFields_viaParcel() {
+        // Set fields via reflection to force them to be long, then parcel and unparcel to make sure
+        // it gets truncated upon unparcelling.
+        Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder",
+                Condition.STATE_TRUE);
+
+        try {
+            String longString = Strings.repeat("A", 65536);
+            Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
+            Field id = Class.forName(CLASS).getDeclaredField("id");
+            id.setAccessible(true);
+            id.set(cond, longUri);
+            Field summary = Class.forName(CLASS).getDeclaredField("summary");
+            summary.setAccessible(true);
+            summary.set(cond, longString);
+            Field line1 = Class.forName(CLASS).getDeclaredField("line1");
+            line1.setAccessible(true);
+            line1.set(cond, longString);
+            Field line2 = Class.forName(CLASS).getDeclaredField("line2");
+            line2.setAccessible(true);
+            line2.set(cond, longString);
+        } catch (NoSuchFieldException e) {
+            fail(e.toString());
+        } catch (ClassNotFoundException e) {
+            fail(e.toString());
+        } catch (IllegalAccessException e) {
+            fail(e.toString());
+        }
+
+        Parcel parcel = Parcel.obtain();
+        cond.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        Condition fromParcel = new Condition(parcel);
+        assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.id.toString().length());
+        assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.summary.length());
+        assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line1.length());
+        assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line2.length());
+    }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java b/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java
new file mode 100644
index 0000000..2bb5abe
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BaseInputConnectionTest {
+    private static final int[] CURSOR_CAPS_MODES =
+            new int[] {
+                InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS,
+                InputType.TYPE_TEXT_FLAG_CAP_WORDS,
+                InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
+            };
+
+    private BaseInputConnection mBaseInputConnection;
+    private Editable mEditable;
+    private View mMockView;
+
+    @Before
+    public void setUp() throws Exception {
+        mMockView = new View(InstrumentationRegistry.getInstrumentation().getContext());
+        mBaseInputConnection = new BaseInputConnection(mMockView, /*fullEditor=*/ true);
+        mEditable = mBaseInputConnection.getEditable();
+        verifyContent("", 0, 0, -1, -1);
+    }
+
+    @Test
+    public void testCommitText_toEditorWithoutSelectionAndComposing() {
+        // before commit: "|"
+        // after commit: "text1|"
+        assertThat(mBaseInputConnection.commitText("text1", 1)).isTrue();
+        verifyContent("text1", 5, 5, -1, -1);
+
+        // before commit: "text1|"
+        // after commit: "text1text2|"
+        assertThat(mBaseInputConnection.commitText("text2", "text1".length())).isTrue();
+        verifyContent("text1text2", 10, 10, -1, -1);
+
+        // before commit: "text1text2|"
+        // after commit: "text1text2text3|"
+        assertThat(mBaseInputConnection.commitText("text3", 100)).isTrue();
+        verifyContent("text1text2text3", 15, 15, -1, -1);
+
+        // before commit: "text1text2text3|"
+        // after commit: "text1text2text3text4|"
+        // BUG(b/21476564): this behavior is inconsistent with API description.
+        assertThat(mBaseInputConnection.commitText("text4", 0)).isTrue();
+        verifyContent("text1text2text3text4", 20, 20, -1, -1);
+
+        // before commit: "text1text2text3text4|"
+        // after commit: "text1text2text3text|4text5"
+        assertThat(mBaseInputConnection.commitText("text5", -1)).isTrue();
+        verifyContent("text1text2text3text4text5", 19, 19, -1, -1);
+
+        // before commit: "text1text2text3text|4text5"
+        // after commit: "text1text2text3te|xttext64text5"
+        assertThat(mBaseInputConnection.commitText("text6", -2)).isTrue();
+        verifyContent("text1text2text3texttext64text5", 17, 17, -1, -1);
+
+        // before commit: "text1text2text3te|xttext64text5"
+        // after commit: "|text1text2text3tetext7xttext64text5"
+        assertThat(mBaseInputConnection.commitText("text7", -100)).isTrue();
+        verifyContent("text1text2text3tetext7xttext64text5", 0, 0, -1, -1);
+    }
+
+    @Test
+    public void testCommitText_toEditorWithSelection() {
+        // before commit: "123|456|789"
+        // before commit: "123text|789"
+        prepareContent("123456789", 3, 6, -1, -1);
+        assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+        verifyContent("123text789", 7, 7, -1, -1);
+
+        // before commit: "|123|"
+        // before commit: "|text"
+        prepareContent("123", 0, 3, -1, -1);
+        assertThat(mBaseInputConnection.commitText("text", 0)).isTrue();
+        verifyContent("text", 0, 0, -1, -1);
+    }
+
+    @Test
+    public void testCommitText_toEditorWithComposing() {
+        // before commit: "123456|789"
+        //                    ---
+        // before commit: "123text|789"
+        prepareContent("123456789", 6, 6, 3, 6);
+        assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+        verifyContent("123text789", 7, 7, -1, -1);
+
+        // before commit: "123456789|"
+        //                    ---
+        // before commit: "123text|789"
+        prepareContent("123456789", 6, 6, 3, 6);
+        assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+        verifyContent("123text789", 7, 7, -1, -1);
+
+        // before commit: "|123456789|"
+        //                     ---
+        // before commit: "123text|789"
+        prepareContent("123456789", 0, 9, 3, 6);
+        assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+        verifyContent("123text789", 7, 7, -1, -1);
+    }
+
+    @Test
+    public void deleteSurroundingText_fromEditorWithoutSelectionAndComposing() {
+        // before delete: "123456789|"
+        // after delete: "123456|"
+        prepareContent("123456789", 9, 9, -1, -1);
+        assertThat(mBaseInputConnection.deleteSurroundingText(3, 0)).isTrue();
+        verifyContent("123456", 6, 6, -1, -1);
+
+        // before delete: "123456|"
+        // after delete: "|"
+        assertThat(mBaseInputConnection.deleteSurroundingText(100, 0)).isTrue();
+        verifyContent("", 0, 0, -1, -1);
+
+        // before commit: "|123456789"
+        // after delete: "|456789"
+        prepareContent("123456789", 0, 0, -1, -1);
+        assertThat(mBaseInputConnection.deleteSurroundingText(0, 3)).isTrue();
+        verifyContent("456789", 0, 0, -1, -1);
+
+        // before delete: "|123456789"
+        // after delete: "|"
+        assertThat(mBaseInputConnection.deleteSurroundingText(0, 100)).isTrue();
+        verifyContent("", 0, 0, -1, -1);
+
+        // before delete: "123|456789"
+        // after delete: "1|789"
+        prepareContent("123456789", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.deleteSurroundingText(2, 3)).isTrue();
+        verifyContent("1789", 1, 1, -1, -1);
+    }
+
+    @Test
+    public void deleteSurroundingText_fromEditorSelectionOrComposing() {
+        // before delete: "123|456|789"
+        // before delete: "12|456|9"
+        prepareContent("123456789", 3, 6, -1, -1);
+        assertThat(mBaseInputConnection.deleteSurroundingText(1, 2)).isTrue();
+        verifyContent("124569", 2, 5, -1, -1);
+
+        // before delete: "12|456|9"
+        // before delete: "|456|"
+        assertThat(mBaseInputConnection.deleteSurroundingText(100, 100)).isTrue();
+        verifyContent("456", 0, 3, -1, -1);
+
+        // before commit: "123456|789"
+        //                    ---
+        // before commit: "1[456]|89"
+        prepareContent("123456789", 6, 6, 3, 6);
+        assertThat(mBaseInputConnection.deleteSurroundingText(2, 1)).isTrue();
+        verifyContent("145689", 4, 4, 1, 4);
+
+        // before commit: "1234|56789"
+        //                    - --
+        // before commit: "124|56|89"
+        //                   - --
+        prepareContent("123456789", 4, 4, 3, 6);
+        assertThat(mBaseInputConnection.deleteSurroundingText(1, 1)).isTrue();
+        verifyContent("1245689", 3, 3, 2, 5);
+    }
+
+    @Test
+    public void deleteSurroundingText_negativeLength_willBeIgnored() {
+        // before delete: "123|45678"
+        // after delete: "123|45678"
+        prepareContent("123456789", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.deleteSurroundingText(-1, -1)).isTrue();
+        verifyContent("123456789", 3, 3, -1, -1);
+
+        // before delete: "123|45678"
+        // after delete: "123|5678"
+        assertThat(mBaseInputConnection.deleteSurroundingText(-1, 1)).isTrue();
+        verifyContent("12356789", 3, 3, -1, -1);
+
+        // before delete: "123|45678"
+        // after delete: "12|45678"
+        prepareContent("123456789", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.deleteSurroundingText(1, -1)).isTrue();
+        verifyContent("12456789", 2, 2, -1, -1);
+    }
+
+    @Test
+    public void testFinishComposingText() {
+        // before finish composing: "123456|789"
+        //                             ---
+        // before finish composing: "123456|789"
+        prepareContent("123456789", 6, 6, 3, 6);
+        assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+        verifyContent("123456789", 6, 6, -1, -1);
+
+        // before finish composing: "123456789|"
+        //                             ---
+        // before finish composing: "123456789|"
+        prepareContent("123456789", 9, 9, 3, 6);
+        assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+        verifyContent("123456789", 9, 9, -1, -1);
+
+        // before finish composing: "|123456789|"
+        //                              ---
+        // before finish composing: "|123456789|"
+        prepareContent("123456789", 0, 9, 3, 6);
+        assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+        verifyContent("123456789", 0, 9, -1, -1);
+
+        // before finish composing: "1234|5|6789|"
+        //                           ---- - ----
+        // before finish composing: "1234|5|6789"
+        prepareContent("123456789", 4, 5, 0, 9);
+        assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+        verifyContent("123456789", 4, 5, -1, -1);
+    }
+
+    @Test
+    public void testGetCursorCapsMode() {
+        // "|"
+        prepareContent("", 0, 0, -1, -1);
+        verifyCursorCapsModeWithMode("", 0);
+
+        // Hello|
+        prepareContent("Hello", 5, 5, -1, -1);
+        verifyCursorCapsModeWithMode("Hello", 5);
+
+        // Hello. |
+        prepareContent("Hello. ", 7, 7, -1, -1);
+        verifyCursorCapsModeWithMode("Hello. ", 7);
+
+        // Hello. |Hi|
+        prepareContent("Hello. Hi", 7, 9, -1, -1);
+        verifyCursorCapsModeWithMode("Hello. Hi", 7);
+
+        // Hello. |
+        // -----
+        prepareContent("Hello. ", 7, 7, 0, 5);
+        verifyCursorCapsModeWithMode("Hello. ", 7);
+    }
+
+    private void verifyCursorCapsModeWithMode(CharSequence text, int off) {
+        for (int reqMode : CURSOR_CAPS_MODES) {
+            assertThat(mBaseInputConnection.getCursorCapsMode(reqMode))
+                    .isEqualTo(TextUtils.getCapsMode(text, off, reqMode));
+        }
+    }
+
+    @Test
+    public void testSetComposingText_toEditorWithoutSelectionAndComposing() {
+        // before set composing text: "|"
+        // after set composing text: "abc|"
+        //                            ---
+        assertThat(mBaseInputConnection.setComposingText("abc", 1)).isTrue();
+        verifyContent("abc", 3, 3, 0, 3);
+
+        // before set composing text: "abc|"
+        // after set composing text: "abcdef|"
+        //                               ---
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingText("def", 100)).isTrue();
+        verifyContent("abcdef", 6, 6, 3, 6);
+
+        // before set composing text: "abc|"
+        // after set composing text: "abcdef|"
+        //                               ---
+        // BUG(b/21476564): this behavior is inconsistent with API description.
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingText("def", 0)).isTrue();
+        verifyContent("abcdef", 6, 6, 3, 6);
+
+        // before set composing text: "abc|"
+        // after set composing text: "ab|cdef"
+        //                                ---
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingText("def", -1)).isTrue();
+        verifyContent("abcdef", 2, 2, 3, 6);
+
+        // before set composing text: "abc|"
+        // after set composing text: "|abcdef"
+        //                                ---
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingText("def", -100)).isTrue();
+        verifyContent("abcdef", 0, 0, 3, 6);
+    }
+
+    @Test
+    public void testSetComposingText_toEditorWithComposing() {
+        // before set composing text: "abc|"
+        //                             ---
+        // after set composing text: "def|"
+        //                            ---
+        prepareContent("abc", 3, 3, 0, 3);
+        assertThat(mBaseInputConnection.setComposingText("def", 1)).isTrue();
+        verifyContent("def", 3, 3, 0, 3);
+
+        // before set composing text: "abc|"
+        //                             ---
+        // after set composing text: "hijkl|"
+        //                            -----
+        assertThat(mBaseInputConnection.setComposingText("hijkl", 1)).isTrue();
+        verifyContent("hijkl", 5, 5, 0, 5);
+
+        // before set composing text: "hijkl|"
+        //                             -----
+        // after set composing text: "|mn"
+        //                             --
+        assertThat(mBaseInputConnection.setComposingText("mn", 0)).isTrue();
+        verifyContent("mn", 0, 0, 0, 2);
+
+        // before set composing text: "|mn"
+        //                              --
+        // after set composing text: "|opq"
+        //                             ---
+        assertThat(mBaseInputConnection.setComposingText("opq", -1)).isTrue();
+        verifyContent("opq", 0, 0, 0, 3);
+    }
+
+    @Test
+    public void testSetComposingText_toEditorWithSelection() {
+        // before set composing text: "|abc|"
+        // after set composing text: "defgh|"
+        //                            -----
+        prepareContent("abc", 0, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingText("defgh", 1)).isTrue();
+        verifyContent("defgh", 5, 5, 0, 5);
+
+        // before set composing text: "a|bcdef|g"
+        // after set composing text: "a|123g"
+        //                              ---
+        prepareContent("abcdefg", 1, 6, -1, -1);
+        assertThat(mBaseInputConnection.setComposingText("123", 0)).isTrue();
+        verifyContent("a123g", 1, 1, 1, 4);
+
+        // before set composing text: "a|bcdef|g"
+        //                                ---
+        // after set composing text: "ab123456|fg"
+        //                              ------
+        prepareContent("abcdefg", 1, 6, 2, 5);
+        assertThat(mBaseInputConnection.setComposingText("123456", 1)).isTrue();
+        verifyContent("ab123456fg", 8, 8, 2, 8);
+
+        // before set composing text: "a|bc"
+        //                             ----
+        // after set composing text: "|12345"
+        //                             -----
+        prepareContent("abc", 1, 1, 0, 3);
+        assertThat(mBaseInputConnection.setComposingText("12345", -1)).isTrue();
+        verifyContent("12345", 0, 0, 0, 5);
+    }
+
+    @Test
+    public void testSetComposingRegion_toEditorWithoutSelectionAndComposing() {
+        // before set composing region: "|"
+        // after set composing region: "|"
+        assertThat(mBaseInputConnection.setComposingRegion(1, 1)).isTrue();
+        verifyContent("", 0, 0, -1, -1);
+
+        // before set composing region: "abc|"
+        // after set composing region: "abc|"
+        //                              ---
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingRegion(0, 3)).isTrue();
+        verifyContent("abc", 3, 3, 0, 3);
+
+        // before set composing region: "abc|"
+        // after set composing region: "abc|"
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingRegion(1, 1)).isTrue();
+        verifyContent("abc", 3, 3, -1, -1);
+
+        // before set composing region: "abc|"
+        // after set composing region: "abc|"
+        //                               -
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingRegion(1, 2)).isTrue();
+        verifyContent("abc", 3, 3, 1, 2);
+
+        // before set composing region: "abc|"
+        // after set composing region: "abc|"
+        //                              ---
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingRegion(3, 0)).isTrue();
+        verifyContent("abc", 3, 3, 0, 3);
+
+        // before set composing region: "abc|"
+        // after set composing region: "abc|"
+        //                              ---
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingRegion(-100, 100)).isTrue();
+        verifyContent("abc", 3, 3, 0, 3);
+    }
+
+    @Test
+    public void testSetComposingRegion_toEditorWithSelection() {
+        // before set composing region: "|abc|"
+        // after set composing region: "|abc|"
+        //                               ---
+        prepareContent("abc", 0, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingRegion(0, 3)).isTrue();
+        verifyContent("abc", 0, 3, 0, 3);
+
+        // before set composing region: "ab|cd|ef"
+        // after set composing region: "ab|cd|ef"
+        //                               - -- -
+        prepareContent("abcdef", 2, 4, -1, -1);
+        assertThat(mBaseInputConnection.setComposingRegion(1, 5)).isTrue();
+        verifyContent("abcdef", 2, 4, 1, 5);
+    }
+
+    @Test
+    public void testSetComposingRegion_toEditorWithComposing() {
+        // before set composing region: "abc|"
+        //                               ---
+        // after set composing region: "abc|"
+        //                               -
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setComposingRegion(1, 2)).isTrue();
+        verifyContent("abc", 3, 3, 1, 2);
+
+        // before set composing region: "ab|cd|ef"
+        //                                  --
+        // after set composing region: "ab|cd|ef"
+        //                               - -- -
+        prepareContent("abcdef", 2, 4, 2, 4);
+        assertThat(mBaseInputConnection.setComposingRegion(1, 5)).isTrue();
+        verifyContent("abcdef", 2, 4, 1, 5);
+    }
+
+    @Test
+    public void testSetSelection_toEditorWithoutComposing() {
+        // before set selection: "|"
+        // after set selection: "|"
+        assertThat(mBaseInputConnection.setSelection(0, 0)).isTrue();
+        assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+        assertThat(mBaseInputConnection.setSelection(-1, -1)).isTrue();
+
+        // before set selection: "abc|"
+        // after set selection: "a|b|c"
+        prepareContent("abc", 3, 3, -1, -1);
+        assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+        verifyContent("abc", 1, 1, -1, -1);
+
+        // before set selection: "abcdef|"
+        // after set selection: "ab|cd|ef"
+        prepareContent("abcdef", 6, 6, -1, -1);
+        assertThat(mBaseInputConnection.setSelection(4, 2)).isTrue();
+        verifyContent("abcdef", 4, 2, -1, -1);
+
+        // before set selection: "|abc"
+        // after set selection: "|abc"
+        prepareContent("abc", 0, 0, -1, -1);
+        assertThat(mBaseInputConnection.setSelection(0, 100)).isTrue();
+        verifyContent("abc", 0, 0, -1, -1);
+
+        // before set selection: "|abc"
+        // after set selection: "ab|c"
+        prepareContent("abc", 0, 0, -1, -1);
+        assertThat(mBaseInputConnection.setSelection(2, 2)).isTrue();
+        verifyContent("abc", 2, 2, -1, -1);
+
+        // before set selection: "|abc"
+        // after set selection: "|abc"
+        prepareContent("abc", 0, 0, -1, -1);
+        assertThat(mBaseInputConnection.setSelection(-1, 2)).isTrue();
+        verifyContent("abc", 0, 0, -1, -1);
+    }
+
+    @Test
+    public void testSetSelection_toEditorWithComposing() {
+        // before set selection: "abc|"
+        //                        ---
+        // after set selection: "a|bc"
+        //                       - --
+        prepareContent("abc", 3, 3, 0, 3);
+        assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+        verifyContent("abc", 1, 1, 0, 3);
+
+        // before set selection: "abcdef|"
+        //                          ---
+        // after set selection: "|abcdef|"
+        //                          ---
+        prepareContent("abcdef", 6, 6, 2, 5);
+        assertThat(mBaseInputConnection.setSelection(0, 6)).isTrue();
+        verifyContent("abcdef", 0, 6, 2, 5);
+    }
+
+    @Test
+    public void testGetText_noStyle() {
+        // "123|456|789"
+        prepareContent("123456789", 3, 6, -1, -1);
+
+        verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(1, 0), "3");
+        verifyContentEquals(mBaseInputConnection.getTextAfterCursor(1, 0), "7");
+        verifyContentEquals(mBaseInputConnection.getSelectedText(0), "456");
+        // This falls back to default implementation in {@code InputConnection}, which always return
+        // -1 for offset.
+        assertThat(
+                        mBaseInputConnection
+                                .getSurroundingText(1, 1, 0)
+                                .isEqualTo(new SurroundingText("34567", 1, 4, -1)))
+                .isTrue();
+
+        verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(100, 0), "123");
+        verifyContentEquals(mBaseInputConnection.getTextAfterCursor(100, 0), "789");
+        assertThat(
+                        mBaseInputConnection
+                                .getSurroundingText(100, 100, 0)
+                                .isEqualTo(new SurroundingText("123456789", 3, 6, -1)))
+                .isTrue();
+
+        verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(0, 0), "");
+        verifyContentEquals(mBaseInputConnection.getTextAfterCursor(0, 0), "");
+        assertThat(
+                        mBaseInputConnection
+                                .getSurroundingText(0, 0, 0)
+                                .isEqualTo(new SurroundingText("456", 0, 3, -1)))
+                .isTrue();
+
+        int cursorCapsMode =
+                TextUtils.getCapsMode(
+                        "123456789",
+                        3,
+                        TextUtils.CAP_MODE_CHARACTERS
+                                | TextUtils.CAP_MODE_WORDS
+                                | TextUtils.CAP_MODE_SENTENCES);
+        TextSnapshot expectedTextSnapshot =
+                new TextSnapshot(
+                        new SurroundingText("123456789", 3, 6, -1), -1, -1, cursorCapsMode);
+        verifyTextSnapshotContentEquals(mBaseInputConnection.takeSnapshot(), expectedTextSnapshot);
+    }
+
+    @Test
+    public void testGetText_withStyle() {
+        // "123|456|789"
+        SpannableStringBuilder text = new SpannableStringBuilder("123456789");
+        SuggestionSpan suggestionSpanA =
+                new SuggestionSpan(Locale.US, new String[] {"a"}, SuggestionSpan.FLAG_EASY_CORRECT);
+        SuggestionSpan suggestionSpanB =
+                new SuggestionSpan(Locale.US, new String[] {"b"}, SuggestionSpan.FLAG_EASY_CORRECT);
+        SuggestionSpan suggestionSpanC =
+                new SuggestionSpan(Locale.US, new String[] {"c"}, SuggestionSpan.FLAG_EASY_CORRECT);
+        text.setSpan(suggestionSpanA, 0, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        text.setSpan(suggestionSpanB, 3, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        text.setSpan(suggestionSpanC, 6, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        prepareContent(text, 3, 6, -1, -1);
+
+        verifySpannableString(
+                mBaseInputConnection.getTextBeforeCursor(1, InputConnection.GET_TEXT_WITH_STYLES),
+                "3",
+                1,
+                new int[][] {new int[] {0, 1}},
+                new Object[] {suggestionSpanA});
+        verifySpannableString(
+                mBaseInputConnection.getTextAfterCursor(1, InputConnection.GET_TEXT_WITH_STYLES),
+                "7",
+                1,
+                new int[][] {new int[] {0, 1}},
+                new Object[] {suggestionSpanC});
+        verifySpannableString(
+                mBaseInputConnection.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES),
+                "456",
+                1,
+                new int[][] {new int[] {0, 3}},
+                new Object[] {suggestionSpanB});
+        CharSequence surroundTextString =
+                TextUtils.concat(
+                        text.subSequence(0, 3), text.subSequence(3, 6), text.subSequence(6, 9));
+        assertThat(
+                        mBaseInputConnection
+                                .getSurroundingText(100, 100, InputConnection.GET_TEXT_WITH_STYLES)
+                                .isEqualTo(new SurroundingText(surroundTextString, 3, 6, -1)))
+                .isTrue();
+
+        int cursorCapsMode =
+                TextUtils.getCapsMode(
+                        "123456789",
+                        3,
+                        TextUtils.CAP_MODE_CHARACTERS
+                                | TextUtils.CAP_MODE_WORDS
+                                | TextUtils.CAP_MODE_SENTENCES);
+        TextSnapshot expectedTextSnapshot =
+                new TextSnapshot(
+                        new SurroundingText(surroundTextString, 3, 6, -1), -1, -1, cursorCapsMode);
+        verifyTextSnapshotContentEquals(mBaseInputConnection.takeSnapshot(), expectedTextSnapshot);
+    }
+
+    private void prepareContent(
+            CharSequence text,
+            int selectionStart,
+            int selectionEnd,
+            int composingSpanStart,
+            int composingSpanEnd) {
+        mEditable.clear();
+        mEditable.append(text);
+        Selection.setSelection(mEditable, selectionStart, selectionEnd);
+        if (isValidComposingSpan(text.length(), composingSpanStart, composingSpanEnd)) {
+            BaseInputConnection.setComposingSpans(mEditable, composingSpanStart, composingSpanEnd);
+        }
+        verifyContent(text, selectionStart, selectionEnd, composingSpanStart, composingSpanEnd);
+    }
+
+    private boolean isValidComposingSpan(
+            int textLength, int composingSpanStart, int composingSpanEnd) {
+        return composingSpanStart >= 0
+                && composingSpanStart <= textLength
+                && composingSpanEnd >= 0
+                && composingSpanEnd <= textLength;
+    }
+
+    private void verifyContent(
+            CharSequence text,
+            int selectionStart,
+            int selectionEnd,
+            int composingSpanStart,
+            int composingSpanEnd) {
+        assertThat(mEditable).isNotNull();
+        verifyContentEquals(mEditable, text.toString());
+        assertThat(Selection.getSelectionStart(mEditable)).isEqualTo(selectionStart);
+        assertThat(Selection.getSelectionEnd(mEditable)).isEqualTo(selectionEnd);
+        assertThat(BaseInputConnection.getComposingSpanStart(mEditable))
+                .isEqualTo(composingSpanStart);
+        assertThat(BaseInputConnection.getComposingSpanEnd(mEditable)).isEqualTo(composingSpanEnd);
+    }
+
+    private void verifySpannableString(
+            CharSequence text,
+            String expectedString,
+            int expectedSpanSize,
+            int[][] expectedSpanRanges,
+            Object[] expectedSpans) {
+        verifyContentEquals(text, expectedString);
+        SpannableStringBuilder spannableString = new SpannableStringBuilder(text);
+        Object[] spanList = spannableString.getSpans(0, text.length(), Object.class);
+        assertThat(spanList).isNotNull();
+        assertThat(spanList).hasLength(expectedSpanSize);
+        for (int i = 0; i < expectedSpanSize; i++) {
+            assertThat(spannableString.getSpanStart(spanList[i]))
+                    .isEqualTo(expectedSpanRanges[i][0]);
+            assertThat(spannableString.getSpanEnd(spanList[i])).isEqualTo(expectedSpanRanges[i][1]);
+        }
+        for (int i = 0; i < expectedSpanSize; i++) {
+            assertThat(spanList[i]).isEqualTo(expectedSpans[i]);
+        }
+    }
+
+    private void verifyContentEquals(CharSequence text, String expectedText) {
+        assertThat(text.toString().contentEquals(expectedText)).isTrue();
+    }
+
+    private void verifyTextSnapshotContentEquals(TextSnapshot t1, TextSnapshot t2) {
+        assertThat(t1.getCompositionStart()).isEqualTo(t2.getCompositionStart());
+        assertThat(t1.getCompositionEnd()).isEqualTo(t2.getCompositionEnd());
+        assertThat(t1.getCursorCapsMode()).isEqualTo(t2.getCursorCapsMode());
+        assertThat(t1.getSurroundingText().isEqualTo(t2.getSurroundingText())).isTrue();
+    }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
index 50ce335..047f330 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
@@ -16,18 +16,21 @@
 
 package android.view.inputmethod;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.SuggestionSpan;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Locale;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SurroundingTextTest {
@@ -35,22 +38,22 @@
     @Test
     public void testSurroundingTextBasicCreation() {
         SurroundingText surroundingText1 = new SurroundingText("test", 0, 0, 0);
-        assertThat(surroundingText1.getText(), is("test"));
-        assertThat(surroundingText1.getSelectionStart(), is(0));
-        assertThat(surroundingText1.getSelectionEnd(), is(0));
-        assertThat(surroundingText1.getOffset(), is(0));
+        assertThat(surroundingText1.getText().toString()).isEqualTo("test");
+        assertThat(surroundingText1.getSelectionStart()).isEqualTo(0);
+        assertThat(surroundingText1.getSelectionEnd()).isEqualTo(0);
+        assertThat(surroundingText1.getOffset()).isEqualTo(0);
 
         SurroundingText surroundingText2 = new SurroundingText("", -1, -1, -1);
-        assertThat(surroundingText2.getText(), is(""));
-        assertThat(surroundingText2.getSelectionStart(), is(-1));
-        assertThat(surroundingText2.getSelectionEnd(), is(-1));
-        assertThat(surroundingText2.getOffset(), is(-1));
+        assertThat(surroundingText2.getText().toString()).isEmpty();
+        assertThat(surroundingText2.getSelectionStart()).isEqualTo(-1);
+        assertThat(surroundingText2.getSelectionEnd()).isEqualTo(-1);
+        assertThat(surroundingText2.getOffset()).isEqualTo(-1);
 
         SurroundingText surroundingText3 = new SurroundingText("hello", 0, 5, 0);
-        assertThat(surroundingText3.getText(), is("hello"));
-        assertThat(surroundingText3.getSelectionStart(), is(0));
-        assertThat(surroundingText3.getSelectionEnd(), is(5));
-        assertThat(surroundingText3.getOffset(), is(0));
+        assertThat(surroundingText3.getText().toString()).isEqualTo("hello");
+        assertThat(surroundingText3.getSelectionStart()).isEqualTo(0);
+        assertThat(surroundingText3.getSelectionEnd()).isEqualTo(5);
+        assertThat(surroundingText3.getOffset()).isEqualTo(0);
     }
 
     @Test
@@ -62,20 +65,73 @@
         parcel.setDataPosition(0);
         SurroundingText surroundingTextFromParcel =
                 SurroundingText.CREATOR.createFromParcel(parcel);
-        assertThat(surroundingText.getText(), is("text"));
-        assertThat(surroundingText.getSelectionStart(), is(0));
-        assertThat(surroundingText.getSelectionEnd(), is(1));
-        assertThat(surroundingText.getOffset(), is(2));
-        assertThat(surroundingTextFromParcel.getText(), is("text"));
-        assertThat(surroundingTextFromParcel.getSelectionStart(), is(0));
-        assertThat(surroundingTextFromParcel.getSelectionEnd(), is(1));
-        assertThat(surroundingTextFromParcel.getOffset(), is(2));
+        assertThat(surroundingText.getText().toString()).isEqualTo("text");
+        assertThat(surroundingText.getSelectionStart()).isEqualTo(0);
+        assertThat(surroundingText.getSelectionEnd()).isEqualTo(1);
+        assertThat(surroundingText.getOffset()).isEqualTo(2);
+        assertThat(surroundingTextFromParcel.getText().toString()).isEqualTo("text");
+        assertThat(surroundingTextFromParcel.getSelectionStart()).isEqualTo(0);
+        assertThat(surroundingTextFromParcel.getSelectionEnd()).isEqualTo(1);
+        assertThat(surroundingTextFromParcel.getOffset()).isEqualTo(2);
     }
 
     @Test
-    public void testIsEqualComparesText() {
-        final SurroundingText text1 = new SurroundingText("hello", 0, 1, 0);
-        final SurroundingText text2 = new SurroundingText("there", 0, 1, 0);
-        assertFalse(text1.isEqualTo(text2));
+    public void testIsEqualComparesText_isNotEqualTo() {
+        final SurroundingText text = new SurroundingText("hello", 0, 1, 0);
+
+        verifySurroundingTextNotEquals(text, new SurroundingText("there", 0, 1, 0));
+        verifySurroundingTextNotEquals(text, new SurroundingText("hello", 0, 1, -1));
+        verifySurroundingTextNotEquals(text, new SurroundingText("hello", 0, 0, 0));
+        verifySurroundingTextNotEquals(text, new SurroundingText("hello", 1, 1, 0));
+
+        SpannableString spannableString = new SpannableString("hello");
+        spannableString.setSpan(
+                new SuggestionSpan(
+                        Locale.US, new String[] {"Hello"}, SuggestionSpan.FLAG_EASY_CORRECT),
+                0,
+                5,
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        verifySurroundingTextNotEquals(text, new SurroundingText(spannableString, 1, 1, 0));
+    }
+
+    private void verifySurroundingTextNotEquals(SurroundingText text1, SurroundingText text2) {
+        assertThat(text1.isEqualTo(text2)).isFalse();
+        assertThat(text1).isNotEqualTo(text2);
+        assertThat(text1.equals(text2)).isFalse();
+        assertThat(text1.hashCode()).isNotEqualTo(text2.hashCode());
+        assertThat(text1 == text2).isFalse();
+    }
+
+    @Test
+    public void testIsEqualComparesText_isEqualTo() {
+        final SurroundingText text = new SurroundingText("hello", 0, 1, 0);
+
+        verifySurroundingTextEquals(
+                text,
+                new SurroundingText("hello", 0, 1, 0),
+                /*equals=*/ false,
+                /*isSameInstance=*/ false);
+        verifySurroundingTextEquals(text, text, /*equals=*/ true, /*isSameInstance=*/ true);
+    }
+
+    private void verifySurroundingTextEquals(
+            SurroundingText text1, SurroundingText text2, boolean equals, boolean isSameInstance) {
+        assertThat(text1.isEqualTo(text2)).isTrue();
+        if (equals) {
+            assertThat(text1).isEqualTo(text2);
+            assertThat(text1.equals(text2)).isTrue();
+            assertThat(text1.hashCode()).isEqualTo(text2.hashCode());
+        } else {
+            assertThat(text1).isNotEqualTo(text2);
+            assertThat(text1.equals(text2)).isFalse();
+            assertThat(text1.hashCode()).isNotEqualTo(text2.hashCode());
+        }
+        if (isSameInstance) {
+            assertThat(text1 == text2).isTrue();
+            assertThat(text1).isSameInstanceAs(text2);
+        } else {
+            assertThat(text1 == text2).isFalse();
+            assertThat(text1).isNotSameInstanceAs(text2);
+        }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index a22dd1c..516dee7 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -161,4 +161,33 @@
         assertThrows(RuntimeException.class,
                 () -> LongArrayMultiStateCounter.CREATOR.createFromParcel(parcel));
     }
+
+    @Test
+    public void combineValues() {
+        long[] values = new long[] {0, 1, 2, 3, 42};
+        LongArrayMultiStateCounter.LongArrayContainer container =
+                new LongArrayMultiStateCounter.LongArrayContainer(values.length);
+        container.setValues(values);
+
+        long[] out = new long[3];
+        int[] indexes = {2, 1, 1, 0, 0};
+        boolean nonZero = container.combineValues(out, indexes);
+        assertThat(nonZero).isTrue();
+        assertThat(out).isEqualTo(new long[]{45, 3, 0});
+
+        // All zeros
+        container.setValues(new long[]{0, 0, 0, 0, 0});
+        nonZero = container.combineValues(out, indexes);
+        assertThat(nonZero).isFalse();
+        assertThat(out).isEqualTo(new long[]{0, 0, 0});
+
+        // Index out of range
+        IndexOutOfBoundsException e1 = assertThrows(
+                IndexOutOfBoundsException.class,
+                () -> container.combineValues(out, new int[]{0, 1, -1, 0, 0}));
+        assertThat(e1.getMessage()).isEqualTo("Index -1 is out of bounds: [0, 2]");
+        IndexOutOfBoundsException e2 = assertThrows(IndexOutOfBoundsException.class,
+                () -> container.combineValues(out, new int[]{0, 1, 4, 0, 0}));
+        assertThat(e2.getMessage()).isEqualTo("Index 4 is out of bounds: [0, 2]");
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 6af3d2b..126f835 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -139,6 +139,16 @@
         }
     }
 
+    @Override
+    public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
+        // TODO: Implement this method
+    }
+
+    @Override
+    public void clearSplitAttributesCalculator() {
+        // TODO: Implement this method
+    }
+
     @NonNull
     List<EmbeddingRule> getSplitRules() {
         return mSplitRules;
@@ -1516,13 +1526,20 @@
                         .toActivityStack();
                 final ActivityStack secondaryContainer = container.getSecondaryContainer()
                         .toActivityStack();
+                final SplitAttributes.SplitType splitType = shouldShowSideBySide(container)
+                        ? new SplitAttributes.SplitType.RatioSplitType(
+                                container.getSplitRule().getSplitRatio())
+                        : new SplitAttributes.SplitType.ExpandContainersSplitType();
                 final SplitInfo splitState = new SplitInfo(primaryContainer, secondaryContainer,
                         // Splits that are not showing side-by-side are reported as having 0 split
                         // ratio, since by definition in the API the primary container occupies no
                         // width of the split when covered by the secondary.
-                        shouldShowSideBySide(container)
-                                ? container.getSplitRule().getSplitRatio()
-                                : 0.0f);
+                        // TODO(b/241042437): use v2 APIs for splitAttributes
+                        new SplitAttributes.Builder()
+                                .setSplitType(splitType)
+                                .setLayoutDirection(container.getSplitRule().getLayoutDirection())
+                                .build()
+                        );
                 splitStates.add(splitState);
             }
         }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index cdee9e3..af5d8c5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -16,7 +16,6 @@
 
 package androidx.window.extensions.embedding;
 
-import static android.graphics.Matrix.MSCALE_X;
 import static android.graphics.Matrix.MTRANS_X;
 import static android.graphics.Matrix.MTRANS_Y;
 
@@ -41,30 +40,44 @@
      */
     private static final int LAYER_NO_OVERRIDE = -1;
 
+    @NonNull
     final Animation mAnimation;
+    @NonNull
     final RemoteAnimationTarget mTarget;
+    @NonNull
     final SurfaceControl mLeash;
+    /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+    @NonNull
+    private final Rect mWholeAnimationBounds = new Rect();
 
+    @NonNull
     final Transformation mTransformation = new Transformation();
+    @NonNull
     final float[] mMatrix = new float[9];
+    @NonNull
     final float[] mVecs = new float[4];
+    @NonNull
     final Rect mRect = new Rect();
     private boolean mIsFirstFrame = true;
     private int mOverrideLayer = LAYER_NO_OVERRIDE;
 
     TaskFragmentAnimationAdapter(@NonNull Animation animation,
             @NonNull RemoteAnimationTarget target) {
-        this(animation, target, target.leash);
+        this(animation, target, target.leash, target.screenSpaceBounds);
     }
 
     /**
      * @param leash the surface to animate.
+     * @param wholeAnimationBounds  area in absolute coordinate that the animation surface shouldn't
+     *                              go beyond.
      */
     TaskFragmentAnimationAdapter(@NonNull Animation animation,
-            @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash) {
+            @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
+            @NonNull Rect wholeAnimationBounds) {
         mAnimation = animation;
         mTarget = target;
         mLeash = leash;
+        mWholeAnimationBounds.set(wholeAnimationBounds);
     }
 
     /**
@@ -94,23 +107,32 @@
 
     /** To be overridden by subclasses to adjust the animation surface change. */
     void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+        // Update the surface position and alpha.
         mTransformation.getMatrix().postTranslate(
                 mTarget.localBounds.left, mTarget.localBounds.top);
         t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
         t.setAlpha(mLeash, mTransformation.getAlpha());
-        // Get current animation position.
+
+        // Get current surface bounds in absolute coordinate.
+        // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
         final int positionX = Math.round(mMatrix[MTRANS_X]);
         final int positionY = Math.round(mMatrix[MTRANS_Y]);
-        // The exiting surface starts at position: mTarget.localBounds and moves with
-        // positionX varying. Offset our crop region by the amount we have slided so crop
-        // regions stays exactly on the original container in split.
-        final int cropOffsetX = mTarget.localBounds.left - positionX;
-        final int cropOffsetY = mTarget.localBounds.top - positionY;
-        final Rect cropRect = new Rect();
-        cropRect.set(mTarget.localBounds);
-        // Because window crop uses absolute position.
-        cropRect.offsetTo(0, 0);
-        cropRect.offset(cropOffsetX, cropOffsetY);
+        final Rect cropRect = new Rect(mTarget.screenSpaceBounds);
+        final Rect localBounds = mTarget.localBounds;
+        cropRect.offset(positionX - localBounds.left, positionY - localBounds.top);
+
+        // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+        final int offsetX = cropRect.left;
+        final int offsetY = cropRect.top;
+
+        // Intersect to make sure the animation happens within the whole animation bounds.
+        if (!cropRect.intersect(mWholeAnimationBounds)) {
+            // Hide the surface when it is outside of the animation area.
+            t.setAlpha(mLeash, 0);
+        }
+
+        // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+        cropRect.offset(-offsetX, -offsetY);
         t.setCrop(mLeash, cropRect);
     }
 
@@ -124,52 +146,6 @@
     }
 
     /**
-     * Should be used when the {@link RemoteAnimationTarget} is in split with others, and want to
-     * animate together as one. This adapter will offset the animation leash to make the animate of
-     * two windows look like a single window.
-     */
-    static class SplitAdapter extends TaskFragmentAnimationAdapter {
-        private final boolean mIsLeftHalf;
-        private final int mWholeAnimationWidth;
-
-        /**
-         * @param isLeftHalf whether this is the left half of the animation.
-         * @param wholeAnimationWidth the whole animation windows width.
-         */
-        SplitAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target,
-                boolean isLeftHalf, int wholeAnimationWidth) {
-            super(animation, target);
-            mIsLeftHalf = isLeftHalf;
-            mWholeAnimationWidth = wholeAnimationWidth;
-            if (wholeAnimationWidth == 0) {
-                throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
-            }
-        }
-
-        @Override
-        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
-            float posX = mTarget.localBounds.left;
-            final float posY = mTarget.localBounds.top;
-            // This window is half of the whole animation window. Offset left/right to make it
-            // look as one with the other half.
-            mTransformation.getMatrix().getValues(mMatrix);
-            final int targetWidth = mTarget.localBounds.width();
-            final float scaleX = mMatrix[MSCALE_X];
-            final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
-            final float curOffset = targetWidth * (1 - scaleX) / 2;
-            final float offsetDiff = totalOffset - curOffset;
-            if (mIsLeftHalf) {
-                posX += offsetDiff;
-            } else {
-                posX -= offsetDiff;
-            }
-            mTransformation.getMatrix().postTranslate(posX, posY);
-            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
-            t.setAlpha(mLeash, mTransformation.getAlpha());
-        }
-    }
-
-    /**
      * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
      * size change.
      */
@@ -177,7 +153,7 @@
 
         SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
             // Start leash is the snapshot of the starting surface.
-            super(animation, target, target.startLeash);
+            super(animation, target, target.startLeash, target.screenSpaceBounds);
         }
 
         @Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index 8af2d9c..8c416e8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -213,10 +213,10 @@
         for (RemoteAnimationTarget target : targets) {
             if (target.mode != MODE_CLOSING) {
                 openingTargets.add(target);
-                openingWholeScreenBounds.union(target.localBounds);
+                openingWholeScreenBounds.union(target.screenSpaceBounds);
             } else {
                 closingTargets.add(target);
-                closingWholeScreenBounds.union(target.localBounds);
+                closingWholeScreenBounds.union(target.screenSpaceBounds);
             }
         }
 
@@ -249,20 +249,8 @@
             @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
             @NonNull Rect wholeAnimationBounds) {
         final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
-        final Rect targetBounds = target.localBounds;
-        if (targetBounds.left == wholeAnimationBounds.left
-                && targetBounds.right != wholeAnimationBounds.right) {
-            // This is the left split of the whole animation window.
-            return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
-                    true /* isLeftHalf */, wholeAnimationBounds.width());
-        } else if (targetBounds.left != wholeAnimationBounds.left
-                && targetBounds.right == wholeAnimationBounds.right) {
-            // This is the right split of the whole animation window.
-            return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
-                    false /* isLeftHalf */, wholeAnimationBounds.width());
-        }
-        // Open/close window that fills the whole animation.
-        return new TaskFragmentAnimationAdapter(animation, target);
+        return new TaskFragmentAnimationAdapter(animation, target, target.leash,
+                wholeAnimationBounds);
     }
 
     @NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index 97d42391b..ef5ea56 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -195,7 +195,10 @@
                     ? com.android.internal.R.anim.task_fragment_open_enter
                     : com.android.internal.R.anim.task_fragment_open_exit);
         }
-        animation.initialize(target.localBounds.width(), target.localBounds.height(),
+        // Use the whole animation bounds instead of the change bounds, so that when multiple change
+        // targets are opening at the same time, the animation applied to each will be the same.
+        // Otherwise, we may see gap between the activities that are launching together.
+        animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
                 wholeAnimationBounds.width(), wholeAnimationBounds.height());
         animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         return animation;
@@ -215,7 +218,10 @@
                     ? com.android.internal.R.anim.task_fragment_close_enter
                     : com.android.internal.R.anim.task_fragment_close_exit);
         }
-        animation.initialize(target.localBounds.width(), target.localBounds.height(),
+        // Use the whole animation bounds instead of the change bounds, so that when multiple change
+        // targets are closing at the same time, the animation applied to each will be the same.
+        // Otherwise, we may see gap between the activities that are finishing together.
+        animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
                 wholeAnimationBounds.width(), wholeAnimationBounds.height());
         animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         return animation;
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index e9a1721..2c766d8 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
index cc4db93..591e347 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.activityembedding;
 
-import static android.graphics.Matrix.MSCALE_X;
 import static android.graphics.Matrix.MTRANS_X;
 import static android.graphics.Matrix.MTRANS_Y;
 
@@ -42,31 +41,45 @@
      */
     private static final int LAYER_NO_OVERRIDE = -1;
 
+    @NonNull
     final Animation mAnimation;
+    @NonNull
     final TransitionInfo.Change mChange;
+    @NonNull
     final SurfaceControl mLeash;
+    /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+    @NonNull
+    private final Rect mWholeAnimationBounds = new Rect();
 
+    @NonNull
     final Transformation mTransformation = new Transformation();
+    @NonNull
     final float[] mMatrix = new float[9];
+    @NonNull
     final float[] mVecs = new float[4];
+    @NonNull
     final Rect mRect = new Rect();
     private boolean mIsFirstFrame = true;
     private int mOverrideLayer = LAYER_NO_OVERRIDE;
 
     ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
             @NonNull TransitionInfo.Change change) {
-        this(animation, change, change.getLeash());
+        this(animation, change, change.getLeash(), change.getEndAbsBounds());
     }
 
     /**
      * @param leash the surface to animate, which is not necessary the same as
-     * {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example.
+     *              {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example.
+     * @param wholeAnimationBounds  area in absolute coordinate that the animation surface shouldn't
+     *                              go beyond.
      */
     ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
-            @NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash) {
+            @NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash,
+            @NonNull Rect wholeAnimationBounds) {
         mAnimation = animation;
         mChange = change;
         mLeash = leash;
+        mWholeAnimationBounds.set(wholeAnimationBounds);
     }
 
     /**
@@ -96,23 +109,31 @@
 
     /** To be overridden by subclasses to adjust the animation surface change. */
     void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+        // Update the surface position and alpha.
         final Point offset = mChange.getEndRelOffset();
         mTransformation.getMatrix().postTranslate(offset.x, offset.y);
         t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
         t.setAlpha(mLeash, mTransformation.getAlpha());
-        // Get current animation position.
+
+        // Get current surface bounds in absolute coordinate.
+        // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
         final int positionX = Math.round(mMatrix[MTRANS_X]);
         final int positionY = Math.round(mMatrix[MTRANS_Y]);
-        // The exiting surface starts at position: Change#getEndRelOffset() and moves with
-        // positionX varying. Offset our crop region by the amount we have slided so crop
-        // regions stays exactly on the original container in split.
-        final int cropOffsetX = offset.x - positionX;
-        final int cropOffsetY = offset.y - positionY;
-        final Rect cropRect = new Rect();
-        cropRect.set(mChange.getEndAbsBounds());
-        // Because window crop uses absolute position.
-        cropRect.offsetTo(0, 0);
-        cropRect.offset(cropOffsetX, cropOffsetY);
+        final Rect cropRect = new Rect(mChange.getEndAbsBounds());
+        cropRect.offset(positionX - offset.x, positionY - offset.y);
+
+        // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+        final int offsetX = cropRect.left;
+        final int offsetY = cropRect.top;
+
+        // Intersect to make sure the animation happens within the whole animation bounds.
+        if (!cropRect.intersect(mWholeAnimationBounds)) {
+            // Hide the surface when it is outside of the animation area.
+            t.setAlpha(mLeash, 0);
+        }
+
+        // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+        cropRect.offset(-offsetX, -offsetY);
         t.setCrop(mLeash, cropRect);
     }
 
@@ -127,53 +148,6 @@
     }
 
     /**
-     * Should be used when the {@link TransitionInfo.Change} is in split with others, and wants to
-     * animate together as one. This adapter will offset the animation leash to make the animate of
-     * two windows look like a single window.
-     */
-    static class SplitAdapter extends ActivityEmbeddingAnimationAdapter {
-        private final boolean mIsLeftHalf;
-        private final int mWholeAnimationWidth;
-
-        /**
-         * @param isLeftHalf whether this is the left half of the animation.
-         * @param wholeAnimationWidth the whole animation windows width.
-         */
-        SplitAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
-                boolean isLeftHalf, int wholeAnimationWidth) {
-            super(animation, change);
-            mIsLeftHalf = isLeftHalf;
-            mWholeAnimationWidth = wholeAnimationWidth;
-            if (wholeAnimationWidth == 0) {
-                throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
-            }
-        }
-
-        @Override
-        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
-            final Point offset = mChange.getEndRelOffset();
-            float posX = offset.x;
-            final float posY = offset.y;
-            // This window is half of the whole animation window. Offset left/right to make it
-            // look as one with the other half.
-            mTransformation.getMatrix().getValues(mMatrix);
-            final int changeWidth = mChange.getEndAbsBounds().width();
-            final float scaleX = mMatrix[MSCALE_X];
-            final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
-            final float curOffset = changeWidth * (1 - scaleX) / 2;
-            final float offsetDiff = totalOffset - curOffset;
-            if (mIsLeftHalf) {
-                posX += offsetDiff;
-            } else {
-                posX -= offsetDiff;
-            }
-            mTransformation.getMatrix().postTranslate(posX, posY);
-            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
-            t.setAlpha(mLeash, mTransformation.getAlpha());
-        }
-    }
-
-    /**
      * Should be used for the animation of the snapshot of a {@link TransitionInfo.Change} that has
      * size change.
      */
@@ -181,7 +155,7 @@
 
         SnapshotAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
                 @NonNull SurfaceControl snapshotLeash) {
-            super(animation, change, snapshotLeash);
+            super(animation, change, snapshotLeash, change.getEndAbsBounds());
         }
 
         @Override
@@ -196,7 +170,9 @@
         void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
             super.onAnimationEnd(t);
             // Remove the screenshot leash after animation is finished.
-            t.remove(mLeash);
+            if (mLeash.isValid()) {
+                t.remove(mLeash);
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 7e0795d..d88cc00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -22,9 +22,9 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.SurfaceControl;
 import android.view.animation.Animation;
@@ -40,6 +40,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.function.BiFunction;
 
 /** To run the ActivityEmbedding animations. */
@@ -169,15 +170,12 @@
         final Rect openingWholeScreenBounds = new Rect();
         final Rect closingWholeScreenBounds = new Rect();
         for (TransitionInfo.Change change : info.getChanges()) {
-            final Rect bounds = new Rect(change.getEndAbsBounds());
-            final Point offset = change.getEndRelOffset();
-            bounds.offsetTo(offset.x, offset.y);
             if (Transitions.isOpeningType(change.getMode())) {
                 openingChanges.add(change);
-                openingWholeScreenBounds.union(bounds);
+                openingWholeScreenBounds.union(change.getEndAbsBounds());
             } else {
                 closingChanges.add(change);
-                closingWholeScreenBounds.union(bounds);
+                closingWholeScreenBounds.union(change.getEndAbsBounds());
             }
         }
 
@@ -210,60 +208,73 @@
             @NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider,
             @NonNull Rect wholeAnimationBounds) {
         final Animation animation = animationProvider.apply(change, wholeAnimationBounds);
-        final Rect bounds = new Rect(change.getEndAbsBounds());
-        final Point offset = change.getEndRelOffset();
-        bounds.offsetTo(offset.x, offset.y);
-        if (bounds.left == wholeAnimationBounds.left
-                && bounds.right != wholeAnimationBounds.right) {
-            // This is the left split of the whole animation window.
-            return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
-                    true /* isLeftHalf */, wholeAnimationBounds.width());
-        } else if (bounds.left != wholeAnimationBounds.left
-                && bounds.right == wholeAnimationBounds.right) {
-            // This is the right split of the whole animation window.
-            return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
-                    false /* isLeftHalf */, wholeAnimationBounds.width());
-        }
-        // Open/close window that fills the whole animation.
-        return new ActivityEmbeddingAnimationAdapter(animation, change);
+        return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(),
+                wholeAnimationBounds);
     }
 
     @NonNull
     private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters(
             @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
         final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
+        final Set<TransitionInfo.Change> handledChanges = new ArraySet<>();
+
+        // For the first iteration, we prepare the animation for the change type windows. This is
+        // needed because there may be window that is reparented while resizing. In such case, we
+        // will do the following:
+        // 1. Capture a screenshot from the Activity surface.
+        // 2. Attach the screenshot surface to the top of TaskFragment (Activity's parent) surface.
+        // 3. Animate the TaskFragment using Activity Change info (start/end bounds).
+        // This is because the TaskFragment surface/change won't contain the Activity's before its
+        // reparent.
         for (TransitionInfo.Change change : info.getChanges()) {
-            if (change.getMode() == TRANSIT_CHANGE
-                    && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
-                // This is the window with bounds change.
-                final WindowContainerToken parentToken = change.getParent();
-                final Rect parentBounds;
-                if (parentToken != null) {
-                    TransitionInfo.Change parentChange = info.getChange(parentToken);
-                    parentBounds = parentChange != null
-                            ? parentChange.getEndAbsBounds()
-                            : change.getEndAbsBounds();
-                } else {
-                    parentBounds = change.getEndAbsBounds();
-                }
-                final Animation[] animations =
-                        mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds);
-                // Adapter for the starting screenshot leash.
-                final SurfaceControl screenshotLeash = createScreenshot(change, startTransaction);
-                if (screenshotLeash != null) {
-                    // The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
-                    adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
-                            animations[0], change, screenshotLeash));
-                } else {
-                    Log.e(TAG, "Failed to take screenshot for change=" + change);
-                }
-                // Adapter for the ending bounds changed leash.
-                adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
-                        animations[1], change));
+            if (change.getMode() != TRANSIT_CHANGE
+                    || change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
                 continue;
             }
 
-            // These are the other windows that don't have bounds change in the same transition.
+            // This is the window with bounds change.
+            handledChanges.add(change);
+            final WindowContainerToken parentToken = change.getParent();
+            TransitionInfo.Change boundsAnimationChange = change;
+            if (parentToken != null) {
+                // When the parent window is also included in the transition as an opening window,
+                // we would like to animate the parent window instead.
+                final TransitionInfo.Change parentChange = info.getChange(parentToken);
+                if (parentChange != null && Transitions.isOpeningType(parentChange.getMode())) {
+                    // We won't create a separate animation for the parent, but to animate the
+                    // parent for the child resizing.
+                    handledChanges.add(parentChange);
+                    boundsAnimationChange = parentChange;
+                }
+            }
+
+            final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
+                    boundsAnimationChange.getEndAbsBounds());
+
+            // Create a screenshot based on change, but attach it to the top of the
+            // boundsAnimationChange.
+            final SurfaceControl screenshotLeash = getOrCreateScreenshot(change,
+                    boundsAnimationChange, startTransaction);
+            if (screenshotLeash != null) {
+                // Adapter for the starting screenshot leash.
+                // The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
+                adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
+                        animations[0], change, screenshotLeash));
+            } else {
+                Log.e(TAG, "Failed to take screenshot for change=" + change);
+            }
+            // Adapter for the ending bounds changed leash.
+            adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
+                    animations[1], boundsAnimationChange));
+        }
+
+        // Handle the other windows that don't have bounds change in the same transition.
+        for (TransitionInfo.Change change : info.getChanges()) {
+            if (handledChanges.contains(change)) {
+                // Skip windows that we have already handled in the previous iteration.
+                continue;
+            }
+
             final Animation animation;
             if (!TransitionInfo.isIndependent(change, info)) {
                 // No-op if it will be covered by the changing parent window.
@@ -278,13 +289,27 @@
         return adapters;
     }
 
-    /** Takes a screenshot of the given {@link TransitionInfo.Change} surface. */
+    /**
+     * Takes a screenshot of the given {@code screenshotChange} surface if WM Core hasn't taken one.
+     * The screenshot leash should be attached to the {@code animationChange} surface which we will
+     * animate later.
+     */
     @Nullable
-    private SurfaceControl createScreenshot(@NonNull TransitionInfo.Change change,
-            @NonNull SurfaceControl.Transaction startTransaction) {
-        final Rect cropBounds = new Rect(change.getStartAbsBounds());
+    private SurfaceControl getOrCreateScreenshot(@NonNull TransitionInfo.Change screenshotChange,
+            @NonNull TransitionInfo.Change animationChange,
+            @NonNull SurfaceControl.Transaction t) {
+        final SurfaceControl screenshotLeash = screenshotChange.getSnapshot();
+        if (screenshotLeash != null) {
+            // If WM Core has already taken a screenshot, make sure it is reparented to the
+            // animation leash.
+            t.reparent(screenshotLeash, animationChange.getLeash());
+            return screenshotLeash;
+        }
+
+        // If WM Core hasn't taken a screenshot, take a screenshot now.
+        final Rect cropBounds = new Rect(screenshotChange.getStartAbsBounds());
         cropBounds.offsetTo(0, 0);
-        return ScreenshotUtils.takeScreenshot(startTransaction, change.getLeash(), cropBounds,
-                Integer.MAX_VALUE);
+        return ScreenshotUtils.takeScreenshot(t, screenshotChange.getLeash(),
+                animationChange.getLeash(), cropBounds, Integer.MAX_VALUE);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 6f06f28..ad0dddf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -185,8 +185,10 @@
         animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
                 ? R.anim.task_fragment_open_enter
                 : R.anim.task_fragment_open_exit);
-        final Rect bounds = change.getEndAbsBounds();
-        animation.initialize(bounds.width(), bounds.height(),
+        // Use the whole animation bounds instead of the change bounds, so that when multiple change
+        // targets are opening at the same time, the animation applied to each will be the same.
+        // Otherwise, we may see gap between the activities that are launching together.
+        animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
                 wholeAnimationBounds.width(), wholeAnimationBounds.height());
         animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         return animation;
@@ -203,8 +205,10 @@
         animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
                 ? R.anim.task_fragment_close_enter
                 : R.anim.task_fragment_close_exit);
-        final Rect bounds = change.getEndAbsBounds();
-        animation.initialize(bounds.width(), bounds.height(),
+        // Use the whole animation bounds instead of the change bounds, so that when multiple change
+        // targets are closing at the same time, the animation applied to each will be the same.
+        // Otherwise, we may see gap between the activities that are finishing together.
+        animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
                 wholeAnimationBounds.width(), wholeAnimationBounds.height());
         animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         return animation;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index c4bd73b..2a1bf0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -28,7 +28,7 @@
 public class ScreenshotUtils {
 
     /**
-     * Take a screenshot of the specified SurfaceControl.
+     * Takes a screenshot of the specified SurfaceControl.
      *
      * @param sc the SurfaceControl to take a screenshot of
      * @param crop the crop to use when capturing the screenshot
@@ -49,11 +49,14 @@
         SurfaceControl mScreenshot = null;
         SurfaceControl.Transaction mTransaction;
         SurfaceControl mSurfaceControl;
+        SurfaceControl mParentSurfaceControl;
         int mLayer;
 
-        BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, int layer) {
+        BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, SurfaceControl parentSc,
+                int layer) {
             mTransaction = t;
             mSurfaceControl = sc;
+            mParentSurfaceControl = parentSc;
             mLayer = layer;
         }
 
@@ -72,7 +75,7 @@
 
             mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
             mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
-            mTransaction.reparent(mScreenshot, mSurfaceControl);
+            mTransaction.reparent(mScreenshot, mParentSurfaceControl);
             mTransaction.setLayer(mScreenshot, mLayer);
             mTransaction.show(mScreenshot);
             mTransaction.apply();
@@ -80,7 +83,7 @@
     }
 
     /**
-     * Take a screenshot of the specified SurfaceControl.
+     * Takes a screenshot of the specified SurfaceControl.
      *
      * @param t the transaction used to set changes on the resulting screenshot.
      * @param sc the SurfaceControl to take a screenshot of
@@ -91,7 +94,23 @@
      */
     public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
             Rect crop, int layer) {
-        BufferConsumer consumer = new BufferConsumer(t, sc, layer);
+        return takeScreenshot(t, sc, sc /* parentSc */, crop, layer);
+    }
+
+    /**
+     * Takes a screenshot of the specified SurfaceControl.
+     *
+     * @param t the transaction used to set changes on the resulting screenshot.
+     * @param sc the SurfaceControl to take a screenshot of
+     * @param parentSc  the SurfaceControl to attach the screenshot to.
+     * @param crop the crop to use when capturing the screenshot
+     * @param layer the layer to place the screenshot
+     *
+     * @return A SurfaceControl where the screenshot will be attached, or null if failed.
+     */
+    public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
+            SurfaceControl parentSc, Rect crop, int layer) {
+        BufferConsumer consumer = new BufferConsumer(t, sc, parentSc, layer);
         captureLayer(sc, crop, consumer);
         return consumer.mScreenshot;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 4c85d20..419e62d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -561,6 +561,10 @@
         if (from == to) {
             // No animation run, still callback to stop resizing.
             mSplitLayoutHandler.onLayoutSizeChanged(this);
+
+            if (flingFinishedCallback != null) {
+                flingFinishedCallback.run();
+            }
             InteractionJankMonitorUtils.endTracing(
                     CUJ_SPLIT_SCREEN_RESIZE);
             return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 3714fe7..ecdafa9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -21,6 +21,7 @@
 import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
+import com.android.internal.logging.InstanceId;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.window.RemoteTransition;
@@ -67,34 +68,35 @@
      * Starts a shortcut in a stage.
      */
     oneway void startShortcut(String packageName, String shortcutId, int position,
-            in Bundle options, in UserHandle user) = 8;
+            in Bundle options, in UserHandle user, in InstanceId instanceId) = 8;
 
     /**
      * Starts an activity in a stage.
      */
     oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int position,
-            in Bundle options) = 9;
+            in Bundle options, in InstanceId instanceId) = 9;
 
     /**
      * Starts tasks simultaneously in one transition.
      */
     oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
             in Bundle sideOptions, int sidePosition, float splitRatio,
-            in RemoteTransition remoteTransition) = 10;
+            in RemoteTransition remoteTransition, in InstanceId instanceId) = 10;
 
     /**
      * Version of startTasks using legacy transition system.
      */
     oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
             int sideTaskId, in Bundle sideOptions, int sidePosition,
-            float splitRatio, in RemoteAnimationAdapter adapter) = 11;
+            float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11;
 
     /**
      * Starts a pair of intent and task using legacy transition system.
      */
     oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
             in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions,
-            int sidePosition, float splitRatio, in RemoteAnimationAdapter adapter) = 12;
+            int sidePosition, float splitRatio, in RemoteAnimationAdapter adapter,
+            in InstanceId instanceId) = 12;
 
     /**
      * Blocking call that notifies and gets additional split-screen targets when entering
@@ -115,5 +117,5 @@
      */
     oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, int taskId,
             in Bundle mainOptions, in Bundle sideOptions, int sidePosition, float splitRatio,
-            in RemoteAnimationAdapter adapter) = 15;
+            in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15;
 }
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 9206afb..025e559 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
@@ -387,6 +387,17 @@
         }
     }
 
+    /**
+     * See {@link #startShortcut(String, String, int, Bundle, UserHandle)}
+     * @param instanceId to be used by {@link SplitscreenEventLogger}
+     */
+    public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
+            @Nullable Bundle options, UserHandle user, @NonNull InstanceId instanceId) {
+        mStageCoordinator.getLogger().enterRequested(instanceId);
+        startShortcut(packageName, shortcutId, position, options, user);
+    }
+
+    @Override
     public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
             @Nullable Bundle options, UserHandle user) {
         IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -415,7 +426,6 @@
                 0 /* duration */, 0 /* statusBarTransitionDelay */);
         ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
         activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
-
         try {
             LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
             launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
@@ -425,6 +435,17 @@
         }
     }
 
+    /**
+     * See {@link #startIntent(PendingIntent, Intent, int, Bundle)}
+     * @param instanceId to be used by {@link SplitscreenEventLogger}
+     */
+    public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
+            @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId) {
+        mStageCoordinator.getLogger().enterRequested(instanceId);
+        startIntent(intent, fillInIntent, position, options);
+    }
+
+    @Override
     public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
             @SplitPosition int position, @Nullable Bundle options) {
         if (fillInIntent == null) {
@@ -446,7 +467,6 @@
             mStageCoordinator.startIntentLegacy(intent, fillInIntent, position, options);
             return;
         }
-
         mStageCoordinator.startIntent(intent, fillInIntent, position, options);
     }
 
@@ -772,60 +792,64 @@
         @Override
         public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
                 int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
-                float splitRatio, RemoteAnimationAdapter adapter) {
+                float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startTasks",
                     (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
                             mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
-                            splitRatio, adapter));
+                            splitRatio, adapter, instanceId));
         }
 
         @Override
         public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
                 Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions,
-                int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+                int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+                InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController,
                     "startIntentAndTaskWithLegacyTransition", (controller) ->
                             controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition(
                                     pendingIntent, fillInIntent, taskId, mainOptions, sideOptions,
-                                    sidePosition, splitRatio, adapter));
+                                    sidePosition, splitRatio, adapter, instanceId));
         }
 
         @Override
         public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
                 int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
-                @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+                @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+                InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController,
                     "startShortcutAndTaskWithLegacyTransition", (controller) ->
                             controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
                                     shortcutInfo, taskId, mainOptions, sideOptions, sidePosition,
-                                    splitRatio, adapter));
+                                    splitRatio, adapter, instanceId));
         }
 
         @Override
         public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
                 int sideTaskId, @Nullable Bundle sideOptions,
                 @SplitPosition int sidePosition, float splitRatio,
-                @Nullable RemoteTransition remoteTransition) {
+                @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startTasks",
                     (controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
-                            sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition));
+                            sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition,
+                            instanceId));
         }
 
         @Override
         public void startShortcut(String packageName, String shortcutId, int position,
-                @Nullable Bundle options, UserHandle user) {
+                @Nullable Bundle options, UserHandle user, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startShortcut",
                     (controller) -> {
-                        controller.startShortcut(packageName, shortcutId, position, options, user);
+                        controller.startShortcut(packageName, shortcutId, position, options, user,
+                                instanceId);
                     });
         }
 
         @Override
         public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
-                @Nullable Bundle options) {
+                @Nullable Bundle options, InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startIntent",
                     (controller) -> {
-                        controller.startIntent(intent, fillInIntent, position, options);
+                        controller.startIntent(intent, fillInIntent, position, options, instanceId);
                     });
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 3e7a100..626ccb1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.splitscreen;
 
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__LAUNCHER;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
@@ -59,7 +59,7 @@
 
     // Drag info
     private @SplitPosition int mDragEnterPosition;
-    private InstanceId mDragEnterSessionId;
+    private InstanceId mEnterSessionId;
 
     // For deduping async events
     private int mLastMainStagePosition = -1;
@@ -82,9 +82,17 @@
     /**
      * May be called before logEnter() to indicate that the session was started from a drag.
      */
-    public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) {
+    public void enterRequestedByDrag(@SplitPosition int position, InstanceId enterSessionId) {
         mDragEnterPosition = position;
-        mDragEnterSessionId = dragSessionId;
+        enterRequested(enterSessionId);
+    }
+
+    /**
+     * May be called before logEnter() to indicate that the session was started from launcher.
+     * This specifically is for all the scenarios where split started without a drag interaction
+     */
+    public void enterRequested(InstanceId enterSessionId) {
+        mEnterSessionId = enterSessionId;
     }
 
     /**
@@ -97,7 +105,7 @@
         mLoggerSessionId = mIdSequence.newInstanceId();
         int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED
                 ? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape)
-                : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+                : SPLITSCREEN_UICHANGED__ENTER_REASON__LAUNCHER;
         updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
                 mainStageUid);
         updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
@@ -112,7 +120,7 @@
                 mLastMainStageUid,
                 mLastSideStagePosition,
                 mLastSideStageUid,
-                mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0,
+                mEnterSessionId != null ? mEnterSessionId.getId() : 0,
                 mLoggerSessionId.getId());
     }
 
@@ -176,7 +184,7 @@
         // Reset states
         mLoggerSessionId = null;
         mDragEnterPosition = SPLIT_POSITION_UNDEFINED;
-        mDragEnterSessionId = null;
+        mEnterSessionId = null;
         mLastMainStagePosition = -1;
         mLastMainStageUid = -1;
         mLastSideStagePosition = -1;
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 8ed16ce..946dfde 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
@@ -69,6 +69,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.IActivityTaskManager;
 import android.app.PendingIntent;
 import android.app.WindowConfiguration;
@@ -87,6 +88,7 @@
 import android.util.Slog;
 import android.view.Choreographer;
 import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -405,6 +407,10 @@
         return result;
     }
 
+    SplitscreenEventLogger getLogger() {
+        return mLogger;
+    }
+
     /** Launches an activity into split. */
     void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
             @Nullable Bundle options) {
@@ -497,7 +503,7 @@
     /** Starts 2 tasks in one transition. */
     void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
             @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
-            @Nullable RemoteTransition remoteTransition) {
+            @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mainOptions = mainOptions != null ? mainOptions : new Bundle();
         sideOptions = sideOptions != null ? sideOptions : new Bundle();
@@ -526,46 +532,57 @@
 
         mSplitTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null);
+        setEnterInstanceId(instanceId);
     }
 
     /** Starts 2 tasks in one legacy transition. */
     void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
             int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
-            float splitRatio, RemoteAnimationAdapter adapter) {
+            float splitRatio, RemoteAnimationAdapter adapter,
+            InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (sideOptions == null) sideOptions = new Bundle();
         addActivityOptions(sideOptions, mSideStage);
         wct.startTask(sideTaskId, sideOptions);
 
-        startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter);
+        startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter,
+                instanceId);
     }
 
     /** Start an intent and a task ordered by {@code intentFirst}. */
     void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
             int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
-            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+            InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (sideOptions == null) sideOptions = new Bundle();
         addActivityOptions(sideOptions, mSideStage);
         wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
 
-        startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
+        startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter,
+                instanceId);
     }
 
     void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
             int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
-            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+            InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (sideOptions == null) sideOptions = new Bundle();
         addActivityOptions(sideOptions, mSideStage);
         wct.startShortcut(mContext.getPackageName(), shortcutInfo, sideOptions);
 
-        startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
+        startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter,
+                instanceId);
     }
 
-    private void startWithLegacyTransition(WindowContainerTransaction sideWct, int mainTaskId,
+    /**
+     * @param instanceId if {@code null}, will not log. Otherwise it will be used in
+     *                   {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
+     */
+    private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
             @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
-            RemoteAnimationAdapter adapter) {
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         // Init divider first to make divider leash for remote animation target.
         mSplitLayout.init();
         mSplitLayout.setDivideRatio(splitRatio);
@@ -574,59 +591,56 @@
         mShouldUpdateRecents = false;
         mIsDividerRemoteAnimating = true;
 
-        LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
+        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+        prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
+        prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
+
+        IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
             @Override
-            public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
-                    RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
-                    IRemoteAnimationFinishedCallback finishedCallback,
-                    SurfaceControl.Transaction t) {
-                if (apps == null || apps.length == 0) {
-                    updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
-                    setDividerVisibility(true, t);
-                    t.apply();
-                    onRemoteAnimationFinished(apps);
-                    try {
-                        adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Error starting remote animation", e);
-                    }
-                    return;
-                }
-
-                // Wrap the divider bar into non-apps target to animate together.
-                nonApps = ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
-                        getDividerBarLegacyTarget());
-
-                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
-                setDividerVisibility(true, t);
-                for (int i = 0; i < apps.length; ++i) {
-                    if (apps[i].mode == MODE_OPENING) {
-                        t.show(apps[i].leash);
-                        // Reset the surface position of the opening app to prevent double-offset.
-                        t.setPosition(apps[i].leash, 0, 0);
-                    }
-                }
-                t.apply();
-
+            public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                    RemoteAnimationTarget[] apps,
+                    RemoteAnimationTarget[] wallpapers,
+                    RemoteAnimationTarget[] nonApps,
+                    final IRemoteAnimationFinishedCallback finishedCallback) {
                 IRemoteAnimationFinishedCallback wrapCallback =
                         new IRemoteAnimationFinishedCallback.Stub() {
                             @Override
                             public void onAnimationFinished() throws RemoteException {
-                                onRemoteAnimationFinished(apps);
+                                onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct);
                                 finishedCallback.onAnimationFinished();
                             }
                         };
                 Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
                 try {
-                    adapter.getRunner().onAnimationStart(
-                            transit, apps, wallpapers, nonApps, wrapCallback);
+                    adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
+                            ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
+                                    getDividerBarLegacyTarget()), wrapCallback);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error starting remote animation", e);
+                }
+            }
+
+            @Override
+            public void onAnimationCancelled(boolean isKeyguardOccluded) {
+                onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
+                try {
+                    adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Error starting remote animation", e);
                 }
             }
         };
+        RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
+                wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
 
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        if (mainOptions == null) {
+            mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
+        } else {
+            ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
+            mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+            mainOptions = mainActivityOptions.toBundle();
+        }
+
         setSideStagePosition(sidePosition, wct);
         if (!mMainStage.isActive()) {
             mMainStage.activate(wct, false /* reparent */);
@@ -634,34 +648,40 @@
 
         if (mainOptions == null) mainOptions = new Bundle();
         addActivityOptions(mainOptions, mMainStage);
-        wct.startTask(mainTaskId, mainOptions);
-        wct.merge(sideWct, true);
-
         updateWindowBounds(mSplitLayout, wct);
+        wct.startTask(mainTaskId, mainOptions);
         wct.reorder(mRootTaskInfo.token, true);
         wct.setForceTranslucent(mRootTaskInfo.token, false);
 
-        mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+        mSyncQueue.queue(wct);
+        mSyncQueue.runInSync(t -> {
+            setDividerVisibility(true, t);
+            updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+        });
+
+        setEnterInstanceId(instanceId);
     }
 
-    private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
+    private void setEnterInstanceId(InstanceId instanceId) {
+        if (instanceId != null) {
+            mLogger.enterRequested(instanceId);
+        }
+    }
+
+    private void onRemoteAnimationFinishedOrCancelled(boolean cancel,
+            WindowContainerTransaction evictWct) {
         mIsDividerRemoteAnimating = false;
         mShouldUpdateRecents = true;
-        if (apps == null || apps.length == 0) return;
-
-        // If any stage has no child after finished animation, that side of the split will display
-        // nothing. This might happen if starting the same app on the both sides while not
-        // supporting multi-instance. Exit the split screen and expand that app to full screen.
-        if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
-            mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
-                    ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
-            return;
+        // If any stage has no child after animation finished, it means that split will display
+        // nothing, such status will happen if task and intent is same app but not support
+        // multi-instance, we should exit split and expand that app as full screen.
+        if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
+            mMainExecutor.execute(() ->
+                    exitSplitScreen(mMainStage.getChildCount() == 0
+                            ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+        } else {
+            mSyncQueue.queue(evictWct);
         }
-
-        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
-        prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
-        prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
-        mSyncQueue.queue(evictWct);
     }
 
     /**
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 60c812a..819358b 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -677,21 +677,39 @@
 
     @Override
     public String toString() {
-        return "AudioPlaybackConfiguration piid:" + mPlayerIId
-                + " deviceId:" + mDeviceId
-                + " type:" + toLogFriendlyPlayerType(mPlayerType)
-                + " u/pid:" + mClientUid + "/" + mClientPid
-                + " state:" + toLogFriendlyPlayerState(mPlayerState)
-                + " attr:" + mPlayerAttr
-                + " sessionId:" + mSessionId
-                + " mutedState:"
-                + " muteFromMasterMute=" + ((mMutedState & PLAYER_MUTE_MASTER) != 0)
-                + " muteFromStreamVolume=" + ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0)
-                + " muteFromStreamMuted=" + ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0)
-                + " muteFromPlaybackRestricted=" + ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED)
-                != 0)
-                + " muteFromClientVolume=" + ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0)
-                + " muteFromVolumeShaper=" + ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0);
+        StringBuilder apcToString = new StringBuilder();
+        apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append(
+                " deviceId:").append(mDeviceId).append(" type:").append(
+                toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(mClientUid).append(
+                "/").append(mClientPid).append(" state:").append(
+                toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append(mPlayerAttr).append(
+                " sessionId:").append(mSessionId).append(" mutedState:");
+        if (mMutedState == PLAYER_MUTE_INVALID) {
+            apcToString.append("invalid ");
+        } else if (mMutedState == 0) {
+            apcToString.append("none ");
+        } else {
+            if ((mMutedState & PLAYER_MUTE_MASTER) != 0) {
+                apcToString.append("master ");
+            }
+            if ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0) {
+                apcToString.append("streamVolume ");
+            }
+            if ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0) {
+                apcToString.append("streamMute ");
+            }
+            if ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0) {
+                apcToString.append("playbackRestricted ");
+            }
+            if ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0) {
+                apcToString.append("clientVolume ");
+            }
+            if ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0) {
+                apcToString.append("volumeShaper ");
+            }
+        }
+
+        return apcToString.toString();
     }
 
     //=====================================================================
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index e5bf8ca..e34fedd 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -20,7 +20,8 @@
     <application
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_label"
-        android:supportsRtl="true">
+        android:supportsRtl="true"
+        android:enableOnBackInvokedCallback="true">
         <activity
             android:name=".MainActivity"
             android:exported="true">
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index 104966d..418d6cb 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -59,6 +59,7 @@
 }
 
 dependencies {
+    api "androidx.appcompat:appcompat:1.6.0-rc01"
     api "androidx.compose.material3:material3:$jetpack_compose_material3_version"
     api "androidx.compose.material:material-icons-extended:$jetpack_compose_version"
     api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version"
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index ae15da6..e5a1862 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -22,7 +22,10 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavHostController
 import androidx.navigation.compose.NavHost
 import androidx.navigation.compose.composable
 import androidx.navigation.compose.rememberNavController
@@ -50,9 +53,6 @@
 
     @Composable
     private fun MainContent() {
-        val destination =
-            intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPageName()
-
         val navController = rememberNavController()
         CompositionLocalProvider(navController.localNavController()) {
             NavHost(navController, ROOT_PAGE_NAME) {
@@ -70,13 +70,23 @@
                     }
                 }
             }
+        }
 
+        InitialDestinationNavigator(navController)
+    }
+
+    @Composable
+    private fun InitialDestinationNavigator(navController: NavHostController) {
+        val destinationNavigated = rememberSaveable { mutableStateOf(false) }
+        if (destinationNavigated.value) return
+        destinationNavigated.value = true
+        LaunchedEffect(Unit) {
+            val destination =
+                intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPageName()
             if (destination.isNotEmpty()) {
-                LaunchedEffect(Unit) {
-                    navController.navigate(destination) {
-                        popUpTo(navController.graph.findStartDestination().id) {
-                            inclusive = true
-                        }
+                navController.navigate(destination) {
+                    popUpTo(navController.graph.findStartDestination().id) {
+                        inclusive = true
                     }
                 }
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 766c036..7353cc0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -407,7 +407,7 @@
                 || sessionInfo.getVolumeHandling() != MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
     }
 
-    private void refreshDevices() {
+    private synchronized void refreshDevices() {
         mMediaDevices.clear();
         mCurrentConnectedDevice = null;
         if (TextUtils.isEmpty(mPackageName)) {
@@ -437,7 +437,7 @@
         return infos;
     }
 
-    private void buildAvailableRoutes() {
+    private synchronized void buildAvailableRoutes() {
         for (MediaRoute2Info route : getAvailableRoutes(mPackageName)) {
             if (DEBUG) {
                 Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : "
@@ -447,7 +447,7 @@
         }
     }
 
-    private List<MediaRoute2Info> getAvailableRoutes(String packageName) {
+    private synchronized List<MediaRoute2Info> getAvailableRoutes(String packageName) {
         final List<MediaRoute2Info> infos = new ArrayList<>();
         RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName);
         if (routingSessionInfo != null) {
@@ -571,7 +571,7 @@
 
         @Override
         public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
-            dispatchDataChanged();
+            refreshDevices();
         }
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index f4af6e8..33fb91d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -666,12 +666,22 @@
     }
 
     @Test
-    public void onSessionUpdated_shouldDispatchDataChanged() {
+    public void onSessionUpdated_shouldDispatchDeviceListAdded() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        when(info.getId()).thenReturn(TEST_ID);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        when(info.isSystemRoute()).thenReturn(true);
+
+        final List<MediaRoute2Info> routes = new ArrayList<>();
+        routes.add(info);
+        mShadowRouter2Manager.setAllRoutes(routes);
+
+        mInfoMediaManager.mPackageName = "";
         mInfoMediaManager.registerCallback(mCallback);
 
         mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class));
 
-        verify(mCallback).onDeviceAttributesChanged();
+        verify(mCallback).onDeviceListAdded(any());
     }
 
     @Test
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml
new file mode 100644
index 0000000..5c0a7c8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="20"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c "
+                    android:valueTo="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+                    android:valueType="pathType">
+                    <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:duration="40"
+                    android:propertyName="pathData"
+                    android:startOffset="20"
+                    android:valueFrom="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+                    android:valueTo="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+                    android:valueType="pathType">
+                    <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:duration="40"
+                    android:propertyName="pathData"
+                    android:startOffset="60"
+                    android:valueFrom="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+                    android:valueTo="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+                    android:valueType="pathType">
+                    <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:duration="100"
+                    android:propertyName="pathData"
+                    android:startOffset="100"
+                    android:valueFrom="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+                    android:valueTo="M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,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:duration="217"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportHeight="24"
+            android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="12"
+                    android:translateY="12">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#ffffff"
+                        android:fillType="nonZero"
+                        android:pathData=" M0 -7.08 C-2.69,-7.08 -7.03,-5.19 -7.03,0.03 C-7.03,5.25 -2.19,7.08 0,7.08 C3.03,7.08 7.08,3.91 7.08,0 C7.08,-3.91 3.69,-7.08 0,-7.08c  M-8.33 0 C-8.33,-4.6 -4.6,-8.33 0,-8.33 C4.6,-8.33 8.33,-4.6 8.33,0 C8.33,4.6 4.6,8.33 0,8.33 C-4.6,8.33 -8.33,4.6 -8.33,0c " />
+                    <path
+                        android:name="_R_G_L_0_G_D_1_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#ffffff"
+                        android:fillType="nonZero"
+                        android:pathData=" M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml
new file mode 100644
index 0000000..a96b748
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_0_G_D_1_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="100"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c "
+                    android:valueTo="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="67"
+                    android:propertyName="pathData"
+                    android:startOffset="100"
+                    android:valueFrom="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+                    android:valueTo="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+                    android:valueType="pathType">
+                    <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:duration="67"
+                    android:propertyName="pathData"
+                    android:startOffset="167"
+                    android:valueFrom="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+                    android:valueTo="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+                    android:valueType="pathType">
+                    <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:duration="100"
+                    android:propertyName="pathData"
+                    android:startOffset="233"
+                    android:valueFrom="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+                    android:valueTo="M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,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:duration="350"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportHeight="24"
+            android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="12"
+                    android:translateY="12">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#edf2eb"
+                        android:fillType="nonZero"
+                        android:pathData=" M0 -7.08 C-2.69,-7.08 -7.03,-5.19 -7.03,0.03 C-7.03,5.25 -2.19,7.08 0,7.08 C3.03,7.08 7.08,3.91 7.08,0 C7.08,-3.91 3.69,-7.08 0,-7.08c  M-8.33 0 C-8.33,-4.6 -4.6,-8.33 0,-8.33 C4.6,-8.33 8.33,-4.6 8.33,0 C8.33,4.6 4.6,8.33 0,8.33 C-4.6,8.33 -8.33,4.6 -8.33,0c " />
+                    <path
+                        android:name="_R_G_L_0_G_D_1_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#edf2eb"
+                        android:fillType="nonZero"
+                        android:pathData=" M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 32871f0..ac131ae 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -84,6 +84,10 @@
     <!-- The translation for disappearing security views after having solved them. -->
     <dimen name="disappear_y_translation">-32dp</dimen>
 
+    <!-- Dimens for animation for the Bouncer PIN view -->
+    <dimen name="pin_view_trans_y_entry">120dp</dimen>
+    <dimen name="pin_view_trans_y_entry_offset">10dp</dimen>
+
     <!-- Spacing around each button used for PIN view -->
     <dimen name="num_pad_key_width">72dp</dimen>
     <dimen name="num_pad_entry_row_margin_bottom">12dp</dimen>
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
new file mode 100644
index 0000000..40bfd83
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners
+        android:radius="28dp"/>
+    <solid android:color="@color/media_dialog_background" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 93c16e4..b76de5a 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -20,6 +20,7 @@
     android:id="@+id/media_output_dialog"
     android:layout_width="@dimen/large_dialog_width"
     android:layout_height="wrap_content"
+    android:background="@drawable/media_output_dialog_background"
     android:orientation="vertical">
 
     <LinearLayout
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index dd5987b..11dbe4a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -186,7 +186,7 @@
     <color name="media_dialog_item_main_content">@color/material_dynamic_primary20</color>
     <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
     <color name="media_dialog_connected_item_background">@color/material_dynamic_primary90</color>
-    <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color>
+    <color name="media_dialog_seekbar_progress">@android:color/system_accent1_200</color>
     <color name="media_dialog_button_background">@color/material_dynamic_primary40</color>
     <color name="media_dialog_solid_button_text">@color/material_dynamic_neutral95</color>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 08b1bdc..e76047e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -520,7 +520,7 @@
     <dimen name="qs_tile_margin_vertical">@dimen/qs_tile_margin_horizontal</dimen>
     <dimen name="qs_tile_margin_top_bottom">4dp</dimen>
     <dimen name="qs_brightness_margin_top">8dp</dimen>
-    <dimen name="qs_brightness_margin_bottom">24dp</dimen>
+    <dimen name="qs_brightness_margin_bottom">16dp</dimen>
     <dimen name="qqs_layout_margin_top">16dp</dimen>
     <dimen name="qqs_layout_padding_bottom">24dp</dimen>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
deleted file mode 100644
index 30c062b..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ /dev/null
@@ -1,380 +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.shared.system;
-
-import android.graphics.HardwareRenderer;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Handler.Callback;
-import android.os.Message;
-import android.os.Trace;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-import android.view.View;
-import android.view.ViewRootImpl;
-
-import java.util.function.Consumer;
-
-/**
- * Helper class to apply surface transactions in sync with RenderThread.
- *
- * NOTE: This is a modification of {@link android.view.SyncRtSurfaceTransactionApplier}, we can't 
- *       currently reference that class from the shared lib as it is hidden.
- */
-public class SyncRtSurfaceTransactionApplierCompat {
-
-    public static final int FLAG_ALL = 0xffffffff;
-    public static final int FLAG_ALPHA = 1;
-    public static final int FLAG_MATRIX = 1 << 1;
-    public static final int FLAG_WINDOW_CROP = 1 << 2;
-    public static final int FLAG_LAYER = 1 << 3;
-    public static final int FLAG_CORNER_RADIUS = 1 << 4;
-    public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5;
-    public static final int FLAG_VISIBILITY = 1 << 6;
-    public static final int FLAG_RELATIVE_LAYER = 1 << 7;
-    public static final int FLAG_SHADOW_RADIUS = 1 << 8;
-
-    private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
-
-    private final SurfaceControl mBarrierSurfaceControl;
-    private final ViewRootImpl mTargetViewRootImpl;
-    private final Handler mApplyHandler;
-
-    private int mSequenceNumber = 0;
-    private int mPendingSequenceNumber = 0;
-    private Runnable mAfterApplyCallback;
-
-    /**
-     * @param targetView The view in the surface that acts as synchronization anchor.
-     */
-    public SyncRtSurfaceTransactionApplierCompat(View targetView) {
-        mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
-        mBarrierSurfaceControl = mTargetViewRootImpl != null
-            ? mTargetViewRootImpl.getSurfaceControl() : null;
-
-        mApplyHandler = new Handler(new Callback() {
-            @Override
-            public boolean handleMessage(Message msg) {
-                if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) {
-                    onApplyMessage(msg.arg1);
-                    return true;
-                }
-                return false;
-            }
-        });
-    }
-
-    private void onApplyMessage(int seqNo) {
-        mSequenceNumber = seqNo;
-        if (mSequenceNumber == mPendingSequenceNumber && mAfterApplyCallback != null) {
-            Runnable r = mAfterApplyCallback;
-            mAfterApplyCallback = null;
-            r.run();
-        }
-    }
-
-    /**
-     * Schedules applying surface parameters on the next frame.
-     *
-     * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
-     *               this method to avoid synchronization issues.
-     */
-    public void scheduleApply(final SyncRtSurfaceTransactionApplierCompat.SurfaceParams... params) {
-        if (mTargetViewRootImpl == null || mTargetViewRootImpl.getView() == null) {
-            return;
-        }
-
-        mPendingSequenceNumber++;
-        final int toApplySeqNo = mPendingSequenceNumber;
-        mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
-            @Override
-            public void onFrameDraw(long frame) {
-                if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {
-                    Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
-                            .sendToTarget();
-                    return;
-                }
-                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Sync transaction frameNumber=" + frame);
-                Transaction t = new Transaction();
-                for (int i = params.length - 1; i >= 0; i--) {
-                    SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
-                            params[i];
-                    surfaceParams.applyTo(t);
-                }
-                if (mTargetViewRootImpl != null) {
-                    mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
-                } else {
-                    t.apply();
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-                Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
-                        .sendToTarget();
-            }
-        });
-
-        // Make sure a frame gets scheduled.
-        mTargetViewRootImpl.getView().invalidate();
-    }
-
-    /**
-     * Calls the runnable when any pending apply calls have completed
-     */
-    public void addAfterApplyCallback(final Runnable afterApplyCallback) {
-        if (mSequenceNumber == mPendingSequenceNumber) {
-            afterApplyCallback.run();
-        } else {
-            if (mAfterApplyCallback == null) {
-                mAfterApplyCallback = afterApplyCallback;
-            } else {
-                final Runnable oldCallback = mAfterApplyCallback;
-                mAfterApplyCallback = new Runnable() {
-                    @Override
-                    public void run() {
-                        afterApplyCallback.run();
-                        oldCallback.run();
-                    }
-                };
-            }
-        }
-    }
-
-    public static void applyParams(TransactionCompat t,
-            SyncRtSurfaceTransactionApplierCompat.SurfaceParams params) {
-        params.applyTo(t.mTransaction);
-    }
-
-    /**
-     * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
-     * attached if necessary.
-     */
-    public static void create(final View targetView,
-            final Consumer<SyncRtSurfaceTransactionApplierCompat> callback) {
-        if (targetView == null) {
-            // No target view, no applier
-            callback.accept(null);
-        } else if (targetView.getViewRootImpl() != null) {
-            // Already attached, we're good to go
-            callback.accept(new SyncRtSurfaceTransactionApplierCompat(targetView));
-        } else {
-            // Haven't been attached before we can get the view root
-            targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
-                @Override
-                public void onViewAttachedToWindow(View v) {
-                    targetView.removeOnAttachStateChangeListener(this);
-                    callback.accept(new SyncRtSurfaceTransactionApplierCompat(targetView));
-                }
-
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                    // Do nothing
-                }
-            });
-        }
-    }
-
-    public static class SurfaceParams {
-        public static class Builder {
-            final SurfaceControl surface;
-            int flags;
-            float alpha;
-            float cornerRadius;
-            int backgroundBlurRadius;
-            Matrix matrix;
-            Rect windowCrop;
-            int layer;
-            SurfaceControl relativeTo;
-            int relativeLayer;
-            boolean visible;
-            float shadowRadius;
-
-            /**
-             * @param surface The surface to modify.
-             */
-            public Builder(SurfaceControl surface) {
-                this.surface = surface;
-            }
-
-            /**
-             * @param alpha The alpha value to apply to the surface.
-             * @return this Builder
-             */
-            public Builder withAlpha(float alpha) {
-                this.alpha = alpha;
-                flags |= FLAG_ALPHA;
-                return this;
-            }
-
-            /**
-             * @param matrix The matrix to apply to the surface.
-             * @return this Builder
-             */
-            public Builder withMatrix(Matrix matrix) {
-                this.matrix = new Matrix(matrix);
-                flags |= FLAG_MATRIX;
-                return this;
-            }
-
-            /**
-             * @param windowCrop The window crop to apply to the surface.
-             * @return this Builder
-             */
-            public Builder withWindowCrop(Rect windowCrop) {
-                this.windowCrop = new Rect(windowCrop);
-                flags |= FLAG_WINDOW_CROP;
-                return this;
-            }
-
-            /**
-             * @param layer The layer to assign the surface.
-             * @return this Builder
-             */
-            public Builder withLayer(int layer) {
-                this.layer = layer;
-                flags |= FLAG_LAYER;
-                return this;
-            }
-
-            /**
-             * @param relativeTo The surface that's set relative layer to.
-             * @param relativeLayer The relative layer.
-             * @return this Builder
-             */
-            public Builder withRelativeLayerTo(SurfaceControl relativeTo, int relativeLayer) {
-                this.relativeTo = relativeTo;
-                this.relativeLayer = relativeLayer;
-                flags |= FLAG_RELATIVE_LAYER;
-                return this;
-            }
-
-            /**
-             * @param radius the Radius for rounded corners to apply to the surface.
-             * @return this Builder
-             */
-            public Builder withCornerRadius(float radius) {
-                this.cornerRadius = radius;
-                flags |= FLAG_CORNER_RADIUS;
-                return this;
-            }
-
-            /**
-             * @param radius the Radius for the shadows to apply to the surface.
-             * @return this Builder
-             */
-            public Builder withShadowRadius(float radius) {
-                this.shadowRadius = radius;
-                flags |= FLAG_SHADOW_RADIUS;
-                return this;
-            }
-
-            /**
-             * @param radius the Radius for blur to apply to the background surfaces.
-             * @return this Builder
-             */
-            public Builder withBackgroundBlur(int radius) {
-                this.backgroundBlurRadius = radius;
-                flags |= FLAG_BACKGROUND_BLUR_RADIUS;
-                return this;
-            }
-
-            /**
-             * @param visible The visibility to apply to the surface.
-             * @return this Builder
-             */
-            public Builder withVisibility(boolean visible) {
-                this.visible = visible;
-                flags |= FLAG_VISIBILITY;
-                return this;
-            }
-
-            /**
-             * @return a new SurfaceParams instance
-             */
-            public SurfaceParams build() {
-                return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer,
-                        relativeTo, relativeLayer, cornerRadius, backgroundBlurRadius, visible,
-                        shadowRadius);
-            }
-        }
-
-        private SurfaceParams(SurfaceControl surface, int flags, float alpha, Matrix matrix,
-                Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
-                float cornerRadius, int backgroundBlurRadius, boolean visible, float shadowRadius) {
-            this.flags = flags;
-            this.surface = surface;
-            this.alpha = alpha;
-            this.matrix = matrix;
-            this.windowCrop = windowCrop;
-            this.layer = layer;
-            this.relativeTo = relativeTo;
-            this.relativeLayer = relativeLayer;
-            this.cornerRadius = cornerRadius;
-            this.backgroundBlurRadius = backgroundBlurRadius;
-            this.visible = visible;
-            this.shadowRadius = shadowRadius;
-        }
-
-        private final int flags;
-        private final float[] mTmpValues = new float[9];
-
-        public final SurfaceControl surface;
-        public final float alpha;
-        public final float cornerRadius;
-        public final int backgroundBlurRadius;
-        public final Matrix matrix;
-        public final Rect windowCrop;
-        public final int layer;
-        public final SurfaceControl relativeTo;
-        public final int relativeLayer;
-        public final boolean visible;
-        public final float shadowRadius;
-
-        public void applyTo(SurfaceControl.Transaction t) {
-            if ((flags & FLAG_MATRIX) != 0) {
-                t.setMatrix(surface, matrix, mTmpValues);
-            }
-            if ((flags & FLAG_WINDOW_CROP) != 0) {
-                t.setWindowCrop(surface, windowCrop);
-            }
-            if ((flags & FLAG_ALPHA) != 0) {
-                t.setAlpha(surface, alpha);
-            }
-            if ((flags & FLAG_LAYER) != 0) {
-                t.setLayer(surface, layer);
-            }
-            if ((flags & FLAG_CORNER_RADIUS) != 0) {
-                t.setCornerRadius(surface, cornerRadius);
-            }
-            if ((flags & FLAG_BACKGROUND_BLUR_RADIUS) != 0) {
-                t.setBackgroundBlurRadius(surface, backgroundBlurRadius);
-            }
-            if ((flags & FLAG_VISIBILITY) != 0) {
-                if (visible) {
-                    t.show(surface);
-                } else {
-                    t.hide(surface);
-                }
-            }
-            if ((flags & FLAG_RELATIVE_LAYER) != 0) {
-                t.setRelativeLayer(surface, relativeTo, relativeLayer);
-            }
-            if ((flags & FLAG_SHADOW_RADIUS) != 0) {
-                t.setShadowRadius(surface, shadowRadius);
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
deleted file mode 100644
index 43a882a5..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ /dev/null
@@ -1,108 +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.shared.system;
-
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-
-public class TransactionCompat {
-
-    final Transaction mTransaction;
-
-    final float[] mTmpValues = new float[9];
-
-    public TransactionCompat() {
-        mTransaction = new Transaction();
-    }
-
-    public void apply() {
-        mTransaction.apply();
-    }
-
-    public TransactionCompat show(SurfaceControl surfaceControl) {
-        mTransaction.show(surfaceControl);
-        return this;
-    }
-
-    public TransactionCompat hide(SurfaceControl surfaceControl) {
-        mTransaction.hide(surfaceControl);
-        return this;
-    }
-
-    public TransactionCompat setPosition(SurfaceControl surfaceControl, float x, float y) {
-        mTransaction.setPosition(surfaceControl, x, y);
-        return this;
-    }
-
-    public TransactionCompat setSize(SurfaceControl surfaceControl, int w, int h) {
-        mTransaction.setBufferSize(surfaceControl, w, h);
-        return this;
-    }
-
-    public TransactionCompat setLayer(SurfaceControl surfaceControl, int z) {
-        mTransaction.setLayer(surfaceControl, z);
-        return this;
-    }
-
-    public TransactionCompat setAlpha(SurfaceControl surfaceControl, float alpha) {
-        mTransaction.setAlpha(surfaceControl, alpha);
-        return this;
-    }
-
-    public TransactionCompat setOpaque(SurfaceControl surfaceControl, boolean opaque) {
-        mTransaction.setOpaque(surfaceControl, opaque);
-        return this;
-    }
-
-    public TransactionCompat setMatrix(SurfaceControl surfaceControl, float dsdx, float dtdx,
-            float dtdy, float dsdy) {
-        mTransaction.setMatrix(surfaceControl, dsdx, dtdx, dtdy, dsdy);
-        return this;
-    }
-
-    public TransactionCompat setMatrix(SurfaceControl surfaceControl, Matrix matrix) {
-        mTransaction.setMatrix(surfaceControl, matrix, mTmpValues);
-        return this;
-    }
-
-    public TransactionCompat setWindowCrop(SurfaceControl surfaceControl, Rect crop) {
-        mTransaction.setWindowCrop(surfaceControl, crop);
-        return this;
-    }
-
-    public TransactionCompat setCornerRadius(SurfaceControl surfaceControl, float radius) {
-        mTransaction.setCornerRadius(surfaceControl, radius);
-        return this;
-    }
-
-    public TransactionCompat setBackgroundBlurRadius(SurfaceControl surfaceControl, int radius) {
-        mTransaction.setBackgroundBlurRadius(surfaceControl, radius);
-        return this;
-    }
-
-    public TransactionCompat setColor(SurfaceControl surfaceControl, float[] color) {
-        mTransaction.setColor(surfaceControl, color);
-        return this;
-    }
-
-    public static void setRelativeLayer(Transaction t, SurfaceControl surfaceControl,
-            SurfaceControl relativeTo, int z) {
-        t.setRelativeLayer(surfaceControl, relativeTo, z);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 919b71b..f82e7db 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -55,6 +55,8 @@
     val bouncerIsOrWillShow: Boolean,
     val faceAuthenticated: Boolean,
     val faceDisabled: Boolean,
+    val faceLockedOut: Boolean,
+    val fpLockedOut: Boolean,
     val goingToSleep: Boolean,
     val keyguardAwakeExcludingBouncerShowing: Boolean,
     val keyguardGoingAway: Boolean,
@@ -65,7 +67,7 @@
     val scanningAllowedByStrongAuth: Boolean,
     val secureCameraLaunched: Boolean,
     val switchingUser: Boolean,
-    val udfpsBouncerShowing: Boolean
+    val udfpsBouncerShowing: Boolean,
 ) : KeyguardListenModel()
 /**
  * Verbose debug information associated with [KeyguardUpdateMonitor.shouldTriggerActiveUnlock].
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 20fa8f8..453072b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -48,6 +48,9 @@
     private ConstraintLayout mContainer;
     private int mDisappearYTranslation;
     private View[][] mViews;
+    private int mYTrans;
+    private int mYTransOffset;
+    private View mBouncerMessageView;
     @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
 
     public KeyguardPINView(Context context) {
@@ -67,6 +70,8 @@
                         mContext, android.R.interpolator.fast_out_linear_in));
         mDisappearYTranslation = getResources().getDimensionPixelSize(
                 R.dimen.disappear_y_translation);
+        mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
+        mYTransOffset = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry_offset);
     }
 
     @Override
@@ -138,6 +143,7 @@
         super.onFinishInflate();
 
         mContainer = findViewById(R.id.pin_container);
+        mBouncerMessageView = findViewById(R.id.bouncer_message_area);
         mViews = new View[][]{
                 new View[]{
                         findViewById(R.id.row0), null, null
@@ -206,6 +212,12 @@
 
     /** Animate subviews according to expansion or time. */
     private void animate(float progress) {
+        Interpolator standardDecelerate = Interpolators.STANDARD_DECELERATE;
+        Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE;
+
+        mBouncerMessageView.setTranslationY(
+                mYTrans - mYTrans * standardDecelerate.getInterpolation(progress));
+
         for (int i = 0; i < mViews.length; i++) {
             View[] row = mViews[i];
             for (View view : row) {
@@ -213,14 +225,15 @@
                     continue;
                 }
 
-                float scaledProgress = MathUtils.constrain(
+                float scaledProgress = legacyDecelerate.getInterpolation(MathUtils.constrain(
                         (progress - 0.075f * i) / (1f - 0.075f * mViews.length),
                         0f,
                         1f
-                );
+                ));
                 view.setAlpha(scaledProgress);
-                Interpolator interpolator = Interpolators.STANDARD_ACCELERATE;
-                view.setTranslationY(40 - (40 * interpolator.getInterpolation(scaledProgress)));
+                int yDistance = mYTrans + mYTransOffset * i;
+                view.setTranslationY(
+                        yDistance - (yDistance * standardDecelerate.getInterpolation(progress)));
                 if (view instanceof NumPadAnimationListener) {
                     ((NumPadAnimationListener) view).setProgress(scaledProgress);
                 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 9f4585f..89fcc47 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -82,6 +82,12 @@
     }
 
     @Override
+    public void startAppearAnimation() {
+        mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_pin);
+        super.startAppearAnimation();
+    }
+
+    @Override
     public boolean startDisappearAnimation(Runnable finishRunnable) {
         return mView.startDisappearAnimation(
                 mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1463840..5ccab54 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2612,6 +2612,7 @@
         final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
         final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
         final boolean onlyFaceEnrolled = isOnlyFaceEnrolled();
+        final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;
 
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2628,7 +2629,7 @@
                 && strongAuthAllowsScanning && mIsPrimaryUser
                 && (!mSecureCameraLaunched || mOccludingAppRequestingFace)
                 && !faceAuthenticated
-                && !fpLockedout;
+                && !fpOrFaceIsLockedOut;
 
         // Aggregate relevant fields for debug logging.
         maybeLogListenerModelData(
@@ -2643,6 +2644,8 @@
                     mBouncerIsOrWillBeShowing,
                     faceAuthenticated,
                     faceDisabledForUser,
+                    isFaceLockedOut(),
+                    fpLockedout,
                     mGoingToSleep,
                     awakeKeyguardExcludingBouncerShowing,
                     mKeyguardGoingAway,
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index e0cafae..41111e3 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -86,7 +86,7 @@
 
     public void setProgress(float progress) {
         mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress);
-        int height = (int) (mHeight * 0.8f + mHeight * 0.2 * progress);
+        int height = (int) (mHeight * 0.7f + mHeight * 0.3 * progress);
         int difference = mHeight - height;
         mBackground.setBounds(0, difference / 2, mHeight, mHeight - difference / 2);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 9138b23..9cfd399 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui;
 
-import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.Application;
 import android.app.Notification;
@@ -29,7 +28,6 @@
 import android.os.Bundle;
 import android.os.Looper;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -130,13 +128,6 @@
                         ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
             }
 
-            // Enable binder tracing on system server for calls originating from SysUI
-            try {
-                ActivityManager.getService().enableBinderTracing();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Unable to enable binder tracing", e);
-            }
-
             registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 78099d1f..3695df5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -98,7 +98,7 @@
      * Flag to enable the usage of the new bouncer data source. This is a refactor of and
      * eventual replacement of KeyguardBouncer.java.
      */
-    public static final ReleasedFlag MODERN_BOUNCER = new ReleasedFlag(208);
+    public static final UnreleasedFlag MODERN_BOUNCER = new UnreleasedFlag(208);
 
     /** Whether UserSwitcherActivity should use modern architecture. */
     public static final UnreleasedFlag MODERN_USER_SWITCHER_ACTIVITY =
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index b39770d..2d09ddd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -310,6 +310,7 @@
             if (icon.getType() != Icon.TYPE_BITMAP && icon.getType() != Icon.TYPE_ADAPTIVE_BITMAP) {
                 // icon doesn't support getBitmap, use default value for color scheme
                 updateButtonBackgroundColorFilter();
+                updateDialogBackgroundColor();
             } else {
                 Configuration config = mContext.getResources().getConfiguration();
                 int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
@@ -319,11 +320,14 @@
                 if (colorSetUpdated) {
                     mAdapter.updateColorScheme(wallpaperColors, isDarkThemeOn);
                     updateButtonBackgroundColorFilter();
+                    updateDialogBackgroundColor();
                 }
             }
             mHeaderIcon.setVisibility(View.VISIBLE);
             mHeaderIcon.setImageIcon(icon);
         } else {
+            updateButtonBackgroundColorFilter();
+            updateDialogBackgroundColor();
             mHeaderIcon.setVisibility(View.GONE);
         }
         if (appSourceIcon != null) {
@@ -381,11 +385,16 @@
 
     private void updateButtonBackgroundColorFilter() {
         ColorFilter buttonColorFilter = new PorterDuffColorFilter(
-                mAdapter.getController().getColorButtonBackground(),
+                mMediaOutputController.getColorButtonBackground(),
                 PorterDuff.Mode.SRC_IN);
         mDoneButton.getBackground().setColorFilter(buttonColorFilter);
         mStopButton.getBackground().setColorFilter(buttonColorFilter);
-        mDoneButton.setTextColor(mAdapter.getController().getColorPositiveButtonText());
+        mDoneButton.setTextColor(mMediaOutputController.getColorPositiveButtonText());
+    }
+
+    private void updateDialogBackgroundColor() {
+        getDialogView().getBackground().setTint(mMediaOutputController.getColorDialogBackground());
+        mDeviceListLayout.setBackgroundColor(mMediaOutputController.getColorDialogBackground());
     }
 
     private Drawable resizeDrawable(Drawable drawable, int size) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 96817c9..f040e06 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -133,6 +133,7 @@
     @VisibleForTesting
     LocalMediaManager mLocalMediaManager;
     private MediaOutputMetricLogger mMetricLogger;
+    private int mCurrentState;
 
     private int mColorItemContent;
     private int mColorSeekbarProgress;
@@ -140,6 +141,7 @@
     private int mColorItemBackground;
     private int mColorConnectedItemBackground;
     private int mColorPositiveButtonText;
+    private int mColorDialogBackground;
     private float mInactiveRadius;
     private float mActiveRadius;
 
@@ -188,6 +190,8 @@
                 R.dimen.media_output_dialog_background_radius);
         mActiveRadius = mContext.getResources().getDimension(
                 R.dimen.media_output_dialog_active_background_radius);
+        mColorDialogBackground = Utils.getColorStateListDefaultColor(mContext,
+                R.color.media_dialog_background);
     }
 
     void start(@NonNull Callback cb) {
@@ -204,6 +208,9 @@
                 if (TextUtils.equals(controller.getPackageName(), mPackageName)) {
                     mMediaController = controller;
                     mMediaController.unregisterCallback(mCb);
+                    if (mMediaController.getPlaybackState() != null) {
+                        mCurrentState = mMediaController.getPlaybackState().getState();
+                    }
                     mMediaController.registerCallback(mCb);
                     break;
                 }
@@ -461,6 +468,7 @@
             mColorItemBackground = mCurrentColorScheme.getNeutral2().get(9); // N2-800
             mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().get(9); // A2-800
             mColorPositiveButtonText = mCurrentColorScheme.getAccent2().get(9); // A2-800
+            mColorDialogBackground = mCurrentColorScheme.getNeutral1().get(10); // N1-900
         } else {
             mColorItemContent = mCurrentColorScheme.getAccent1().get(9); // A1-800
             mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(4); // A1-300
@@ -468,6 +476,7 @@
             mColorItemBackground = mCurrentColorScheme.getAccent2().get(1); // A2-50
             mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().get(2); // A1-100
             mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().get(1); // N1-50
+            mColorDialogBackground = mCurrentColorScheme.getBackgroundColor();
         }
     }
 
@@ -487,6 +496,10 @@
         return mColorPositiveButtonText;
     }
 
+    public int getColorDialogBackground() {
+        return mColorDialogBackground;
+    }
+
     public int getColorItemContent() {
         return mColorItemContent;
     }
@@ -976,10 +989,16 @@
 
         @Override
         public void onPlaybackStateChanged(PlaybackState playbackState) {
-            final int state = playbackState.getState();
-            if (state == PlaybackState.STATE_STOPPED || state == PlaybackState.STATE_PAUSED) {
+            final int newState =
+                    playbackState == null ? PlaybackState.STATE_STOPPED : playbackState.getState();
+            if (mCurrentState == newState) {
+                return;
+            }
+
+            if (newState == PlaybackState.STATE_STOPPED || newState == PlaybackState.STATE_PAUSED) {
                 mCallback.onMediaStoppedOrPaused();
             }
+            mCurrentState = newState;
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 920f463..e1289a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -99,7 +99,7 @@
     // slider, as well as animating the alpha of the QS tile layout (as we are tracking QQS tiles)
     @Nullable
     private TouchAnimator mFirstPageAnimator;
-    // TranslationX animator for QQS/QS tiles
+    // TranslationX animator for QQS/QS tiles. Only used on the first page!
     private TouchAnimator mTranslationXAnimator;
     // TranslationY animator for QS tiles (and their components) in the first page
     private TouchAnimator mTranslationYAnimator;
@@ -107,13 +107,14 @@
     private TouchAnimator mQQSTranslationYAnimator;
     // Animates alpha of permanent views (QS tile layout, QQS tiles) when not in first page
     private TouchAnimator mNonfirstPageAlphaAnimator;
-    // TranslatesY the QS Tile layout using QS.getHeightDiff()
-    private TouchAnimator mQSTileLayoutTranslatorAnimator;
     // This animates fading of media player
     private TouchAnimator mAllPagesDelayedAnimator;
-    // Animator for brightness slider(s)
+    // Brightness slider translation driver, uses mQSExpansionPathInterpolator.yInterpolator
     @Nullable
-    private TouchAnimator mBrightnessAnimator;
+    private TouchAnimator mBrightnessTranslationAnimator;
+    // Brightness slider opacity driver. Uses linear interpolator.
+    @Nullable
+    private TouchAnimator mBrightnessOpacityAnimator;
     // Animator for Footer actions in QQS
     private TouchAnimator mQQSFooterActionsAnimator;
     // Height animator for QQS tiles (height changing from QQS size to QS size)
@@ -137,7 +138,6 @@
     private final QSTileHost mHost;
     private final Executor mExecutor;
     private boolean mShowCollapsedOnKeyguard;
-    private boolean mTranslateWhileExpanding;
     private int mQQSTop;
 
     private int[] mTmpLoc1 = new int[2];
@@ -298,13 +298,6 @@
 
         QSTileLayout tileLayout = mQsPanelController.getTileLayout();
         mAllViews.add((View) tileLayout);
-        int heightDiff = mQs.getHeightDiff();
-        if (!mTranslateWhileExpanding) {
-            heightDiff *= SHORT_PARALLAX_AMOUNT;
-        }
-        mQSTileLayoutTranslatorAnimator = new Builder()
-                .addFloat(tileLayout, "translationY", heightDiff, 0)
-                .build();
 
         mLastQQSTileHeight = 0;
 
@@ -407,12 +400,7 @@
                     mAnimatedQsViews.add(tileView);
                     mAllViews.add(quickTileView);
                     mAllViews.add(quickTileView.getSecondaryLabel());
-                } else if (isIconInAnimatedRow(count)) {
-
-                    firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
-
-                    mAllViews.add(tileIcon);
-                } else {
+                } else if (!isIconInAnimatedRow(count)) {
                     // Pretend there's a corresponding QQS tile (for the position) that we are
                     // expanding from.
                     SideLabelTileLayout qqsLayout =
@@ -442,7 +430,7 @@
             }
         }
 
-        animateBrightnessSlider(firstPageBuilder);
+        animateBrightnessSlider();
 
         mFirstPageAnimator = firstPageBuilder
                 // Fade in the tiles/labels as we reach the final position.
@@ -568,7 +556,9 @@
         return new Pair<>(animator, builder.build());
     }
 
-    private void animateBrightnessSlider(Builder firstPageBuilder) {
+    private void animateBrightnessSlider() {
+        mBrightnessTranslationAnimator = null;
+        mBrightnessOpacityAnimator = null;
         View qsBrightness = mQsPanelController.getBrightnessView();
         View qqsBrightness = mQuickQSPanelController.getBrightnessView();
         if (qqsBrightness != null && qqsBrightness.getVisibility() == View.VISIBLE) {
@@ -576,25 +566,45 @@
             mAnimatedQsViews.add(qsBrightness);
             mAllViews.add(qqsBrightness);
             int translationY = getRelativeTranslationY(qsBrightness, qqsBrightness);
-            mBrightnessAnimator = new Builder()
+            mBrightnessTranslationAnimator = new Builder()
                     // we need to animate qs brightness even if animation will not be visible,
                     // as we might start from sliderScaleY set to 0.3 if device was in collapsed QS
                     // portrait orientation before
                     .addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
                     .addFloat(qqsBrightness, "translationY", 0, translationY)
+                    .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator())
                     .build();
         } else if (qsBrightness != null) {
-            firstPageBuilder.addFloat(qsBrightness, "translationY",
-                    qsBrightness.getMeasuredHeight() * 0.5f, 0);
-            mBrightnessAnimator = new Builder()
+            // The brightness slider's visible bottom edge must maintain a constant margin from the
+            // QS tiles during transition. Thus the slider must (1) perform the same vertical
+            // translation as the tiles, and (2) compensate for the slider scaling.
+
+            // For (1), compute the distance via the vertical distance between QQS and QS tile
+            // layout top.
+            View quickSettingsRootView = mQs.getView();
+            View qsTileLayout = (View) mQsPanelController.getTileLayout();
+            View qqsTileLayout = (View) mQuickQSPanelController.getTileLayout();
+            getRelativePosition(mTmpLoc1, qsTileLayout, quickSettingsRootView);
+            getRelativePosition(mTmpLoc2, qqsTileLayout, quickSettingsRootView);
+            int tileMovement = mTmpLoc2[1] - mTmpLoc1[1];
+
+            // For (2), the slider scales to the vertical center, so compensate with half the
+            // height at full collapse.
+            float scaleCompensation = qsBrightness.getMeasuredHeight() * 0.5f;
+            mBrightnessTranslationAnimator = new Builder()
+                    .addFloat(qsBrightness, "translationY", scaleCompensation + tileMovement, 0)
+                    .addFloat(qsBrightness, "sliderScaleY", 0, 1)
+                    .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator())
+                    .build();
+
+            // While the slider's position and unfurl is animated throughouth the motion, the
+            // fade in happens independently.
+            mBrightnessOpacityAnimator = new Builder()
                     .addFloat(qsBrightness, "alpha", 0, 1)
-                    .addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
-                    .setInterpolator(Interpolators.ALPHA_IN)
-                    .setStartDelay(0.3f)
+                    .setStartDelay(0.2f)
+                    .setEndDelay(1 - 0.5f)
                     .build();
             mAllViews.add(qsBrightness);
-        } else {
-            mBrightnessAnimator = null;
         }
     }
 
@@ -676,11 +686,13 @@
         if (mQQSTileHeightAnimator != null) {
             mQQSTileHeightAnimator.setPosition(position);
         }
-        mQSTileLayoutTranslatorAnimator.setPosition(position);
         mQQSTranslationYAnimator.setPosition(position);
         mAllPagesDelayedAnimator.setPosition(position);
-        if (mBrightnessAnimator != null) {
-            mBrightnessAnimator.setPosition(position);
+        if (mBrightnessOpacityAnimator != null) {
+            mBrightnessOpacityAnimator.setPosition(position);
+        }
+        if (mBrightnessTranslationAnimator != null) {
+            mBrightnessTranslationAnimator.setPosition(position);
         }
         if (mQQSFooterActionsAnimator != null) {
             mQQSFooterActionsAnimator.setPosition(position);
@@ -774,13 +786,6 @@
         setCurrentPosition();
     };
 
-    /**
-     * True whe QS will be pulled from the top, false when it will be clipped.
-     */
-    public void setTranslateWhileExpanding(boolean shouldTranslate) {
-        mTranslateWhileExpanding = shouldTranslate;
-    }
-
     private static class HeightExpansionAnimator {
         private final List<View> mViews = new ArrayList<>();
         private final ValueAnimator mAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 072c32f..3820500 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -573,7 +573,6 @@
     @Override
     public void setInSplitShade(boolean inSplitShade) {
         mInSplitShade = inSplitShade;
-        mQSAnimator.setTranslateWhileExpanding(inSplitShade);
         updateShowCollapsedOnKeyguard();
         updateQsState();
     }
@@ -669,7 +668,11 @@
         mQSPanelController.setRevealExpansion(expansion);
         mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
         mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
-        mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
+
+        float qsScrollViewTranslation =
+                onKeyguard && !mShowCollapsedOnKeyguard ? panelTranslationY : 0;
+        mQSPanelScrollView.setTranslationY(qsScrollViewTranslation);
+
         if (fullyCollapsed) {
             mQSPanelScrollView.setScrollY(0);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index e2964ea..f60e066 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles;
 
 import android.app.UiModeManager;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Handler;
@@ -60,9 +61,7 @@
         ConfigurationController.ConfigurationListener,
         BatteryController.BatteryStateChangeCallback {
     public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
-    private final Icon mIcon = ResourceIcon.get(
-            com.android.internal.R.drawable.ic_qs_ui_mode_night);
-    private UiModeManager mUiModeManager;
+    private final UiModeManager mUiModeManager;
     private final BatteryController mBatteryController;
     private final LocationController mLocationController;
     @Inject
@@ -82,7 +81,8 @@
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
         mBatteryController = batteryController;
-        mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
+        mUiModeManager = (UiModeManager) host.getUserContext().getSystemService(
+                Context.UI_MODE_SERVICE);
         mLocationController = locationController;
         configurationController.observe(getLifecycle(), this);
         batteryController.observe(getLifecycle(), this);
@@ -155,7 +155,6 @@
         }
         state.value = nightMode;
         state.label = mContext.getString(R.string.quick_settings_ui_mode_night_label);
-        state.icon = mIcon;
         state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
                 ? state.label
                 : TextUtils.concat(state.label, ", ", state.secondaryLabel);
@@ -164,6 +163,9 @@
         } else {
             state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         }
+        state.icon = ResourceIcon.get(state.state == Tile.STATE_ACTIVE
+                ? R.drawable.qs_light_dark_theme_icon_on
+                : R.drawable.qs_light_dark_theme_icon_off);
         state.showRippleEffect = false;
         state.expandedAccessibilityClassName = Switch.class.getName();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index ba26cfa..df81c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1369,6 +1369,8 @@
         }
         ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
         View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
+        LinearLayout actionListMarginTarget = layout.findViewById(
+                com.android.internal.R.id.notification_action_list_margin_target);
         if (bubbleButton == null || actionContainer == null) {
             return;
         }
@@ -1393,6 +1395,16 @@
             bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
             bubbleButton.setVisibility(VISIBLE);
             actionContainer.setVisibility(VISIBLE);
+            // Set notification_action_list_margin_target's bottom margin to 0 when showing bubble
+            if (actionListMarginTarget != null) {
+                ViewGroup.LayoutParams lp = actionListMarginTarget.getLayoutParams();
+                if (lp instanceof ViewGroup.MarginLayoutParams) {
+                    final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+                    if (mlp.bottomMargin > 0) {
+                        mlp.setMargins(mlp.leftMargin, mlp.topMargin, mlp.rightMargin, 0);
+                    }
+                }
+            }
         } else  {
             bubbleButton.setVisibility(GONE);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 0b2d443..4576a64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -23,7 +23,6 @@
 import android.hardware.biometrics.BiometricFaceConstants;
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.metrics.LogMaker;
 import android.os.Handler;
@@ -654,10 +653,7 @@
         final boolean fingerprintLockout = biometricSourceType == BiometricSourceType.FINGERPRINT
                 && (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
                 || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT);
-        final boolean faceLockout = biometricSourceType == BiometricSourceType.FACE
-                && (msgId == FaceManager.FACE_ERROR_LOCKOUT
-                || msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT);
-        if (fingerprintLockout || faceLockout) {
+        if (fingerprintLockout) {
             startWakeAndUnlock(MODE_SHOW_BOUNCER);
             UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 7463dac..9603de9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -99,11 +99,14 @@
 import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.DateTimeView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.NonNull;
 import androidx.lifecycle.Lifecycle;
@@ -509,10 +512,17 @@
     private CentralSurfacesComponent mCentralSurfacesComponent;
 
     // Flags for disabling the status bar
-    // Two variables becaseu the first one evidently ran out of room for new flags.
+    // Two variables because the first one evidently ran out of room for new flags.
     private int mDisabled1 = 0;
     private int mDisabled2 = 0;
 
+    /**
+     * This keeps track of whether we have (or haven't) registered the predictive back callback.
+     * Since we can have visible -> visible transitions, we need to avoid
+     * double-registering (or double-unregistering) our callback.
+     */
+    private boolean mIsBackCallbackRegistered = false;
+
     /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
     private @Appearance int mAppearance;
 
@@ -636,6 +646,12 @@
 
     private final InteractionJankMonitor mJankMonitor;
 
+    private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
+        if (DEBUG) {
+            Log.d(TAG, "mOnBackInvokedCallback() called");
+        }
+        onBackPressed();
+    };
 
     /**
      * Public constructor for CentralSurfaces.
@@ -2731,9 +2747,38 @@
         if (visibleToUser) {
             handleVisibleToUserChangedImpl(visibleToUser);
             mNotificationLogger.startNotificationLogging();
+
+            if (!mIsBackCallbackRegistered) {
+                ViewRootImpl viewRootImpl = getViewRootImpl();
+                if (viewRootImpl != null) {
+                    viewRootImpl.getOnBackInvokedDispatcher()
+                            .registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                                    mOnBackInvokedCallback);
+                    mIsBackCallbackRegistered = true;
+                    if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered");
+                }
+            } else {
+                if (DEBUG) Log.d(TAG, "is now VISIBLE to user, BUT callback ALREADY unregistered");
+            }
         } else {
             mNotificationLogger.stopNotificationLogging();
             handleVisibleToUserChangedImpl(visibleToUser);
+
+            if (mIsBackCallbackRegistered) {
+                ViewRootImpl viewRootImpl = getViewRootImpl();
+                if (viewRootImpl != null) {
+                    viewRootImpl.getOnBackInvokedDispatcher()
+                            .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+                    mIsBackCallbackRegistered = false;
+                    if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered");
+                }
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG,
+                            "is NOT VISIBLE to user, BUT NO callback (or callback ALREADY "
+                                    + "unregistered)");
+                }
+            }
         }
     }
 
@@ -3444,6 +3489,12 @@
         return mNotificationPanelViewController.getKeyguardBottomAreaView();
     }
 
+    protected ViewRootImpl getViewRootImpl()  {
+        NotificationShadeWindowView nswv = getNotificationShadeWindowView();
+        if (nswv != null) return nswv.getViewRootImpl();
+
+        return null;
+    }
     /**
      * Propagation of the bouncer state, indicating that it's fully visible.
      */
@@ -3761,6 +3812,12 @@
         updateScrimController();
     }
 
+    @VisibleForTesting
+    public void setNotificationShadeWindowViewController(
+            NotificationShadeWindowViewController nswvc) {
+        mNotificationShadeWindowViewController = nswvc;
+    }
+
     /**
      * Set the amount of progress we are currently in if we're transitioning to the full shade.
      * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
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 e63c383..44aef7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -220,6 +220,7 @@
                     && !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
                             KeyguardUpdateMonitor.getCurrentUser())
                     && !needsFullscreenBouncer()
+                    && !mKeyguardUpdateMonitor.isFaceLockedOut()
                     && !mKeyguardUpdateMonitor.userNeedsStrongAuth()
                     && !mKeyguardBypassController.getBypassEnabled()) {
                 mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index 0bf038d..2714cf4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -90,6 +90,8 @@
     onlyFaceEnrolled = false,
     faceAuthenticated = false,
     faceDisabled = false,
+    faceLockedOut = false,
+    fpLockedOut = false,
     goingToSleep = false,
     keyguardAwakeExcludingBouncerShowing = false,
     keyguardGoingAway = false,
@@ -99,5 +101,5 @@
     scanningAllowedByStrongAuth = false,
     secureCameraLaunched = false,
     switchingUser = false,
-    udfpsBouncerShowing = false
+    udfpsBouncerShowing = false,
 )
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
new file mode 100644
index 0000000..9e5bfe5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.statusbar.policy.DevicePostureController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPinViewControllerTest : SysuiTestCase() {
+    @Mock private lateinit var keyguardPinView: KeyguardPINView
+
+    @Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea
+
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+    @Mock private lateinit var securityMode: SecurityMode
+
+    @Mock private lateinit var lockPatternUtils: LockPatternUtils
+
+    @Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
+
+    @Mock
+    private lateinit var keyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
+
+    @Mock
+    private lateinit var keyguardMessageAreaController:
+        KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+
+    @Mock private lateinit var mLatencyTracker: LatencyTracker
+
+    @Mock private lateinit var liftToActivateListener: LiftToActivateListener
+
+    @Mock private val mEmergencyButtonController: EmergencyButtonController? = null
+    private val falsingCollector: FalsingCollector = FalsingCollectorFake()
+    @Mock lateinit var postureController: DevicePostureController
+
+    lateinit var pinViewController: KeyguardPinViewController
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        Mockito.`when`(keyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+            .thenReturn(keyguardMessageArea)
+        Mockito.`when`(
+                keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
+            )
+            .thenReturn(keyguardMessageAreaController)
+        pinViewController =
+            KeyguardPinViewController(
+                keyguardPinView,
+                keyguardUpdateMonitor,
+                securityMode,
+                lockPatternUtils,
+                mKeyguardSecurityCallback,
+                keyguardMessageAreaControllerFactory,
+                mLatencyTracker,
+                liftToActivateListener,
+                mEmergencyButtonController,
+                falsingCollector,
+                postureController
+            )
+    }
+
+    @Test
+    fun startAppearAnimation() {
+        pinViewController.startAppearAnimation()
+        verify(keyguardMessageAreaController).setMessageIfEmpty(R.string.keyguard_enter_your_pin)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 093e592..508f81d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -20,6 +20,7 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
 
@@ -751,8 +752,7 @@
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
 
-        mKeyguardUpdateMonitor.mFaceAuthenticationCallback
-                .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+        faceAuthLockedOut();
 
         verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
     }
@@ -764,7 +764,7 @@
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
 
         mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
-                .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
+                .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
 
         verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
     }
@@ -775,10 +775,9 @@
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
 
-        mKeyguardUpdateMonitor.mFaceAuthenticationCallback
-                .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+        faceAuthLockedOut();
         mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
-                .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
+                .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
 
         verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
     }
@@ -1217,7 +1216,7 @@
     public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
         // Face auth should run when the following is true.
         keyguardNotGoingAway();
-        bouncerFullyVisibleAndNotGoingToSleep();
+        occludingAppRequestsFaceAuth();
         currentUserIsPrimary();
         strongAuthNotRequired();
         biometricsEnabledForCurrentUser();
@@ -1225,6 +1224,7 @@
         biometricsNotDisabledThroughDevicePolicyManager();
         userNotCurrentlySwitching();
         mTestableLooper.processAllMessages();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
 
         // Fingerprint is locked out.
         fingerprintErrorLockedOut();
@@ -1500,6 +1500,27 @@
     }
 
     @Test
+    public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
+            throws RemoteException {
+        // Preconditions for face auth to run
+        keyguardNotGoingAway();
+        currentUserIsPrimary();
+        currentUserDoesNotHaveTrust();
+        biometricsNotDisabledThroughDevicePolicyManager();
+        biometricsEnabledForCurrentUser();
+        userNotCurrentlySwitching();
+        mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
+        mTestableLooper.processAllMessages();
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+        // Face is locked out.
+        faceAuthLockedOut();
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+    }
+
+    @Test
     public void testBouncerVisibility_whenBothFingerprintAndFaceIsEnrolled_stopsFaceAuth()
             throws RemoteException {
         // Both fingerprint and face are enrolled by default
@@ -1587,6 +1608,11 @@
         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
     }
 
+    private void faceAuthLockedOut() {
+        mKeyguardUpdateMonitor.mFaceAuthenticationCallback
+                .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+    }
+
     private void faceAuthEnabled() {
         // this ensures KeyguardUpdateMonitor updates the cached mIsFaceEnrolled flag using the
         // face manager mock wire-up in setup()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
new file mode 100644
index 0000000..ea70c26
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.app.UiModeManager
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.os.Handler
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.LocationController
+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.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class UiModeNightTileTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var mockContext: Context
+    @Mock
+    private lateinit var uiModeManager: UiModeManager
+    @Mock
+    private lateinit var resources: Resources
+    @Mock
+    private lateinit var qsLogger: QSLogger
+    @Mock
+    private lateinit var qsHost: QSTileHost
+    @Mock
+    private lateinit var metricsLogger: MetricsLogger
+    @Mock
+    private lateinit var statusBarStateController: StatusBarStateController
+    @Mock
+    private lateinit var activityStarter: ActivityStarter
+    @Mock
+    private lateinit var configurationController: ConfigurationController
+    @Mock
+    private lateinit var batteryController: BatteryController
+    @Mock
+    private lateinit var locationController: LocationController
+
+    private val uiEventLogger = UiEventLoggerFake()
+    private val falsingManager = FalsingManagerFake()
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var tile: UiModeNightTile
+    private lateinit var configuration: Configuration
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        configuration = Configuration()
+        mContext.addMockSystemService(Context.UI_MODE_SERVICE, uiModeManager)
+
+        `when`(qsHost.context).thenReturn(mockContext)
+        `when`(qsHost.userContext).thenReturn(mContext)
+        `when`(mockContext.resources).thenReturn(resources)
+        `when`(resources.configuration).thenReturn(configuration)
+        `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+        tile = UiModeNightTile(
+                qsHost,
+                testableLooper.looper,
+                Handler(testableLooper.looper),
+                falsingManager,
+                metricsLogger,
+                statusBarStateController,
+                activityStarter,
+                qsLogger,
+                configurationController,
+                batteryController,
+                locationController)
+    }
+
+    @Test
+    fun testIcon_whenNightModeOn_isOnState() {
+        val state = QSTile.BooleanState()
+        setNightModeOn()
+
+        tile.handleUpdateState(state, /* arg= */ null)
+
+        assertThat(state.icon)
+                .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_on))
+    }
+
+    @Test
+    fun testIcon_whenNightModeOn_isOffState() {
+        val state = QSTile.BooleanState()
+        setNightModeOff()
+
+        tile.handleUpdateState(state, /* arg= */ null)
+
+        assertThat(state.icon)
+                .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_off))
+    }
+
+    private fun setNightModeOn() {
+        `when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_YES)
+        configuration.uiMode = Configuration.UI_MODE_NIGHT_YES
+    }
+
+    private fun setNightModeOff() {
+        `when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_NO)
+        configuration.uiMode = Configuration.UI_MODE_NIGHT_NO
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 14b7471..f510e48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -69,7 +70,11 @@
 import android.util.SparseArray;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.ViewRootImpl;
 import android.view.WindowManager;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
 
 import androidx.test.filters.SmallTest;
 
@@ -168,6 +173,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -279,6 +285,15 @@
     @Mock private InteractionJankMonitor mJankMonitor;
     @Mock private DeviceStateManager mDeviceStateManager;
     @Mock private WiredChargingRippleController mWiredChargingRippleController;
+    /**
+     * The process of registering/unregistering a predictive back callback requires a
+     * ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test.
+     * To prevent an NPE during test execution, we explicitly craft and provide a fake ViewRootImpl.
+     */
+    @Mock private ViewRootImpl mViewRootImpl;
+    @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
+    @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
+
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -368,10 +383,10 @@
             return null;
         }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
 
-        mShadeController = new ShadeControllerImpl(mCommandQueue,
+        mShadeController = spy(new ShadeControllerImpl(mCommandQueue,
                 mStatusBarStateController, mNotificationShadeWindowController,
                 mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
-                () -> Optional.of(mCentralSurfaces), () -> mAssistManager);
+                () -> Optional.of(mCentralSurfaces), () -> mAssistManager));
 
         when(mOperatorNameViewControllerFactory.create(any()))
                 .thenReturn(mOperatorNameViewController);
@@ -460,7 +475,14 @@
                 mActivityLaunchAnimator,
                 mJankMonitor,
                 mDeviceStateManager,
-                mWiredChargingRippleController, mDreamManager);
+                mWiredChargingRippleController, mDreamManager) {
+            @Override
+            protected ViewRootImpl getViewRootImpl() {
+                return mViewRootImpl;
+            }
+        };
+        when(mViewRootImpl.getOnBackInvokedDispatcher())
+                .thenReturn(mOnBackInvokedDispatcher);
         when(mKeyguardViewMediator.registerCentralSurfaces(
                 any(CentralSurfacesImpl.class),
                 any(NotificationPanelViewController.class),
@@ -738,6 +760,43 @@
         }
     }
 
+    /**
+     * Do the following:
+     * 1. verify that a predictive back callback is registered when CSurf becomes visible
+     * 2. verify that the same callback is unregistered when CSurf becomes invisible
+     */
+    @Test
+    public void testPredictiveBackCallback_registration() {
+        mCentralSurfaces.handleVisibleToUserChanged(true);
+        verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+                mOnBackInvokedCallback.capture());
+
+        mCentralSurfaces.handleVisibleToUserChanged(false);
+        verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
+                eq(mOnBackInvokedCallback.getValue()));
+    }
+
+    /**
+     * Do the following:
+     * 1. capture the predictive back callback during registration
+     * 2. call the callback directly
+     * 3. verify that the ShadeController's panel collapse animation is invoked
+     */
+    @Test
+    public void testPredictiveBackCallback_invocationCollapsesPanel() {
+        mCentralSurfaces.setNotificationShadeWindowViewController(
+                mNotificationShadeWindowViewController);
+        mCentralSurfaces.handleVisibleToUserChanged(true);
+        verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+                mOnBackInvokedCallback.capture());
+
+        when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true);
+        mOnBackInvokedCallback.getValue().onBackInvoked();
+        verify(mShadeController).animateCollapsePanels();
+    }
+
     @Test
     public void testPanelOpenForHeadsUp() {
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 60a3d95..ab209d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -408,6 +408,16 @@
         mBouncer.hide(false /* destroyView */);
         verify(mHandler).removeCallbacks(eq(showRunnable.getValue()));
     }
+
+    @Test
+    public void testShow_doesNotDelaysIfFaceAuthIsLockedOut() {
+        when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
+        mBouncer.show(true /* reset */);
+
+        verify(mHandler, never()).postDelayed(any(), anyLong());
+    }
+
     @Test
     public void testShow_delaysIfFaceAuthIsRunning_unlessBypassEnabled() {
         when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 0bba3c9..9f27f72 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -150,8 +150,9 @@
         // Create a PendingIntent
         final long token = Binder.clearCallingIdentity();
         try {
-            return PendingIntent.getActivity(mContext, /*requestCode */ associationId, intent,
-                    FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
+            return PendingIntent.getActivityAsUser(mContext, /*requestCode */ associationId, intent,
+                    FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE, /* options= */ null,
+                    UserHandle.CURRENT);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 9f3f761..7ca6254 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -577,14 +577,6 @@
             int filterCallingUid);
 
     /**
-     * Resolves an exported activity intent, allowing instant apps to be resolved.
-     */
-    public abstract ResolveInfo resolveIntentExported(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlagsBits long flags,
-            @PrivateResolveFlags long privateResolveFlags, int userId, boolean resolveForStart,
-            int filterCallingUid);
-
-    /**
     * Resolves a service intent, allowing instant apps to be resolved.
     */
     public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
index e21a3d7..6b3f5e2 100644
--- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java
+++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
@@ -98,12 +98,17 @@
     }
 
     private String getCallingPackageName() {
-        final String[] packages = mContext.getPackageManager().getPackagesForUid(
-                Binder.getCallingUid());
+        final PackageManager pm = mContext.getPackageManager();
+        final int uid = Binder.getCallingUid();
+        final String[] packages = pm.getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
            return packages[0];
         }
-        return "unknown";
+        final String name = pm.getNameForUid(uid);
+        if (name != null) {
+            return name;
+        }
+        return String.valueOf(uid);
     }
 
     private void dumpTempValues(String pkg, PrintWriter pw, int type,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2ccba56..842498d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -215,7 +215,6 @@
 import android.appwidget.AppWidgetManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
-import android.compat.annotation.EnabledAfter;
 import android.content.AttributionSource;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
@@ -586,17 +585,6 @@
     private static final long DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED = 161145287L;
 
     /**
-     * Apps targeting Android U and above will need to export components in order to invoke them
-     * through implicit intents.
-     *
-     * If a component is not exported and invoked, it will be removed from the list of receivers.
-     * This applies specifically to activities and broadcasts.
-     */
-    @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
-    public static final long IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS = 229362273;
-
-    /**
      * The maximum number of bytes that {@link #setProcessStateSummary} accepts.
      *
      * @see {@link android.app.ActivityManager#setProcessStateSummary(byte[])}
@@ -12391,58 +12379,6 @@
     }
 
     /**
-     * Filters out non-exported components in a given list of broadcast filters
-     * @param intent the original intent
-     * @param callingUid the calling UID
-     * @param query the list of broadcast filters
-     * @param platformCompat the instance of platform compat
-     */
-    private static void filterNonExportedComponents(Intent intent, int callingUid,
-            List query, PlatformCompat platformCompat, String callerPackage) {
-        if (query == null
-                || intent.getPackage() != null
-                || intent.getComponent() != null
-                || ActivityManager.canAccessUnexportedComponents(callingUid)) {
-            return;
-        }
-        for (int i = query.size() - 1; i >= 0; i--) {
-            String componentInfo;
-            ResolveInfo resolveInfo;
-            BroadcastFilter broadcastFilter;
-            if (query.get(i) instanceof ResolveInfo) {
-                resolveInfo = (ResolveInfo) query.get(i);
-                if (resolveInfo.getComponentInfo().exported) {
-                    continue;
-                }
-                componentInfo = resolveInfo.getComponentInfo()
-                        .getComponentName().flattenToShortString();
-            } else if (query.get(i) instanceof BroadcastFilter) {
-                broadcastFilter = (BroadcastFilter) query.get(i);
-                if (broadcastFilter.exported) {
-                    continue;
-                }
-                componentInfo = broadcastFilter.packageName;
-            } else {
-                continue;
-            }
-            if (!platformCompat.isChangeEnabledByUid(
-                    IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS, callingUid)) {
-                Slog.w(TAG, "Non-exported component not filtered out "
-                        + "(will be filtered out once the app targets U+)- intent: "
-                        + intent.getAction() + ", component: "
-                        + componentInfo + ", sender: "
-                        + callerPackage);
-                return;
-            }
-            Slog.w(TAG, "Non-exported component filtered out - intent: "
-                    + intent.getAction() + ", component: "
-                    + componentInfo + ", sender: "
-                    + callerPackage);
-            query.remove(i);
-        }
-    }
-
-    /**
      * Main code for cleaning up a process when it has gone away.  This is
      * called both as a result of the process dying, or directly when stopping
      * a process when running in single process mode.
@@ -14281,8 +14217,6 @@
             }
         }
 
-        filterNonExportedComponents(intent, callingUid, registeredReceivers,
-                mPlatformCompat, callerPackage);
         int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
         if (!ordered && NR > 0) {
             // If we are not serializing this broadcast, then send the
@@ -14385,8 +14319,6 @@
         if ((receivers != null && receivers.size() > 0)
                 || resultTo != null) {
             BroadcastQueue queue = broadcastQueueForIntent(intent);
-            filterNonExportedComponents(intent, callingUid, receivers,
-                    mPlatformCompat, callerPackage);
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                     callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
@@ -16464,11 +16396,6 @@
         }
     }
 
-    @Override
-    public void enableBinderTracing() {
-        Binder.enableTracingForUid(Binder.getCallingUid());
-    }
-
     @VisibleForTesting
     public final class LocalService extends ActivityManagerInternal
             implements ActivityManagerLocal {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 62ae9b8..8b11c39 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -267,10 +267,6 @@
             OP_CAMERA,
     };
 
-    private static final int[] WATCHABLE_NON_PERMISSION_OPS = {
-            OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
-    };
-
     private static final int MAX_UNFORWARDED_OPS = 10;
     private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
     private static final int RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS = 300000;
@@ -4252,7 +4248,7 @@
     public boolean shouldCollectNotes(int opCode) {
         Preconditions.checkArgumentInRange(opCode, 0, _NUM_OP - 1, "opCode");
 
-        if (ArrayUtils.contains(WATCHABLE_NON_PERMISSION_OPS, opCode)) {
+        if (AppOpsManager.shouldForceCollectNoteForOp(opCode)) {
             return true;
         }
 
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 454894e..e5d40d9 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -1150,18 +1150,29 @@
                     }
                     return builder.toString();
                 case AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED:
-                    builder.append(" muteFromMasterMute:").append(
-                            (mEventValue & PLAYER_MUTE_MASTER) != 0);
-                    builder.append(" muteFromStreamVolume:").append(
-                            (mEventValue & PLAYER_MUTE_STREAM_VOLUME) != 0);
-                    builder.append(" muteFromStreamMute:").append(
-                            (mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0);
-                    builder.append(" muteFromPlaybackRestricted:").append(
-                            (mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0);
-                    builder.append(" muteFromClientVolume:").append(
-                            (mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0);
-                    builder.append(" muteFromVolumeShaper:").append(
-                            (mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0);
+                    builder.append(" source:");
+                    if (mEventValue <= 0) {
+                        builder.append("none ");
+                    } else {
+                        if ((mEventValue & PLAYER_MUTE_MASTER) != 0) {
+                            builder.append("masterMute ");
+                        }
+                        if ((mEventValue & PLAYER_MUTE_STREAM_VOLUME) != 0) {
+                            builder.append("streamVolume ");
+                        }
+                        if ((mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0) {
+                            builder.append("streamMute ");
+                        }
+                        if ((mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0) {
+                            builder.append("playbackRestricted ");
+                        }
+                        if ((mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0) {
+                            builder.append("clientVolume ");
+                        }
+                        if ((mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0) {
+                            builder.append("volumeShaper ");
+                        }
+                    }
                     return builder.toString();
                 default:
                     return builder.toString();
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 431be88..676dc19 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -99,6 +99,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.FileUtils;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Looper;
@@ -2784,26 +2785,18 @@
             // When restricted to test networks, select any network with TRANSPORT_TEST. Since the
             // creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS,
             // this is considered safe.
-            final NetworkRequest req;
 
             if (mProfile.isRestrictedToTestNetworks()) {
-                req = new NetworkRequest.Builder()
+                final NetworkRequest req = new NetworkRequest.Builder()
                         .clearCapabilities()
                         .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
                         .addCapability(NET_CAPABILITY_NOT_VPN)
                         .build();
+                mConnectivityManager.requestNetwork(req, mNetworkCallback);
             } else {
-                // Basically, the request here is referring to the default request which is defined
-                // in ConnectivityService. Ideally, ConnectivityManager should provide an new API
-                // which can provide the status of physical network even though there is a virtual
-                // network. b/147280869 is used for tracking the new API.
-                // TODO: Use the new API to register default physical network.
-                req = new NetworkRequest.Builder()
-                        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                        .build();
+                mConnectivityManager.registerSystemDefaultNetworkCallback(mNetworkCallback,
+                        new Handler(mLooper));
             }
-
-            mConnectivityManager.requestNetwork(req, mNetworkCallback);
         }
 
         private boolean isActiveNetwork(@Nullable Network network) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 6145a91c..b3aee22 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -131,6 +131,8 @@
     // Configuration object for determining thresholds to change brightness dynamically
     private final HysteresisLevels mAmbientBrightnessThresholds;
     private final HysteresisLevels mScreenBrightnessThresholds;
+    private final HysteresisLevels mAmbientBrightnessThresholdsIdle;
+    private final HysteresisLevels mScreenBrightnessThresholdsIdle;
 
     private boolean mLoggingEnabled;
 
@@ -242,7 +244,9 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, Context context,
+            HysteresisLevels screenBrightnessThresholds,
+            HysteresisLevels ambientBrightnessThresholdsIdle,
+            HysteresisLevels screenBrightnessThresholdsIdle, Context context,
             HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
             BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
             int ambientLightHorizonLong) {
@@ -251,7 +255,8 @@
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
-                ambientBrightnessThresholds, screenBrightnessThresholds, context,
+                ambientBrightnessThresholds, screenBrightnessThresholds,
+                ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context,
                 hbmController, brightnessThrottler, idleModeBrightnessMapper,
                 ambientLightHorizonShort, ambientLightHorizonLong
         );
@@ -265,7 +270,9 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, Context context,
+            HysteresisLevels screenBrightnessThresholds,
+            HysteresisLevels ambientBrightnessThresholdsIdle,
+            HysteresisLevels screenBrightnessThresholdsIdle, Context context,
             HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
             BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
             int ambientLightHorizonLong) {
@@ -289,7 +296,9 @@
         mAmbientLightHorizonShort = ambientLightHorizonShort;
         mWeightingIntercept = ambientLightHorizonLong;
         mAmbientBrightnessThresholds = ambientBrightnessThresholds;
+        mAmbientBrightnessThresholdsIdle = ambientBrightnessThresholdsIdle;
         mScreenBrightnessThresholds = screenBrightnessThresholds;
+        mScreenBrightnessThresholdsIdle = screenBrightnessThresholdsIdle;
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
         mHandler = new AutomaticBrightnessHandler(looper);
@@ -586,6 +595,8 @@
         pw.println();
         mAmbientBrightnessThresholds.dump(pw);
         mScreenBrightnessThresholds.dump(pw);
+        mScreenBrightnessThresholdsIdle.dump(pw);
+        mScreenBrightnessThresholdsIdle.dump(pw);
     }
 
     private String configStateToString(int state) {
@@ -680,8 +691,17 @@
             lux = 0;
         }
         mAmbientLux = lux;
-        mAmbientBrighteningThreshold = mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
-        mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
+        if (isInIdleMode()) {
+            mAmbientBrighteningThreshold =
+                    mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(lux);
+            mAmbientDarkeningThreshold =
+                    mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(lux);
+        } else {
+            mAmbientBrighteningThreshold =
+                    mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
+            mAmbientDarkeningThreshold =
+                    mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
+        }
         mHbmController.onAmbientLuxChange(mAmbientLux);
 
         // If the short term model was invalidated and the change is drastic enough, reset it.
@@ -903,10 +923,20 @@
                 mPreThresholdBrightness = mScreenAutoBrightness;
             }
             mScreenAutoBrightness = newScreenAutoBrightness;
-            mScreenBrighteningThreshold = clampScreenBrightness(
-                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
-            mScreenDarkeningThreshold = clampScreenBrightness(
-                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
+            if (isInIdleMode()) {
+                mScreenBrighteningThreshold = clampScreenBrightness(
+                        mScreenBrightnessThresholdsIdle.getBrighteningThreshold(
+                                newScreenAutoBrightness));
+                mScreenDarkeningThreshold = clampScreenBrightness(
+                        mScreenBrightnessThresholdsIdle.getDarkeningThreshold(
+                                newScreenAutoBrightness));
+            } else {
+                mScreenBrighteningThreshold = clampScreenBrightness(
+                        mScreenBrightnessThresholds.getBrighteningThreshold(
+                                newScreenAutoBrightness));
+                mScreenDarkeningThreshold = clampScreenBrightness(
+                        mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
+            }
 
             if (sendUpdate) {
                 mCallbacks.updateBrightness();
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 12b2f47..4165186 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -188,8 +188,8 @@
  *      <ambientLightHorizonLong>10001</ambientLightHorizonLong>
  *      <ambientLightHorizonShort>2001</ambientLightHorizonShort>
  *
- *      <displayBrightnessChangeThresholds>
- *        <brighteningThresholds>
+ *      <displayBrightnessChangeThresholds> // Thresholds for screen changes
+ *        <brighteningThresholds>     // Thresholds for active mode brightness changes.
  *          <minimum>0.001</minimum>  // Minimum change needed in screen brightness to brighten.
  *        </brighteningThresholds>
  *        <darkeningThresholds>
@@ -197,8 +197,8 @@
  *        </darkeningThresholds>
  *      </displayBrightnessChangeThresholds>
  *
- *      <ambientBrightnessChangeThresholds>
- *        <brighteningThresholds>
+ *      <ambientBrightnessChangeThresholds> // Thresholds for lux changes
+ *        <brighteningThresholds>     // Thresholds for active mode brightness changes.
  *          <minimum>0.003</minimum>  // Minimum change needed in ambient brightness to brighten.
  *        </brighteningThresholds>
  *        <darkeningThresholds>
@@ -206,6 +206,24 @@
  *        </darkeningThresholds>
  *      </ambientBrightnessChangeThresholds>
  *
+ *      <displayBrightnessChangeThresholdsIdle> // Thresholds for screen changes in idle mode
+ *        <brighteningThresholds>     // Thresholds for idle mode brightness changes.
+ *          <minimum>0.001</minimum>  // Minimum change needed in screen brightness to brighten.
+ *        </brighteningThresholds>
+ *        <darkeningThresholds>
+ *          <minimum>0.002</minimum>  // Minimum change needed in screen brightness to darken.
+ *        </darkeningThresholds>
+ *      </displayBrightnessChangeThresholdsIdle>
+ *
+ *      <ambientBrightnessChangeThresholdsIdle> // Thresholds for lux changes in idle mode
+ *        <brighteningThresholds>     // Thresholds for idle mode brightness changes.
+ *          <minimum>0.003</minimum>  // Minimum change needed in ambient brightness to brighten.
+ *        </brighteningThresholds>
+ *        <darkeningThresholds>
+ *          <minimum>0.004</minimum>  // Minimum change needed in ambient brightness to darken.
+ *        </darkeningThresholds>
+ *      </ambientBrightnessChangeThresholdsIdle>
+ *
  *    </displayConfiguration>
  *  }
  *  </pre>
@@ -319,9 +337,13 @@
     private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
     private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
     private float mScreenBrighteningMinThreshold = 0.0f;     // Retain behaviour as though there is
-    private float mScreenDarkeningMinThreshold = 0.0f;       // no minimum threshold for change in
-    private float mAmbientLuxBrighteningMinThreshold = 0.0f; // screen brightness or ambient
-    private float mAmbientLuxDarkeningMinThreshold = 0.0f;   // brightness.
+    private float mScreenBrighteningMinThresholdIdle = 0.0f; // no minimum threshold for change in
+    private float mScreenDarkeningMinThreshold = 0.0f;       // screen brightness or ambient
+    private float mScreenDarkeningMinThresholdIdle = 0.0f;   // brightness.
+    private float mAmbientLuxBrighteningMinThreshold = 0.0f;
+    private float mAmbientLuxBrighteningMinThresholdIdle = 0.0f;
+    private float mAmbientLuxDarkeningMinThreshold = 0.0f;
+    private float mAmbientLuxDarkeningMinThresholdIdle = 0.0f;
     private Spline mBrightnessToBacklightSpline;
     private Spline mBacklightToBrightnessSpline;
     private Spline mBacklightToNitsSpline;
@@ -625,22 +647,76 @@
         return mAmbientHorizonShort;
     }
 
+    /**
+     * The minimum value for the screen brightness increase to actually occur.
+     * @return float value in brightness scale of 0 - 1.
+     */
     public float getScreenBrighteningMinThreshold() {
         return mScreenBrighteningMinThreshold;
     }
 
+    /**
+     * The minimum value for the screen brightness decrease to actually occur.
+     * @return float value in brightness scale of 0 - 1.
+     */
     public float getScreenDarkeningMinThreshold() {
         return mScreenDarkeningMinThreshold;
     }
 
+    /**
+     * The minimum value for the screen brightness increase to actually occur while in idle screen
+     * brightness mode.
+     * @return float value in brightness scale of 0 - 1.
+     */
+    public float getScreenBrighteningMinThresholdIdle() {
+        return mScreenBrighteningMinThresholdIdle;
+    }
+
+    /**
+     * The minimum value for the screen brightness decrease to actually occur while in idle screen
+     * brightness mode.
+     * @return float value in brightness scale of 0 - 1.
+     */
+    public float getScreenDarkeningMinThresholdIdle() {
+        return mScreenDarkeningMinThresholdIdle;
+    }
+
+    /**
+     * The minimum value for the ambient lux increase for a screen brightness change to actually
+     * occur.
+     * @return float value in brightness scale of 0 - 1.
+     */
     public float getAmbientLuxBrighteningMinThreshold() {
         return mAmbientLuxBrighteningMinThreshold;
     }
 
+    /**
+     * The minimum value for the ambient lux decrease for a screen brightness change to actually
+     * occur.
+     * @return float value in brightness scale of 0 - 1.
+     */
     public float getAmbientLuxDarkeningMinThreshold() {
         return mAmbientLuxDarkeningMinThreshold;
     }
 
+    /**
+     * The minimum value for the ambient lux increase for a screen brightness change to actually
+     * occur while in idle screen brightness mode.
+     * @return float value in brightness scale of 0 - 1.
+     */
+    public float getAmbientLuxBrighteningMinThresholdIdle() {
+        return mAmbientLuxBrighteningMinThresholdIdle;
+    }
+
+    /**
+     * The minimum value for the ambient lux decrease for a screen brightness change to actually
+     * occur while in idle screen brightness mode.
+     * @return float value in brightness scale of 0 - 1.
+     */
+    public float getAmbientLuxDarkeningMinThresholdIdle() {
+        return mAmbientLuxDarkeningMinThresholdIdle;
+    }
+
     SensorData getAmbientLightSensor() {
         return mAmbientLightSensor;
     }
@@ -745,9 +821,14 @@
                 + ", mAmbientHorizonLong=" + mAmbientHorizonLong
                 + ", mAmbientHorizonShort=" + mAmbientHorizonShort
                 + ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold
+                + ", mScreenDarkeningMinThresholdIdle=" + mScreenDarkeningMinThresholdIdle
                 + ", mScreenBrighteningMinThreshold=" + mScreenBrighteningMinThreshold
+                + ", mScreenBrighteningMinThresholdIdle=" + mScreenBrighteningMinThresholdIdle
                 + ", mAmbientLuxDarkeningMinThreshold=" + mAmbientLuxDarkeningMinThreshold
+                + ", mAmbientLuxDarkeningMinThresholdIdle=" + mAmbientLuxDarkeningMinThresholdIdle
                 + ", mAmbientLuxBrighteningMinThreshold=" + mAmbientLuxBrighteningMinThreshold
+                + ", mAmbientLuxBrighteningMinThresholdIdle="
+                + mAmbientLuxBrighteningMinThresholdIdle
                 + ", mAmbientLightSensor=" + mAmbientLightSensor
                 + ", mProximitySensor=" + mProximitySensor
                 + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
@@ -1376,24 +1457,34 @@
     private void loadBrightnessChangeThresholds(DisplayConfiguration config) {
         Thresholds displayBrightnessThresholds = config.getDisplayBrightnessChangeThresholds();
         Thresholds ambientBrightnessThresholds = config.getAmbientBrightnessChangeThresholds();
+        Thresholds displayBrightnessThresholdsIdle =
+                config.getDisplayBrightnessChangeThresholdsIdle();
+        Thresholds ambientBrightnessThresholdsIdle =
+                config.getAmbientBrightnessChangeThresholdsIdle();
 
+        loadDisplayBrightnessThresholds(displayBrightnessThresholds);
+        loadAmbientBrightnessThresholds(ambientBrightnessThresholds);
+        loadIdleDisplayBrightnessThresholds(displayBrightnessThresholdsIdle);
+        loadIdleAmbientBrightnessThresholds(ambientBrightnessThresholdsIdle);
+    }
+
+    private void loadDisplayBrightnessThresholds(Thresholds displayBrightnessThresholds) {
         if (displayBrightnessThresholds != null) {
             BrightnessThresholds brighteningScreen =
                     displayBrightnessThresholds.getBrighteningThresholds();
             BrightnessThresholds darkeningScreen =
                     displayBrightnessThresholds.getDarkeningThresholds();
 
-            final BigDecimal screenBrighteningThreshold = brighteningScreen.getMinimum();
-            final BigDecimal screenDarkeningThreshold = darkeningScreen.getMinimum();
-
-            if (screenBrighteningThreshold != null) {
-                mScreenBrighteningMinThreshold = screenBrighteningThreshold.floatValue();
+            if (brighteningScreen != null && brighteningScreen.getMinimum() != null) {
+                mScreenBrighteningMinThreshold = brighteningScreen.getMinimum().floatValue();
             }
-            if (screenDarkeningThreshold != null) {
-                mScreenDarkeningMinThreshold = screenDarkeningThreshold.floatValue();
+            if (darkeningScreen != null && darkeningScreen.getMinimum() != null) {
+                mScreenDarkeningMinThreshold = darkeningScreen.getMinimum().floatValue();
             }
         }
+    }
 
+    private void loadAmbientBrightnessThresholds(Thresholds ambientBrightnessThresholds) {
         if (ambientBrightnessThresholds != null) {
             BrightnessThresholds brighteningAmbientLux =
                     ambientBrightnessThresholds.getBrighteningThresholds();
@@ -1412,6 +1503,44 @@
         }
     }
 
+    private void loadIdleDisplayBrightnessThresholds(Thresholds idleDisplayBrightnessThresholds) {
+        if (idleDisplayBrightnessThresholds != null) {
+            BrightnessThresholds brighteningScreenIdle =
+                    idleDisplayBrightnessThresholds.getBrighteningThresholds();
+            BrightnessThresholds darkeningScreenIdle =
+                    idleDisplayBrightnessThresholds.getDarkeningThresholds();
+
+            if (brighteningScreenIdle != null
+                    && brighteningScreenIdle.getMinimum() != null) {
+                mScreenBrighteningMinThresholdIdle =
+                        brighteningScreenIdle.getMinimum().floatValue();
+            }
+            if (darkeningScreenIdle != null && darkeningScreenIdle.getMinimum() != null) {
+                mScreenDarkeningMinThresholdIdle =
+                        darkeningScreenIdle.getMinimum().floatValue();
+            }
+        }
+    }
+
+    private void loadIdleAmbientBrightnessThresholds(Thresholds idleAmbientBrightnessThresholds) {
+        if (idleAmbientBrightnessThresholds != null) {
+            BrightnessThresholds brighteningAmbientLuxIdle =
+                    idleAmbientBrightnessThresholds.getBrighteningThresholds();
+            BrightnessThresholds darkeningAmbientLuxIdle =
+                    idleAmbientBrightnessThresholds.getDarkeningThresholds();
+
+            if (brighteningAmbientLuxIdle != null
+                    && brighteningAmbientLuxIdle.getMinimum() != null) {
+                mAmbientLuxBrighteningMinThresholdIdle =
+                        brighteningAmbientLuxIdle.getMinimum().floatValue();
+            }
+            if (darkeningAmbientLuxIdle != null && darkeningAmbientLuxIdle.getMinimum() != null) {
+                mAmbientLuxDarkeningMinThresholdIdle =
+                        darkeningAmbientLuxIdle.getMinimum().floatValue();
+            }
+        }
+    }
+
     private boolean thermalStatusIsValid(ThermalStatus value) {
         if (value == null) {
             return false;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e53ac37..fd30d7e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -200,6 +200,8 @@
     private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";
 
     private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
+    private static final String PROP_USE_NEW_DISPLAY_POWER_CONTROLLER =
+            "persist.sys.use_new_display_power_controller";
     private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
     // This value needs to be in sync with the threshold
     // in RefreshRateConfigs::getFrameRateDivisor.
@@ -274,7 +276,7 @@
             new CopyOnWriteArrayList<>();
 
     /** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
-    private final SparseArray<DisplayPowerController> mDisplayPowerControllers =
+    private final SparseArray<DisplayPowerControllerInterface> mDisplayPowerControllers =
             new SparseArray<>();
 
     /** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */
@@ -497,7 +499,6 @@
         ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
         mWideColorSpace = colorSpaces[1];
         mAllowNonNativeRefreshRateOverride = mInjector.getAllowNonNativeRefreshRateOverride();
-
         mSystemReady = false;
     }
 
@@ -577,7 +578,7 @@
                 if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
                     return;
                 }
-                final DisplayPowerController dpc = mDisplayPowerControllers.get(
+                final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
                         logicalDisplay.getDisplayIdLocked());
                 if (dpc == null) {
                     return;
@@ -1557,7 +1558,7 @@
         scheduleTraversalLocked(false);
         mPersistentDataStore.saveIfNeeded();
 
-        DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+        DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
             dpc.onDisplayChanged();
         }
@@ -1575,7 +1576,8 @@
 
     private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
         final int displayId = display.getDisplayIdLocked();
-        final DisplayPowerController dpc = mDisplayPowerControllers.removeReturnOld(displayId);
+        final DisplayPowerControllerInterface dpc =
+                mDisplayPowerControllers.removeReturnOld(displayId);
         if (dpc != null) {
             dpc.stop();
         }
@@ -1604,7 +1606,7 @@
             mHandler.post(work);
         }
         final int displayId = display.getDisplayIdLocked();
-        DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+        DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
             dpc.onDisplayChanged();
         }
@@ -1615,7 +1617,7 @@
 
     private void handleLogicalDisplayDeviceStateTransitionLocked(@NonNull LogicalDisplay display) {
         final int displayId = display.getDisplayIdLocked();
-        final DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+        final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
             dpc.onDeviceStateTransition();
         }
@@ -1852,14 +1854,14 @@
             if (userId != mCurrentUserId) {
                 return;
             }
-            DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+            DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
             if (dpc != null) {
                 dpc.setBrightnessConfiguration(c);
             }
         }
     }
 
-    private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) {
+    private DisplayPowerControllerInterface getDpcFromUniqueIdLocked(String uniqueId) {
         final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
         final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice);
         if (logicalDisplay != null) {
@@ -1900,7 +1902,7 @@
                 final BrightnessConfiguration config =
                         getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial);
                 if (config != null) {
-                    final DisplayPowerController dpc = mDisplayPowerControllers.get(
+                    final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
                             logicalDisplay.getDisplayIdLocked());
                     if (dpc != null) {
                         dpc.setBrightnessConfiguration(config);
@@ -2132,8 +2134,8 @@
 
     void setAutoBrightnessLoggingEnabled(boolean enabled) {
         synchronized (mSyncRoot) {
-            final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
-                    Display.DEFAULT_DISPLAY);
+            final DisplayPowerControllerInterface displayPowerController =
+                    mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
             if (displayPowerController != null) {
                 displayPowerController.setAutoBrightnessLoggingEnabled(enabled);
             }
@@ -2142,8 +2144,8 @@
 
     void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
         synchronized (mSyncRoot) {
-            final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
-                    Display.DEFAULT_DISPLAY);
+            final DisplayPowerControllerInterface displayPowerController =
+                    mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
             if (displayPowerController != null) {
                 displayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
             }
@@ -2170,8 +2172,8 @@
 
     void setAmbientColorTemperatureOverride(float cct) {
         synchronized (mSyncRoot) {
-            final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
-                    Display.DEFAULT_DISPLAY);
+            final DisplayPowerControllerInterface displayPowerController =
+                    mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
             if (displayPowerController != null) {
                 displayPowerController.setAmbientColorTemperatureOverride(cct);
             }
@@ -2180,8 +2182,8 @@
 
     void setDockedAndIdleEnabled(boolean enabled, int displayId) {
         synchronized (mSyncRoot) {
-            final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
-                    displayId);
+            final DisplayPowerControllerInterface displayPowerController =
+                    mDisplayPowerControllers.get(displayId);
             if (displayPowerController != null) {
                 displayPowerController.setAutomaticScreenBrightnessMode(enabled);
             }
@@ -2554,10 +2556,19 @@
 
         final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
                 display, mSyncRoot);
-        final DisplayPowerController displayPowerController = new DisplayPowerController(
-                mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
-                mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                () -> handleBrightnessChange(display));
+        final DisplayPowerController displayPowerController;
+
+        if (SystemProperties.getInt(PROP_USE_NEW_DISPLAY_POWER_CONTROLLER, 0) == 1) {
+            displayPowerController = new DisplayPowerController(
+                    mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+                    mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+                    () -> handleBrightnessChange(display));
+        } else {
+            displayPowerController = new DisplayPowerController(
+                    mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+                    mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+                    () -> handleBrightnessChange(display));
+        }
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
 
@@ -3212,7 +3223,7 @@
                                     uniqueId, userSerial);
                     if (config == null) {
                         // Get default configuration
-                        DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+                        DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
                         if (dpc != null) {
                             config = dpc.getDefaultBrightnessConfiguration();
                         }
@@ -3263,7 +3274,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+                    DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
                     if (dpc != null) {
                         return dpc.getBrightnessInfo();
                     }
@@ -3310,7 +3321,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+                    DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
                     if (dpc != null) {
                         dpc.setBrightness(brightness);
                     }
@@ -3330,7 +3341,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+                    DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
                     if (dpc != null) {
                         brightness = dpc.getScreenBrightnessSetting();
                     }
@@ -3534,7 +3545,7 @@
                             id).getPrimaryDisplayDeviceLocked();
                     final int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
                     if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
-                        final DisplayPowerController displayPowerController =
+                        final DisplayPowerControllerInterface displayPowerController =
                                 mDisplayPowerControllers.get(id);
                         ready &= displayPowerController.requestPowerState(request,
                                 waitForNegativeProximity);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2476350..58a80e3 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -104,7 +104,7 @@
  * slower by changing the "animator duration scale" option in Development Settings.
  */
 final class DisplayPowerController implements AutomaticBrightnessController.Callbacks,
-        DisplayWhiteBalanceController.Callbacks {
+        DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface {
     private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
     private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
 
@@ -682,6 +682,7 @@
     /**
      * Returns true if the proximity sensor screen-off function is available.
      */
+    @Override
     public boolean isProximitySensorAvailable() {
         return mProximitySensor != null;
     }
@@ -693,6 +694,7 @@
      * @param includePackage if false will null out the package name in events
      */
     @Nullable
+    @Override
     public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
             @UserIdInt int userId, boolean includePackage) {
         if (mBrightnessTracker == null) {
@@ -701,6 +703,7 @@
         return mBrightnessTracker.getEvents(userId, includePackage);
     }
 
+    @Override
     public void onSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         if (mBrightnessTracker != null) {
@@ -709,6 +712,7 @@
     }
 
     @Nullable
+    @Override
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
             @UserIdInt int userId) {
         if (mBrightnessTracker == null) {
@@ -720,6 +724,7 @@
     /**
      * Persist the brightness slider events and ambient brightness stats to disk.
      */
+    @Override
     public void persistBrightnessTrackerState() {
         if (mBrightnessTracker != null) {
             mBrightnessTracker.persistBrightnessTrackerState();
@@ -781,6 +786,7 @@
         }
     }
 
+    @Override
     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
         if (mAutomaticBrightnessController == null) {
             return null;
@@ -794,6 +800,7 @@
      * of each display need to be properly reflected in AutomaticBrightnessController.
      */
     @GuardedBy("DisplayManagerService.mSyncRoot")
+    @Override
     public void onDisplayChanged() {
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         if (device == null) {
@@ -823,6 +830,7 @@
      * This process involves turning off some displays so we need updatePowerState() to run and
      * calculate the new state.
      */
+    @Override
     public void onDeviceStateTransition() {
         sendUpdatePowerState();
     }
@@ -833,6 +841,7 @@
      * This method should be called when the DisplayPowerController is no longer in use; i.e. when
      * the {@link #mDisplayId display} has been removed.
      */
+    @Override
     public void stop() {
         synchronized (mLock) {
             mStopped = true;
@@ -989,6 +998,25 @@
                     screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
                     screenDarkeningMinThreshold, screenBrighteningMinThreshold);
 
+            // Idle screen thresholds
+            float screenDarkeningMinThresholdIdle =
+                    mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle();
+            float screenBrighteningMinThresholdIdle =
+                    mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle();
+            HysteresisLevels screenBrightnessThresholdsIdle = new HysteresisLevels(
+                    screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
+                    screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle);
+
+            // Idle ambient thresholds
+            float ambientDarkeningMinThresholdIdle =
+                    mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle();
+            float ambientBrighteningMinThresholdIdle =
+                    mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle();
+            HysteresisLevels ambientBrightnessThresholdsIdle = new HysteresisLevels(
+                    ambientBrighteningThresholds, ambientDarkeningThresholds,
+                    ambientThresholdLevels, ambientDarkeningMinThresholdIdle,
+                    ambientBrighteningMinThresholdIdle);
+
             long brighteningLightDebounce = mDisplayDeviceConfig
                     .getAutoBrightnessBrighteningLightDebounce();
             long darkeningLightDebounce = mDisplayDeviceConfig
@@ -1024,7 +1052,8 @@
                     PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
                     lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
                     darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
-                    ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
+                    ambientBrightnessThresholds, screenBrightnessThresholds,
+                    ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
                     mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
                     mDisplayDeviceConfig.getAmbientHorizonShort(),
                     mDisplayDeviceConfig.getAmbientHorizonLong());
@@ -1063,6 +1092,7 @@
         }
     }
 
+    @Override
     public void setAutomaticScreenBrightnessMode(boolean isIdle) {
         if (mAutomaticBrightnessController != null) {
             if (isIdle) {
@@ -1766,27 +1796,32 @@
      * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
      * currently enabled and forcing the screen to be dark.
      */
+    @Override
     public void ignoreProximitySensorUntilChanged() {
         mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
     }
 
+    @Override
     public void setBrightnessConfiguration(BrightnessConfiguration c) {
         Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
         msg.sendToTarget();
     }
 
+    @Override
     public void setTemporaryBrightness(float brightness) {
         Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
                 Float.floatToIntBits(brightness), 0 /*unused*/);
         msg.sendToTarget();
     }
 
+    @Override
     public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
         Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
                 Float.floatToIntBits(adjustment), 0 /*unused*/);
         msg.sendToTarget();
     }
 
+    @Override
     public BrightnessInfo getBrightnessInfo() {
         synchronized (mCachedBrightnessInfo) {
             return new BrightnessInfo(
@@ -2347,7 +2382,8 @@
         return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
     }
 
-    float getScreenBrightnessSetting() {
+    @Override
+    public float getScreenBrightnessSetting() {
         float brightness = mBrightnessSetting.getBrightness();
         if (Float.isNaN(brightness)) {
             brightness = mScreenBrightnessDefault;
@@ -2362,7 +2398,8 @@
         return clampScreenBrightnessForVr(brightnessFloat);
     }
 
-    void setBrightness(float brightnessValue) {
+    @Override
+    public void setBrightness(float brightnessValue) {
         // Update the setting, which will eventually call back into DPC to have us actually update
         // the display with the new value.
         mBrightnessSetting.setBrightness(brightnessValue);
@@ -2511,6 +2548,7 @@
         }
     };
 
+    @Override
     public void dump(final PrintWriter pw) {
         synchronized (mLock) {
             pw.println();
@@ -2919,7 +2957,8 @@
         }
     }
 
-    void setAutoBrightnessLoggingEnabled(boolean enabled) {
+    @Override
+    public void setAutoBrightnessLoggingEnabled(boolean enabled) {
         if (mAutomaticBrightnessController != null) {
             mAutomaticBrightnessController.setLoggingEnabled(enabled);
         }
@@ -2930,14 +2969,16 @@
         sendUpdatePowerState();
     }
 
-    void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+    @Override
+    public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
         if (mDisplayWhiteBalanceController != null) {
             mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
             mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
         }
     }
 
-    void setAmbientColorTemperatureOverride(float cct) {
+    @Override
+    public void setAmbientColorTemperatureOverride(float cct) {
         if (mDisplayWhiteBalanceController != null) {
             mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
             // The ambient color temperature override is only applied when the ambient color
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
new file mode 100644
index 0000000..6677f35
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.pm.ParceledListSlice;
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+
+import java.io.PrintWriter;
+
+/**
+ * An interface to manage the display's power state and brightness
+ */
+public interface DisplayPowerControllerInterface {
+
+    /**
+     * Notified when the display is changed. We use this to apply any changes that might be needed
+     * when displays get swapped on foldable devices.
+     */
+    void onDisplayChanged();
+
+    /**
+     * Unregisters all listeners and interrupts all running threads; halting future work.
+     *
+     * This method should be called when the DisplayPowerController is no longer in use; i.e. when
+     * the display has been removed.
+     */
+    void stop();
+
+    /**
+     * Used to manage the displays preparing to transition from one device state to another.
+     */
+    void onDeviceStateTransition();
+
+    /**
+     * Used to update the display's BrightnessConfiguration
+     * @param config The new BrightnessConfiguration
+     */
+    void setBrightnessConfiguration(BrightnessConfiguration config);
+
+    /**
+     * Used to set the ambient color temperature of the Display
+     * @param ambientColorTemperature The target ambientColorTemperature
+     */
+    void setAmbientColorTemperatureOverride(float ambientColorTemperature);
+
+    /**
+     * Used to decide the associated AutomaticBrightnessController's BrightnessMode
+     * @param isIdle Flag which represents if the Idle BrightnessMode is to be set
+     */
+    void setAutomaticScreenBrightnessMode(boolean isIdle);
+
+    /**
+     * Used to enable/disable the logging of the WhileBalance associated entities
+     * @param enabled Flag which represents if the logging is the be enabled
+     */
+    void setDisplayWhiteBalanceLoggingEnabled(boolean enabled);
+
+    /**
+     * Used to dump the state.
+     * @param writer The PrintWriter used to dump the state.
+     */
+    void dump(PrintWriter writer);
+
+    /**
+     * Used to get the ambient brightness stats
+     */
+    ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId);
+
+    /**
+     * Get the default brightness configuration
+     */
+    BrightnessConfiguration getDefaultBrightnessConfiguration();
+
+    /**
+     * Set the screen brightness of the associated display
+     * @param brightness The value to which the brightness is to be set
+     */
+    void setBrightness(float brightness);
+
+    /**
+     * Checks if the proximity sensor is available
+     */
+    boolean isProximitySensorAvailable();
+
+    /**
+     * Persist the brightness slider events and ambient brightness stats to disk.
+     */
+    void persistBrightnessTrackerState();
+
+    /**
+     * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
+     * currently enabled and forcing the screen to be dark.
+     */
+    void ignoreProximitySensorUntilChanged();
+
+    /**
+     * Requests a new power state.
+     *
+     * @param request The requested power state.
+     * @param waitForNegativeProximity If true, issues a request to wait for
+     * negative proximity before turning the screen back on,
+     * assuming the screen was turned off by the proximity sensor.
+     * @return True if display is ready, false if there are important changes that must
+     * be made asynchronously.
+     */
+    boolean requestPowerState(DisplayManagerInternal.DisplayPowerRequest request,
+            boolean waitForNegativeProximity);
+
+    /**
+     * Sets up the temporary autobrightness adjustment when the user is yet to settle down to a
+     * value.
+     */
+    void setTemporaryAutoBrightnessAdjustment(float adjustment);
+
+    /**
+     * Gets the screen brightness setting
+     */
+    float getScreenBrightnessSetting();
+
+    /**
+     * Sets up the temporary brightness for the associated display
+     */
+    void setTemporaryBrightness(float brightness);
+
+    /**
+     * Gets the associated {@link BrightnessInfo}
+     */
+    BrightnessInfo getBrightnessInfo();
+
+    /**
+     * Get the {@link BrightnessChangeEvent}s for the specified user.
+     */
+    ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(int userId, boolean hasUsageStats);
+
+    /**
+     * Sets up the logging for the associated {@link AutomaticBrightnessController}
+     * @param enabled Flag to represent if the logging is to be enabled
+     */
+    void setAutoBrightnessLoggingEnabled(boolean enabled);
+
+    /**
+     * Handles the changes to be done to update the brightness when the user is changed
+     * @param newUserId The new userId
+     */
+    void onSwitchUser(int newUserId);
+}
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 7a932ce..3413489 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,15 +18,12 @@
 
 import android.util.Slog;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.PrintWriter;
 import java.util.Arrays;
 
 /**
  * A helper class for handling access to illuminance hysteresis level values.
  */
-@VisibleForTesting
 public class HysteresisLevels {
     private static final String TAG = "HysteresisLevels";
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 65e7ce1..34d6d29 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -465,20 +465,6 @@
                 filterCallingUid);
     }
 
-    /**
-     * @deprecated similar to {@link resolveIntent} but limits the matches to exported components.
-     */
-    @Override
-    @Deprecated
-    public final ResolveInfo resolveIntentExported(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlagsBits long flags,
-            @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
-            boolean resolveForStart, int filterCallingUid) {
-        return getResolveIntentHelper().resolveIntentInternal(snapshot(),
-                intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
-                filterCallingUid, true);
-    }
-
     @Override
     @Deprecated
     public final ResolveInfo resolveService(Intent intent, String resolvedType,
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index fada577..c2fd637 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -52,7 +52,6 @@
 
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.am.ActivityManagerService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
@@ -101,38 +100,6 @@
         mInstantAppInstallerActivitySupplier = instantAppInstallerActivitySupplier;
     }
 
-    private static void filterNonExportedComponents(Intent intent, int filterCallingUid,
-            List<ResolveInfo> query, PlatformCompat platformCompat, Computer computer) {
-        if (query == null
-                || intent.getPackage() != null
-                || intent.getComponent() != null
-                || ActivityManager.canAccessUnexportedComponents(filterCallingUid)) {
-            return;
-        }
-        AndroidPackage caller = computer.getPackage(filterCallingUid);
-        String callerPackage = caller == null ? "Not specified" : caller.getPackageName();
-        for (int i = query.size() - 1; i >= 0; i--) {
-            if (!query.get(i).getComponentInfo().exported) {
-                if (!platformCompat.isChangeEnabledByUid(
-                        ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS,
-                        filterCallingUid)) {
-                    Slog.w(TAG, "Non-exported component not filtered out "
-                            + "(will be filtered out once the app targets U+)- intent: "
-                            + intent.getAction() + ", component: "
-                            + query.get(i).getComponentInfo()
-                            .getComponentName().flattenToShortString()
-                            + ", starter: " + callerPackage);
-                    return;
-                }
-                Slog.w(TAG, "Non-exported component filtered out - intent: "
-                        + intent.getAction() + ", component: "
-                        + query.get(i).getComponentInfo().getComponentName().flattenToShortString()
-                        + ", starter: " + callerPackage);
-                query.remove(i);
-            }
-        }
-    }
-
     /**
      * 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
@@ -142,20 +109,6 @@
             @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
             boolean resolveForStart, int filterCallingUid) {
-        return resolveIntentInternal(computer, intent, resolvedType, flags,
-                privateResolveFlags, userId, resolveForStart, filterCallingUid, false);
-    }
-
-    /**
-     * 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.
-     * Allows picking exported components only.
-     */
-    public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlagsBits long flags,
-            @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
-            boolean resolveForStart, int filterCallingUid, boolean exportedComponentsOnly) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
 
@@ -171,10 +124,6 @@
             final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,
                     resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
                     resolveForStart, true /*allowDynamicSplits*/);
-            if (exportedComponentsOnly) {
-                filterNonExportedComponents(intent, filterCallingUid, query,
-                        mPlatformCompat, computer);
-            }
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             final boolean queryMayBeFiltered =
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 16b5ee5..1c583be 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1522,8 +1522,17 @@
         this.task = newTask;
 
         if (shouldStartChangeTransition(newParent, oldParent)) {
-            // Animate change transition on TaskFragment level to get the correct window crop.
-            newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
+            if (mTransitionController.isShellTransitionsEnabled()) {
+                // For Shell transition, call #initializeChangeTransition directly to take the
+                // screenshot at the Activity level. And Shell will be in charge of handling the
+                // surface reparent and crop.
+                initializeChangeTransition(getBounds());
+            } else {
+                // For legacy app transition, we want to take a screenshot of the Activity surface,
+                // but animate the change transition on TaskFragment level to get the correct window
+                // crop.
+                newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
+            }
         }
 
         super.onParentChanged(newParent, oldParent);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 28cd001..dc91c15 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -744,7 +744,7 @@
             // (e.g. AMS.startActivityAsUser).
             final long token = Binder.clearCallingIdentity();
             try {
-                return mService.getPackageManagerInternalLocked().resolveIntentExported(
+                return mService.getPackageManagerInternalLocked().resolveIntent(
                         intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
                         filterCallingUid);
             } finally {
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 1fd66fc..d497d8c 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -141,15 +141,16 @@
             return false;
 
         } else {
-            mResultActivities.add(r);
             if (r.resultTo != null) {
                 // If this activity is sending a reply to a previous activity, we can't do
                 // anything with it now until we reach the start of the reply chain.
                 // NOTE: that we are assuming the result is always to the previous activity,
                 // which is almost always the case but we really shouldn't count on.
+                mResultActivities.add(r);
                 return false;
             } else if (mTargetTaskFound && allowTaskReparenting && mTargetTask.affinity != null
                     && mTargetTask.affinity.equals(r.taskAffinity)) {
+                mResultActivities.add(r);
                 // This activity has an affinity for our task. Either remove it if we are
                 // clearing or move it over to our task. Note that we currently punt on the case
                 // where we are resetting a task that is not at the top but who has activities
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 221e186..77d0f37 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -67,7 +67,7 @@
 
     /** Which sync method to use for transition syncs. */
     static final int SYNC_METHOD =
-            android.os.SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", true)
+            android.os.SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", false)
                     ? BLASTSyncEngine.METHOD_BLAST : BLASTSyncEngine.METHOD_NONE;
 
     /** The same as legacy APP_TRANSITION_TIMEOUT_MS. */
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 6b05d8f..267cff6 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -97,6 +97,16 @@
                     <xs:annotation name="nonnull"/>
                     <xs:annotation name="final"/>
                 </xs:element>
+                <!-- Set of thresholds that dictate the change needed for screen brightness
+                adaptations while in idle mode -->
+                <xs:element type="thresholds" name="displayBrightnessChangeThresholdsIdle" minOccurs="0" maxOccurs="1">
+                    <xs:annotation name="final"/>
+                </xs:element>
+                <!-- Set of thresholds that dictate the change needed for ambient brightness
+                adaptations while in idle mode -->
+                <xs:element type="thresholds" name="ambientBrightnessChangeThresholdsIdle" minOccurs="0" maxOccurs="1">
+                    <xs:annotation name="final"/>
+                </xs:element>
             </xs:sequence>
         </xs:complexType>
     </xs:element>
@@ -319,13 +329,13 @@
     <!-- Thresholds for brightness changes. -->
     <xs:complexType name="thresholds">
         <xs:sequence>
-            <!-- Brightening thresholds. -->
+            <!-- Brightening thresholds for active screen brightness mode. -->
             <xs:element name="brighteningThresholds" type="brightnessThresholds" minOccurs="0"
                         maxOccurs="1" >
                 <xs:annotation name="nonnull"/>
                 <xs:annotation name="final"/>
             </xs:element>
-            <!-- Darkening thresholds. -->
+            <!-- Darkening thresholds for active screen brightness mode. -->
             <xs:element name="darkeningThresholds" type="brightnessThresholds" minOccurs="0"
                         maxOccurs="1" >
                 <xs:annotation name="nonnull"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index fb7a920..f8bff75 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -61,11 +61,13 @@
   public class DisplayConfiguration {
     ctor public DisplayConfiguration();
     method @NonNull public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholds();
+    method public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholdsIdle();
     method public final java.math.BigInteger getAmbientLightHorizonLong();
     method public final java.math.BigInteger getAmbientLightHorizonShort();
     method public com.android.server.display.config.AutoBrightness getAutoBrightness();
     method @Nullable public final com.android.server.display.config.DensityMapping getDensityMapping();
     method @NonNull public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholds();
+    method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle();
     method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
     method public final com.android.server.display.config.SensorDetails getLightSensor();
     method public final com.android.server.display.config.SensorDetails getProxSensor();
@@ -80,11 +82,13 @@
     method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
     method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
     method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
+    method public final void setAmbientBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
     method public final void setAmbientLightHorizonLong(java.math.BigInteger);
     method public final void setAmbientLightHorizonShort(java.math.BigInteger);
     method public void setAutoBrightness(com.android.server.display.config.AutoBrightness);
     method public final void setDensityMapping(@Nullable com.android.server.display.config.DensityMapping);
     method public final void setDisplayBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
+    method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
     method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
     method public final void setLightSensor(com.android.server.display.config.SensorDetails);
     method public final void setProxSensor(com.android.server.display.config.SensorDetails);
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 3c68662..f05b1d4 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -218,6 +218,9 @@
             BackgroundThread.get().getThreadHandler().post(
                     () -> {
                         try {
+                            if (sSelfService.mIProfcollect == null) {
+                                return;
+                            }
                             sSelfService.mIProfcollect.process();
                         } catch (RemoteException e) {
                             Log.e(LOG_TAG, "Failed to process profiles in background: "
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 6a18dc1..9a4bb22 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -233,4 +233,4 @@
                 });
         when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
     }
-}
+}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
index 538adb2..6f0efb0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
@@ -342,7 +342,6 @@
         addDefaultProfileAndParent();
         mockCurrentUser(PARENT_USER_ID);
         startDefaultProfile();
-        setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
 
         assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
                 .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
@@ -584,11 +583,19 @@
     }
 
     protected final void startDefaultProfile() {
-        setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+        startUser(PROFILE_USER_ID);
     }
 
     protected final void stopDefaultProfile() {
-        setUserState(PROFILE_USER_ID, UserState.STATE_STOPPING);
+        stopUser(PROFILE_USER_ID);
+    }
+
+    protected final void startUser(@UserIdInt int userId) {
+        setUserState(userId, UserState.STATE_RUNNING_UNLOCKED);
+    }
+
+    protected final void stopUser(@UserIdInt int userId) {
+        setUserState(userId, UserState.STATE_STOPPING);
     }
 
     // NOTE: should only called by tests that indirectly needs to check user assignments (like
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 8388a70..8b5921c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -88,6 +88,42 @@
                 .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse();
     }
 
+    @Test
+    public void testIsUserRunning_StartedUserShouldReturnTrue() {
+        addUser(USER_ID);
+        startUser(USER_ID);
+
+        assertWithMessage("isUserRunning(%s)", USER_ID)
+                .that(mUms.isUserRunning(USER_ID)).isTrue();
+    }
+
+    @Test
+    public void testIsUserRunning_StoppedUserShouldReturnFalse() {
+        addUser(USER_ID);
+        stopUser(USER_ID);
+
+        assertWithMessage("isUserRunning(%s)", USER_ID)
+                .that(mUms.isUserRunning(USER_ID)).isFalse();
+    }
+
+    @Test
+    public void testIsUserRunning_CurrentUserStartedWorkProfileShouldReturnTrue() {
+        addDefaultProfileAndParent();
+        startDefaultProfile();
+
+        assertWithMessage("isUserRunning(%s)", PROFILE_USER_ID)
+                .that(mUms.isUserRunning(PROFILE_USER_ID)).isTrue();
+    }
+
+    @Test
+    public void testIsUserRunning_CurrentUserStoppedWorkProfileShouldReturnFalse() {
+        addDefaultProfileAndParent();
+        stopDefaultProfile();
+
+        assertWithMessage("isUserRunning(%s)", PROFILE_USER_ID)
+                .that(mUms.isUserRunning(PROFILE_USER_ID)).isFalse();
+    }
+
     @Override
     protected boolean isUserVisible(int userId) {
         return mUms.isUserVisibleUnchecked(userId);
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 9d82f1a..8280fc6 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -82,6 +82,8 @@
     @Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy;
     @Mock HysteresisLevels mAmbientBrightnessThresholds;
     @Mock HysteresisLevels mScreenBrightnessThresholds;
+    @Mock HysteresisLevels mAmbientBrightnessThresholdsIdle;
+    @Mock HysteresisLevels mScreenBrightnessThresholdsIdle;
     @Mock Handler mNoOpHandler;
     @Mock HighBrightnessModeController mHbmController;
     @Mock BrightnessThrottler mBrightnessThrottler;
@@ -129,6 +131,7 @@
                 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
                 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
                 mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
+                mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
                 mContext, mHbmController, mBrightnessThrottler, mIdleBrightnessMappingStrategy,
                 AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
         );
@@ -314,8 +317,9 @@
 
         // Now let's do the same for idle mode
         mController.switchToIdleMode();
-        // Called once for init, and once when switching
-        verify(mBrightnessMappingStrategy, times(2)).isForIdleMode();
+        // Called once for init, and once when switching,
+        // setAmbientLux() is called twice and once in updateAutoBrightness()
+        verify(mBrightnessMappingStrategy, times(5)).isForIdleMode();
         // Ensure, after switching, original BMS is not used anymore
         verifyNoMoreInteractions(mBrightnessMappingStrategy);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 2b0e76c..85e5bfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -379,8 +379,6 @@
         doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any());
         doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyLong(), anyLong(),
                 anyInt(), anyBoolean(), anyInt());
-        doReturn(null).when(mMockPackageManager).resolveIntentExported(any(), any(),
-                anyLong(), anyLong(), anyInt(), anyBoolean(), anyInt());
         doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent();
 
         // Never review permissions
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3023ec9..16f8fd9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -39,7 +39,6 @@
 import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
 import android.telephony.gba.TlsParams;
 import android.telephony.gba.UaSecurityProtocolIdentifier;
 import android.telephony.ims.ImsReasonInfo;
@@ -1136,27 +1135,6 @@
     public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int";
 
     /**
-     * The data call retry configuration for different types of APN.
-     * @hide
-     */
-    public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS =
-            "carrier_data_call_retry_config_strings";
-
-    /**
-     * Delay in milliseconds between trying APN from the pool
-     * @hide
-     */
-    public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG =
-            "carrier_data_call_apn_delay_default_long";
-
-    /**
-     * Faster delay in milliseconds between trying APN from the pool
-     * @hide
-     */
-    public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG =
-            "carrier_data_call_apn_delay_faster_long";
-
-    /**
      * Delay in milliseconds for retrying APN after disconnect
      * @hide
      */
@@ -1164,21 +1142,6 @@
             "carrier_data_call_apn_retry_after_disconnect_long";
 
     /**
-     * The maximum times for telephony to retry data setup on the same APN requested by
-     * network through the data setup response retry timer
-     * {@link DataCallResponse#getRetryDurationMillis()}. This is to prevent that network keeps
-     * asking device to retry data setup forever and causes power consumption issue. For infinite
-     * retring same APN, configure this as 2147483647 (i.e. {@link Integer#MAX_VALUE}).
-     *
-     * Note if network does not suggest any retry timer, frameworks uses the retry configuration
-     * from {@link #KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS}, and the maximum retry times could
-     * be configured there.
-     * @hide
-     */
-    public static final String KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT =
-            "carrier_data_call_retry_network_requested_max_count_int";
-
-    /**
      * Data call setup permanent failure causes by the carrier
      */
     public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS =
@@ -1239,19 +1202,6 @@
             "carrier_metered_roaming_apn_types_strings";
 
     /**
-     * APN types that are not allowed on cellular
-     * @hide
-     */
-    public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
-            "carrier_wwan_disallowed_apn_types_string_array";
-
-    /**
-     * APN types that are not allowed on IWLAN
-     * @hide
-     */
-    public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
-            "carrier_wlan_disallowed_apn_types_string_array";
-    /**
      * CDMA carrier ERI (Enhanced Roaming Indicator) file name
      * @hide
      */
@@ -8470,7 +8420,6 @@
      * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
      * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
      *
-     * // TODO: remove KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS
      * @hide
      */
     public static final String KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY =
@@ -8872,27 +8821,13 @@
         sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
         sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500);
-        sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
-                "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
-                        + "320000:5000,640000:5000,1280000:5000,1800000:5000",
-                "mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
-                        + "320000:5000,640000:5000,1280000:5000,1800000:5000",
-                "ims:max_retries=10, 5000, 5000, 5000",
-                "others:max_retries=3, 5000, 5000, 5000"});
-        sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
-        sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, 3000);
-        sDefaults.putInt(KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT, 3);
         sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
         sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
         sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{"default", "mms", "dun", "supl"});
         sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{"default", "mms", "dun", "supl"});
-        sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
-                new String[]{""});
-        sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
-                new String[]{""});
         sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
                 new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT,
                         TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A,
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3032bab..850d268 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2548,9 +2548,6 @@
     CellIdentity getLastKnownCellIdentity(int subId, String callingPackage,
             String callingFeatureId);
 
-    /** Check if telephony new data stack is enabled. */
-    boolean isUsingNewDataStack();
-
     /**
      *  @return true if the modem service is set successfully, false otherwise.
      */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
new file mode 100644
index 0000000..ea5a5f8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+class GameAppHelper @JvmOverloads constructor(
+    instr: Instrumentation,
+    launcherName: String = ActivityOptions.GAME_ACTIVITY_LAUNCHER_NAME,
+    component: ComponentNameMatcher =
+        ActivityOptions.GAME_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
+    launcherStrategy: ILauncherStrategy =
+        LauncherStrategyFactory.getInstance(instr).launcherStrategy,
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+
+    /**
+     * Swipes down in the mock game app.
+     *
+     * @return true if the swipe operation is successful.
+     */
+    fun swipeDown(): Boolean {
+        val gameView = uiDevice.wait(
+            Until.findObject(By.res(getPackage(), GAME_APP_VIEW_RES)), WAIT_TIME_MS)
+        require(gameView != null) { "Mock game app view not found." }
+
+        val bound = gameView.getVisibleBounds()
+        return uiDevice.swipe(
+            bound.centerX(), bound.top, bound.centerX(), bound.centerY(), SWIPE_STEPS)
+    }
+
+    /**
+     * Switches to a recent app by quick switch gesture. This function can be used in both portrait
+     * and landscape mode.
+     *
+     * @param wmHelper Helper used to get window region.
+     * @param direction UiAutomator Direction enum to indicate the swipe direction.
+     *
+     * @return true if the swipe operation is successful.
+     */
+    fun switchToPreviousAppByQuickSwitchGesture(
+        wmHelper: WindowManagerStateHelper,
+        direction: Direction
+    ): Boolean {
+        val ratioForScreenBottom = 0.97
+        val fullView = wmHelper.getWindowRegion(componentMatcher)
+        require(!fullView.isEmpty) { "Target $componentMatcher view not found." }
+
+        val bound = fullView.bounds
+        val targetYPos = bound.bottom * ratioForScreenBottom
+        val endX = when (direction) {
+            Direction.LEFT -> bound.left
+            Direction.RIGHT -> bound.right
+            else -> {
+                throw IllegalStateException("Only left or right direction is allowed.")
+            }
+        }
+        return uiDevice.swipe(
+            bound.centerX(), targetYPos.toInt(), endX, targetYPos.toInt(), SWIPE_STEPS)
+    }
+
+    /**
+     * Waits for view idel with timeout, then checkes the target object whether visible on screen.
+     *
+     * @param packageName The targe application's package name.
+     * @param identifier The resource id of the target object.
+     * @param timeout The timeout duration in milliseconds.
+     *
+     * @return true if the target object exists.
+     */
+    @JvmOverloads
+    fun isTargetObjVisible(
+        packageName: String,
+        identifier: String,
+        timeout: Long = WAIT_TIME_MS
+    ): Boolean {
+        uiDevice.waitForIdle(timeout)
+        return uiDevice.hasObject(By.res(packageName, identifier))
+    }
+
+    companion object {
+        private const val GAME_APP_VIEW_RES = "container"
+        private const val WAIT_TIME_MS = 3_000L
+        private const val SWIPE_STEPS = 30
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
new file mode 100644
index 0000000..807e672
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.parser.toFlickerComponent
+
+class MailAppHelper @JvmOverloads constructor(
+        instr: Instrumentation,
+        launcherName: String = ActivityOptions.MAIL_ACTIVITY_LAUNCHER_NAME,
+        component: ComponentNameMatcher =
+                ActivityOptions.MAIL_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
+        launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+                .getInstance(instr)
+                .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+
+    fun openMail(rowIdx: Int) {
+        val rowSel = By.res(getPackage(), "mail_row_item_text")
+            .textEndsWith(String.format("%04d", rowIdx))
+        var row: UiObject2? = null
+        for (i in 1..1000) {
+            row = uiDevice.wait(Until.findObject(rowSel), SHORT_WAIT_TIME_MS)
+            if (row != null) break
+            scrollDown()
+        }
+        require(row != null) {""}
+        row.click()
+        uiDevice.wait(Until.gone(By.res(getPackage(), MAIL_LIST_RES_ID)), FIND_TIMEOUT)
+    }
+
+    fun scrollDown() {
+        val listView = waitForMailList()
+        listView.scroll(Direction.DOWN, 1.0f)
+    }
+
+    fun waitForMailList(): UiObject2 {
+        var sel = By.res(getPackage(), MAIL_LIST_RES_ID).scrollable(true)
+        val ret = uiDevice.wait(Until.findObject(sel), FIND_TIMEOUT)
+        require(ret != null) {""}
+        return ret
+    }
+
+    companion object {
+        private const val SHORT_WAIT_TIME_MS = 5000L
+        private const val MAIL_LIST_RES_ID = "mail_recycle_view"
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 354964d..9b1c541 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -62,19 +62,23 @@
         get() = {
             super.transition(this)
             setup {
-                tapl.setExpectedRotation(Surface.ROTATION_0)
+                if (testSpec.isTablet) {
+                    tapl.setExpectedRotation(testSpec.startRotation)
+                } else {
+                    tapl.setExpectedRotation(Surface.ROTATION_0)
+                }
                 RemoveAllTasksButHomeRule.removeAllTasksButHome()
                 this.setRotation(testSpec.startRotation)
             }
-            teardown {
-                testApp.exit(wmHelper)
-            }
             transitions {
                 tapl.goHome()
                     .switchToAllApps()
                     .getAppIcon(testApp.launcherName)
                     .launch(testApp.`package`)
             }
+            teardown {
+                testApp.exit(wmHelper)
+            }
         }
 
     /** {@inheritDoc} */
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 72a02f2..cae3df4 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -101,6 +101,10 @@
                     FLICKER_APP_PACKAGE,
             FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
 
+    public static final String MAIL_ACTIVITY_LAUNCHER_NAME = "MailActivity";
+    public static final ComponentName MAIL_ACTIVITY_COMPONENT_NAME = new ComponentName(
+            FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".MailActivity");
+
     public static final String GAME_ACTIVITY_LAUNCHER_NAME = "GameApp";
     public static final ComponentName GAME_ACTIVITY_COMPONENT_NAME =
             new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/TouchLatency/OWNERS b/tests/TouchLatency/OWNERS
new file mode 100644
index 0000000..2b7de25
--- /dev/null
+++ b/tests/TouchLatency/OWNERS
@@ -0,0 +1,2 @@
+include platform/frameworks/base:/graphics/java/android/graphics/OWNERS
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
\ No newline at end of file