Merge "Removed old data stack"
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/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/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/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/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/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/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/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/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/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/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/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/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index cbdac61..3152e65 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -16,20 +16,6 @@
 
 package com.android.settingslib.bluetooth;
 
-import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
-import static android.bluetooth.BluetoothAdapter.STATE_CONNECTING;
-import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTED;
-import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTING;
-import static android.bluetooth.BluetoothAdapter.STATE_OFF;
-import static android.bluetooth.BluetoothAdapter.STATE_ON;
-import static android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF;
-import static android.bluetooth.BluetoothAdapter.STATE_TURNING_ON;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 
 /**
  * BluetoothCallback provides a callback interface for the settings
@@ -47,7 +33,7 @@
      * {@link android.bluetooth.BluetoothAdapter#STATE_ON},
      * {@link android.bluetooth.BluetoothAdapter#STATE_TURNING_OFF}.
      */
-    default void onBluetoothStateChanged(@AdapterState int bluetoothState) {}
+    default void onBluetoothStateChanged(int bluetoothState) {}
 
     /**
      * It will be called when the local Bluetooth adapter has started
@@ -103,9 +89,7 @@
      * {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED},
      * {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTING}.
      */
-    default void onConnectionStateChanged(
-            @Nullable CachedBluetoothDevice cachedDevice,
-            @ConnectionState int state) {}
+    default void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {}
 
     /**
      * It will be called when device been set as active for {@code bluetoothProfile}
@@ -140,10 +124,8 @@
      * {@link android.bluetooth.BluetoothProfile#STATE_DISCONNECTING}.
      * @param bluetoothProfile the BluetoothProfile id.
      */
-    default void onProfileConnectionStateChanged(
-            CachedBluetoothDevice cachedDevice,
-            @ConnectionState int state,
-            int bluetoothProfile) {
+    default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
+            int state, int bluetoothProfile) {
     }
 
     /**
@@ -158,22 +140,4 @@
      */
     default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
     }
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "STATE_" }, value = {
-            STATE_DISCONNECTED,
-            STATE_CONNECTING,
-            STATE_CONNECTED,
-            STATE_DISCONNECTING,
-    })
-    @interface ConnectionState {}
-
-    @IntDef(prefix = { "STATE_" }, value = {
-            STATE_OFF,
-            STATE_TURNING_ON,
-            STATE_ON,
-            STATE_TURNING_OFF,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface AdapterState {}
 }
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/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/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/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/bluetooth/BluetoothLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
deleted file mode 100644
index b5da7b6..0000000
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.bluetooth
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.dagger.BluetoothLog
-import javax.inject.Inject
-
-/** Helper class for logging bluetooth events. */
-@SysUISingleton
-class BluetoothLogger @Inject constructor(@BluetoothLog private val logBuffer: LogBuffer) {
-    fun logActiveDeviceChanged(address: String, profileId: Int) =
-        logBuffer.log(
-            TAG,
-            LogLevel.DEBUG,
-            {
-                str1 = address
-                int1 = profileId
-            },
-            { "ActiveDeviceChanged. address=$str1 profileId=$int1" }
-        )
-
-    fun logDeviceConnectionStateChanged(address: String?, state: String) =
-        logBuffer.log(
-            TAG,
-            LogLevel.DEBUG,
-            {
-                str1 = address
-                str2 = state
-            },
-            { "DeviceConnectionStateChanged. address=$str1 state=$str2" }
-        )
-
-    fun logAclConnectionStateChanged(address: String, state: String) =
-        logBuffer.log(
-            TAG,
-            LogLevel.DEBUG,
-            {
-                str1 = address
-                str2 = state
-            },
-            { "AclConnectionStateChanged. address=$str1 state=$str2" }
-        )
-
-    fun logProfileConnectionStateChanged(address: String, state: String, profileId: Int) =
-        logBuffer.log(
-            TAG,
-            LogLevel.DEBUG,
-            {
-                str1 = address
-                str2 = state
-                int1 = profileId
-            },
-            { "ProfileConnectionStateChanged. address=$str1 state=$str2 profileId=$int1" }
-        )
-
-    fun logStateChange(state: String) =
-        logBuffer.log(
-            TAG,
-            LogLevel.DEBUG,
-            { str1 = state },
-            { "BluetoothStateChanged. state=$str1" }
-        )
-
-    fun logBondStateChange(address: String, state: Int) =
-        logBuffer.log(
-            TAG,
-            LogLevel.DEBUG,
-            {
-                str1 = address
-                int1 = state
-            },
-            { "DeviceBondStateChanged. address=$str1 state=$int1" }
-        )
-
-    fun logDeviceAdded(address: String) =
-        logBuffer.log(TAG, LogLevel.DEBUG, { str1 = address }, { "DeviceAdded. address=$str1" })
-
-    fun logDeviceDeleted(address: String) =
-        logBuffer.log(TAG, LogLevel.DEBUG, { str1 = address }, { "DeviceDeleted. address=$str1" })
-
-    fun logDeviceAttributesChanged() =
-        logBuffer.log(TAG, LogLevel.DEBUG, {}, { "DeviceAttributesChanged." })
-}
-
-private const val TAG = "BluetoothLog"
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 659f286..a3dc779 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -601,7 +601,7 @@
 
     private final class BluetoothCallbackHandler implements BluetoothCallback {
         @Override
-        public void onBluetoothStateChanged(@BluetoothCallback.AdapterState int bluetoothState) {
+        public void onBluetoothStateChanged(int bluetoothState) {
             mHandler.obtainMessage(MSG_ON_BLUETOOTH_STATE_CHANGED,
                     bluetoothState, 0).sendToTarget();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt
deleted file mode 100644
index 4887b6a..0000000
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.log.dagger
-
-import javax.inject.Qualifier
-
-/** A [com.android.systemui.log.LogBuffer] for bluetooth. */
-@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class BluetoothLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
index b551125..323ee21 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
@@ -1,9 +1,4 @@
 package com.android.systemui.log.dagger
 
-import javax.inject.Qualifier
-
 /** A [com.android.systemui.log.LogBuffer] for KeyguardUpdateMonitor. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
 annotation class KeyguardUpdateMonitorLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index eeba6b3..c2a8764 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -305,14 +305,4 @@
     public static LogBuffer provideKeyguardUpdateMonitorLogBuffer(LogBufferFactory factory) {
         return factory.create("KeyguardUpdateMonitorLog", 200);
     }
-
-    /**
-     * Provides a {@link LogBuffer} for bluetooth-related logs.
-     */
-    @Provides
-    @SysUISingleton
-    @BluetoothLog
-    public static LogBuffer providerBluetoothLogBuffer(LogBufferFactory factory) {
-        return factory.create("BluetoothLog", 50);
-    }
 }
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/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/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index c7ea3c6..e7fa6d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -27,6 +27,7 @@
 import android.os.Message;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 
@@ -36,7 +37,6 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.android.systemui.bluetooth.BluetoothLogger;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -57,9 +57,9 @@
 public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
         CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener {
     private static final String TAG = "BluetoothController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final DumpManager mDumpManager;
-    private final BluetoothLogger mLogger;
     private final LocalBluetoothManager mLocalBluetoothManager;
     private final UserManager mUserManager;
     private final int mCurrentUser;
@@ -70,7 +70,6 @@
     private final List<CachedBluetoothDevice> mConnectedDevices = new ArrayList<>();
 
     private boolean mEnabled;
-    @ConnectionState
     private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
     private boolean mAudioProfileOnly;
     private boolean mIsActive;
@@ -84,12 +83,10 @@
     public BluetoothControllerImpl(
             Context context,
             DumpManager dumpManager,
-            BluetoothLogger logger,
             @Background Looper bgLooper,
             @Main Looper mainLooper,
             @Nullable LocalBluetoothManager localBluetoothManager) {
         mDumpManager = dumpManager;
-        mLogger = logger;
         mLocalBluetoothManager = localBluetoothManager;
         mBgHandler = new Handler(bgLooper);
         mHandler = new H(mainLooper);
@@ -119,7 +116,7 @@
             return;
         }
         pw.print("  mEnabled="); pw.println(mEnabled);
-        pw.print("  mConnectionState="); pw.println(connectionStateToString(mConnectionState));
+        pw.print("  mConnectionState="); pw.println(stateToString(mConnectionState));
         pw.print("  mAudioProfileOnly="); pw.println(mAudioProfileOnly);
         pw.print("  mIsActive="); pw.println(mIsActive);
         pw.print("  mConnectedDevices="); pw.println(getConnectedDevices());
@@ -130,7 +127,7 @@
         }
     }
 
-    private static String connectionStateToString(@ConnectionState int state) {
+    private static String stateToString(int state) {
         switch (state) {
             case BluetoothAdapter.STATE_CONNECTED:
                 return "CONNECTED";
@@ -323,8 +320,8 @@
     }
 
     @Override
-    public void onBluetoothStateChanged(@AdapterState int bluetoothState) {
-        mLogger.logStateChange(BluetoothAdapter.nameForState(bluetoothState));
+    public void onBluetoothStateChanged(int bluetoothState) {
+        if (DEBUG) Log.d(TAG, "BluetoothStateChanged=" + stateToString(bluetoothState));
         mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
                 || bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
         mState = bluetoothState;
@@ -334,7 +331,7 @@
 
     @Override
     public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
-        mLogger.logDeviceAdded(cachedDevice.getAddress());
+        if (DEBUG) Log.d(TAG, "DeviceAdded=" + cachedDevice.getAddress());
         cachedDevice.registerCallback(this);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
@@ -342,7 +339,7 @@
 
     @Override
     public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
-        mLogger.logDeviceDeleted(cachedDevice.getAddress());
+        if (DEBUG) Log.d(TAG, "DeviceDeleted=" + cachedDevice.getAddress());
         mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
@@ -350,7 +347,7 @@
 
     @Override
     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
-        mLogger.logBondStateChange(cachedDevice.getAddress(), bondState);
+        if (DEBUG) Log.d(TAG, "DeviceBondStateChanged=" + cachedDevice.getAddress());
         mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
@@ -358,29 +355,29 @@
 
     @Override
     public void onDeviceAttributesChanged() {
-        mLogger.logDeviceAttributesChanged();
+        if (DEBUG) Log.d(TAG, "DeviceAttributesChanged");
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
     }
 
     @Override
-    public void onConnectionStateChanged(
-            @Nullable CachedBluetoothDevice cachedDevice,
-            @ConnectionState int state) {
-        String address = cachedDevice == null ? null : cachedDevice.getAddress();
-        mLogger.logDeviceConnectionStateChanged(address, connectionStateToString(state));
+    public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+        if (DEBUG) {
+            Log.d(TAG, "ConnectionStateChanged=" + cachedDevice.getAddress() + " "
+                    + stateToString(state));
+        }
         mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
     @Override
-    public void onProfileConnectionStateChanged(
-            CachedBluetoothDevice cachedDevice,
-            @ConnectionState int state,
-            int bluetoothProfile) {
-        mLogger.logProfileConnectionStateChanged(
-                cachedDevice.getAddress(), connectionStateToString(state), bluetoothProfile);
+    public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
+            int state, int bluetoothProfile) {
+        if (DEBUG) {
+            Log.d(TAG, "ProfileConnectionStateChanged=" + cachedDevice.getAddress() + " "
+                    + stateToString(state) + " profileId=" + bluetoothProfile);
+        }
         mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
@@ -388,15 +385,20 @@
 
     @Override
     public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
-        mLogger.logActiveDeviceChanged(activeDevice.getAddress(), bluetoothProfile);
+        if (DEBUG) {
+            Log.d(TAG, "ActiveDeviceChanged=" + activeDevice.getAddress()
+                    + " profileId=" + bluetoothProfile);
+        }
         updateActive();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
     @Override
     public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
-        mLogger.logAclConnectionStateChanged(
-                cachedDevice.getAddress(), connectionStateToString(state));
+        if (DEBUG) {
+            Log.d(TAG, "ACLConnectionStateChanged=" + cachedDevice.getAddress() + " "
+                    + stateToString(state));
+        }
         mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
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/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/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index cf5e878..3dd36d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -41,7 +41,6 @@
 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bluetooth.BluetoothLogger;
 import com.android.systemui.dump.DumpManager;
 
 import org.junit.Before;
@@ -82,7 +81,6 @@
 
         mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
                 mMockDumpManager,
-                mock(BluetoothLogger.class),
                 mTestableLooper.getLooper(),
                 mTestableLooper.getLooper(),
                 mMockBluetoothManager);
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/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e52d21..842498d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16396,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/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/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/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/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/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/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} */