Merge "Do not intercept Escape with modifiers" into main
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 158d914..6383ed8 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -32,6 +32,7 @@
 import android.app.AlarmManager;
 import android.app.BroadcastOptions;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
@@ -41,6 +42,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -81,6 +83,7 @@
 import android.os.UserHandle;
 import android.os.WearModeManagerInternal;
 import android.provider.DeviceConfig;
+import android.provider.Settings;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
@@ -109,6 +112,7 @@
 import com.android.server.deviceidle.IDeviceIdleConstraint;
 import com.android.server.deviceidle.TvConstraintController;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -1020,7 +1024,8 @@
      * global Settings. Any access to this class or its fields should be done while
      * holding the DeviceIdleController lock.
      */
-    public final class Constants implements DeviceConfig.OnPropertiesChangedListener {
+    public final class Constants extends ContentObserver
+            implements DeviceConfig.OnPropertiesChangedListener {
         // Key names stored in the settings value.
         private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
         private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
@@ -1102,7 +1107,7 @@
         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);
+                (60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
         private long mDefaultSensingTimeout =
                 !COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L;
         private long mDefaultLocatingTimeout =
@@ -1115,7 +1120,7 @@
         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);
+                (60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
         private long mDefaultIdlePendingTimeout =
                 !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
         private long mDefaultMaxIdlePendingTimeout =
@@ -1396,6 +1401,7 @@
         /**
          * 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 = mDefaultNotificationAllowlistDurationMs;
@@ -1413,9 +1419,14 @@
          */
         public boolean USE_MODE_MANAGER = mDefaultUseModeManager;
 
+        private final ContentResolver mResolver;
         private final boolean mSmallBatteryDevice;
+        private final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator =
+                new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
 
-        public Constants() {
+        public Constants(Handler handler, ContentResolver resolver) {
+            super(handler);
+            mResolver = resolver;
             initDefault();
             mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
             if (mSmallBatteryDevice) {
@@ -1424,8 +1435,14 @@
             }
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DEVICE_IDLE,
                     AppSchedulingModuleThread.getExecutor(), this);
+            mResolver.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS),
+                    false, this);
             // Load all the constants.
-            onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
+            updateSettingsConstantLocked();
+            mUserSettingDeviceConfigMediator.setDeviceConfigProperties(
+                    DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
+            updateConstantsLocked();
         }
 
         private void initDefault() {
@@ -1574,188 +1591,166 @@
             return (!COMPRESS_TIME || defTimeout < compTimeout) ? defTimeout : compTimeout;
         }
 
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            synchronized (DeviceIdleController.this) {
+                updateSettingsConstantLocked();
+                updateConstantsLocked();
+            }
+        }
+
+        private void updateSettingsConstantLocked() {
+            try {
+                mUserSettingDeviceConfigMediator.setSettingsString(
+                        Settings.Global.getString(mResolver,
+                                Settings.Global.DEVICE_IDLE_CONSTANTS));
+            } catch (IllegalArgumentException e) {
+                // Failed to parse the settings string, log this and move on with previous values.
+                Slog.e(TAG, "Bad device idle settings", e);
+            }
+        }
 
         @Override
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
             synchronized (DeviceIdleController.this) {
-                for (String name : properties.getKeyset()) {
-                    if (name == null) {
-                        continue;
-                    }
-                    switch (name) {
-                        case KEY_FLEX_TIME_SHORT:
-                            FLEX_TIME_SHORT = properties.getLong(
-                                    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,
-                                    mDefaultLightIdleAfterInactiveTimeout);
-                            break;
-                        case KEY_LIGHT_IDLE_TIMEOUT:
-                            LIGHT_IDLE_TIMEOUT = properties.getLong(
-                                    KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
-                            break;
-                        case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
-                            LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
-                                    KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
-                                    mDefaultLightIdleTimeoutInitialFlex);
-                            break;
-                        case KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX:
-                            LIGHT_IDLE_TIMEOUT_MAX_FLEX = properties.getLong(
-                                    KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX,
-                                    mDefaultLightIdleTimeoutMaxFlex);
-                            break;
-                        case KEY_LIGHT_IDLE_FACTOR:
-                            LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
-                                    KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
-                            break;
-                        case KEY_LIGHT_IDLE_INCREASE_LINEARLY:
-                            LIGHT_IDLE_INCREASE_LINEARLY = properties.getBoolean(
-                                    KEY_LIGHT_IDLE_INCREASE_LINEARLY,
-                                    mDefaultLightIdleIncreaseLinearly);
-                            break;
-                        case KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS:
-                            LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
-                                    KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
-                                    mDefaultLightIdleLinearIncreaseFactorMs);
-                            break;
-                        case KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS:
-                            LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
-                                    KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
-                                    mDefaultLightIdleFlexLinearIncreaseFactorMs);
-                            break;
-                        case KEY_LIGHT_MAX_IDLE_TIMEOUT:
-                            LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
-                                    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,
-                                    mDefaultLightIdleMaintenanceMinBudget);
-                            break;
-                        case KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET:
-                            LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = properties.getLong(
-                                    KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
-                                    mDefaultLightIdleMaintenanceMaxBudget);
-                            break;
-                        case KEY_MIN_LIGHT_MAINTENANCE_TIME:
-                            MIN_LIGHT_MAINTENANCE_TIME = properties.getLong(
-                                    KEY_MIN_LIGHT_MAINTENANCE_TIME,
-                                    mDefaultMinLightMaintenanceTime);
-                            break;
-                        case KEY_MIN_DEEP_MAINTENANCE_TIME:
-                            MIN_DEEP_MAINTENANCE_TIME = properties.getLong(
-                                    KEY_MIN_DEEP_MAINTENANCE_TIME,
-                                    mDefaultMinDeepMaintenanceTime);
-                            break;
-                        case KEY_INACTIVE_TIMEOUT:
-                            final long defaultInactiveTimeout = mSmallBatteryDevice
-                                    ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
-                                    : mDefaultInactiveTimeout;
-                            INACTIVE_TIMEOUT = properties.getLong(
-                                    KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
-                            break;
-                        case KEY_SENSING_TIMEOUT:
-                            SENSING_TIMEOUT = properties.getLong(
-                                    KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
-                            break;
-                        case KEY_LOCATING_TIMEOUT:
-                            LOCATING_TIMEOUT = properties.getLong(
-                                    KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
-                            break;
-                        case KEY_LOCATION_ACCURACY:
-                            LOCATION_ACCURACY = properties.getFloat(
-                                    KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
-                            break;
-                        case KEY_MOTION_INACTIVE_TIMEOUT:
-                            MOTION_INACTIVE_TIMEOUT = properties.getLong(
-                                    KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
-                            break;
-                        case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
-                            MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
-                                    KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
-                                    mDefaultMotionInactiveTimeoutFlex);
-                            break;
-                        case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
-                            final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
-                                    ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
-                                    : 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, mDefaultIdlePendingTimeout);
-                            break;
-                        case KEY_MAX_IDLE_PENDING_TIMEOUT:
-                            MAX_IDLE_PENDING_TIMEOUT = properties.getLong(
-                                    KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
-                            break;
-                        case KEY_IDLE_PENDING_FACTOR:
-                            IDLE_PENDING_FACTOR = properties.getFloat(
-                                    KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
-                            break;
-                        case KEY_QUICK_DOZE_DELAY_TIMEOUT:
-                            QUICK_DOZE_DELAY_TIMEOUT = properties.getLong(
-                                    KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
-                            break;
-                        case KEY_IDLE_TIMEOUT:
-                            IDLE_TIMEOUT = properties.getLong(
-                                    KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
-                            break;
-                        case KEY_MAX_IDLE_TIMEOUT:
-                            MAX_IDLE_TIMEOUT = properties.getLong(
-                                    KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
-                            break;
-                        case KEY_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, 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,
-                                    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,
-                                    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,
-                                    mDefaultSmsTempAppAllowlistDurationMs);
-                            break;
-                        case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
-                            NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
-                                    KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
-                                    mDefaultNotificationAllowlistDurationMs);
-                            break;
-                        case KEY_WAIT_FOR_UNLOCK:
-                            WAIT_FOR_UNLOCK = properties.getBoolean(
-                                    KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
-                            break;
-                        case KEY_USE_WINDOW_ALARMS:
-                            USE_WINDOW_ALARMS = properties.getBoolean(
-                                    KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
-                            break;
-                        case KEY_USE_MODE_MANAGER:
-                            USE_MODE_MANAGER = properties.getBoolean(
-                                    KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
-                            break;
-                        default:
-                            Slog.e(TAG, "Unknown configuration key: " + name);
-                            break;
-                    }
-                }
+                mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
+                updateConstantsLocked();
             }
         }
 
+        private void updateConstantsLocked() {
+            if (mSmallBatteryDevice) return;
+            FLEX_TIME_SHORT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
+
+            LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+                    mDefaultLightIdleAfterInactiveTimeout);
+
+            LIGHT_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
+
+            LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
+                    mDefaultLightIdleTimeoutInitialFlex);
+
+            LIGHT_IDLE_TIMEOUT_MAX_FLEX = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX,
+                    mDefaultLightIdleTimeoutMaxFlex);
+
+            LIGHT_IDLE_FACTOR = Math.max(1, mUserSettingDeviceConfigMediator.getFloat(
+                    KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
+
+            LIGHT_IDLE_INCREASE_LINEARLY = mUserSettingDeviceConfigMediator.getBoolean(
+                    KEY_LIGHT_IDLE_INCREASE_LINEARLY,
+                    mDefaultLightIdleIncreaseLinearly);
+
+            LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
+                    mDefaultLightIdleLinearIncreaseFactorMs);
+
+            LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
+                    mDefaultLightIdleFlexLinearIncreaseFactorMs);
+
+            LIGHT_MAX_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
+
+            LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
+                    mDefaultLightIdleMaintenanceMinBudget);
+
+            LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
+                    mDefaultLightIdleMaintenanceMaxBudget);
+
+            MIN_LIGHT_MAINTENANCE_TIME = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MIN_LIGHT_MAINTENANCE_TIME,
+                    mDefaultMinLightMaintenanceTime);
+
+            MIN_DEEP_MAINTENANCE_TIME = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MIN_DEEP_MAINTENANCE_TIME,
+                    mDefaultMinDeepMaintenanceTime);
+
+            final long defaultInactiveTimeout = mSmallBatteryDevice
+                    ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
+                    : mDefaultInactiveTimeout;
+            INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
+
+            SENSING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
+
+            LOCATING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
+
+            LOCATION_ACCURACY = mUserSettingDeviceConfigMediator.getFloat(
+                    KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
+
+            MOTION_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
+
+            MOTION_INACTIVE_TIMEOUT_FLEX = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
+                    mDefaultMotionInactiveTimeoutFlex);
+
+            final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
+                    ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
+                    : mDefaultIdleAfterInactiveTimeout;
+            IDLE_AFTER_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
+                    defaultIdleAfterInactiveTimeout);
+
+            IDLE_PENDING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
+
+            MAX_IDLE_PENDING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
+
+            IDLE_PENDING_FACTOR = mUserSettingDeviceConfigMediator.getFloat(
+                    KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
+
+            QUICK_DOZE_DELAY_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
+
+            IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
+
+            MAX_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
+
+            IDLE_FACTOR = mUserSettingDeviceConfigMediator.getFloat(KEY_IDLE_FACTOR,
+                    mDefaultIdleFactor);
+
+            MIN_TIME_TO_ALARM = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
+
+            MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
+                    mDefaultMaxTempAppAllowlistDurationMs);
+
+            MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+                    mDefaultMmsTempAppAllowlistDurationMs);
+
+            SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+                    mDefaultSmsTempAppAllowlistDurationMs);
+
+            NOTIFICATION_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+                    KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
+                    mDefaultNotificationAllowlistDurationMs);
+
+            WAIT_FOR_UNLOCK = mUserSettingDeviceConfigMediator.getBoolean(
+                    KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
+
+            USE_WINDOW_ALARMS = mUserSettingDeviceConfigMediator.getBoolean(
+                    KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
+
+            USE_MODE_MANAGER = mUserSettingDeviceConfigMediator.getBoolean(
+                    KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
+        }
+
         void dump(PrintWriter pw) {
             pw.println("  Settings:");
 
@@ -2490,9 +2485,10 @@
             return mConnectivityManager;
         }
 
-        Constants getConstants(DeviceIdleController controller) {
+        Constants getConstants(DeviceIdleController controller, Handler handler,
+                ContentResolver resolver) {
             if (mConstants == null) {
-                mConstants = controller.new Constants();
+                mConstants = controller.new Constants(handler, resolver);
             }
             return mConstants;
         }
@@ -2650,7 +2646,7 @@
                 }
             }
 
-            mConstants = mInjector.getConstants(this);
+            mConstants = mInjector.getConstants(this, mHandler, getContext().getContentResolver());
 
             readConfigFileLocked();
             updateWhitelistAppIdsLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index d2150b8..8381d1a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -109,7 +109,6 @@
 import android.content.ContentResolver;
 import android.provider.DeviceConfig;
 import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -154,7 +153,6 @@
     private long mMinSatiatedConsumptionLimit;
     private long mMaxSatiatedConsumptionLimit;
 
-    private final KeyValueListParser mParser = new KeyValueListParser(',');
     private final Injector mInjector;
 
     private final SparseArray<Action> mActions = new SparseArray<>();
@@ -241,35 +239,36 @@
         mRewards.clear();
 
         try {
-            mParser.setString(policyValuesString);
+            mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString);
+            mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
         } catch (IllegalArgumentException e) {
             Slog.e(TAG, "Global setting key incorrect: ", e);
         }
 
-        mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
+        mMinSatiatedBalanceOther = getConstantAsCake(
             KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
-        mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
+        mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(
                 KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
                 DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
                 mMinSatiatedBalanceOther);
-        mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+        mMinSatiatedBalanceExempted = getConstantAsCake(
                 KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
                 DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
                 mMinSatiatedBalanceHeadlessSystemApp);
-        mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
+        mMaxSatiatedBalance = getConstantAsCake(
             KEY_AM_MAX_SATIATED_BALANCE, DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
             Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
-        mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+        mMinSatiatedConsumptionLimit = getConstantAsCake(
                 KEY_AM_MIN_CONSUMPTION_LIMIT, DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
                 arcToCake(1));
-        mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+        mInitialSatiatedConsumptionLimit = getConstantAsCake(
                 KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
                 mMinSatiatedConsumptionLimit);
-        mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+        mMaxSatiatedConsumptionLimit = getConstantAsCake(
                 KEY_AM_MAX_CONSUMPTION_LIMIT, DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
                 mInitialSatiatedConsumptionLimit);
 
-        final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(mParser, properties,
+        final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(
                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
                 DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES);
 
@@ -279,49 +278,49 @@
         // run out of credits, and not when the system has run out of stock.
         mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
                 new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES),
                         exactAllowWhileIdleWakeupBasePrice,
                         /* respectsStockLimit */ false));
         mActions.put(ACTION_ALARM_WAKEUP_EXACT,
                 new Action(ACTION_ALARM_WAKEUP_EXACT,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES),
                         /* respectsStockLimit */ false));
 
         final long inexactAllowWhileIdleWakeupBasePrice =
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
                         DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES);
 
         mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
                 new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES),
                         inexactAllowWhileIdleWakeupBasePrice,
                         /* respectsStockLimit */ false));
         mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
                 new Action(ACTION_ALARM_WAKEUP_INEXACT,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES),
                         /* respectsStockLimit */ false));
 
-        final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
+        final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(
                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
                 DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES);
         mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
                 new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES),
                         exactAllowWhileIdleNonWakeupBasePrice,
@@ -329,18 +328,18 @@
 
         mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
                 new Action(ACTION_ALARM_NONWAKEUP_EXACT,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES),
                         /* respectsStockLimit */ false));
 
-        final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
+        final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(
                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
                 DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES);
-        final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(mParser, properties,
+        final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(
                 KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
                 DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES);
         mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
@@ -350,72 +349,72 @@
 
         mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
                 new Action(ACTION_ALARM_NONWAKEUP_INEXACT,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES)));
         mActions.put(ACTION_ALARM_CLOCK,
                 new Action(ACTION_ALARM_CLOCK,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
                                 DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES),
                         /* respectsStockLimit */ false));
 
         mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
                         DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
                         DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_TOP_ACTIVITY_MAX,
                         DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES)));
         mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
                         DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
                         DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
                         DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
         mRewards.put(REWARD_NOTIFICATION_INTERACTION,
                 new Reward(REWARD_NOTIFICATION_INTERACTION,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
                                 DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
                                 DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
                                 DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
         mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
                         DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
                         DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
                         DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
         mRewards.put(REWARD_OTHER_USER_INTERACTION,
                 new Reward(REWARD_OTHER_USER_INTERACTION,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
                                 DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
                                 DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
                                 DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index a4043dd..61096b9 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -33,9 +33,9 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -197,10 +197,17 @@
     }
 
     protected final InternalResourceService mIrs;
+    protected final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator;
     private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
 
     EconomicPolicy(@NonNull InternalResourceService irs) {
         mIrs = irs;
+        // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
+        // config can cause issues since the scales may be different, so use one or the other.
+        // If user settings exist, then just stick with the Settings constants, even if there
+        // are invalid values.
+        mUserSettingDeviceConfigMediator =
+                new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(',');
         for (int mId : getCostModifiers()) {
             initModifier(mId, irs);
         }
@@ -464,28 +471,14 @@
         return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
     }
 
-    protected long getConstantAsCake(@NonNull KeyValueListParser parser,
-            @Nullable DeviceConfig.Properties properties, String key, long defaultValCake) {
-        return getConstantAsCake(parser, properties, key, defaultValCake, 0);
+    protected long getConstantAsCake(String key, long defaultValCake) {
+        return getConstantAsCake(key, defaultValCake, 0);
     }
 
-    protected long getConstantAsCake(@NonNull KeyValueListParser parser,
-            @Nullable DeviceConfig.Properties properties, String key, long defaultValCake,
-            long minValCake) {
-        // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
-        // config can cause issues since the scales may be different, so use one or the other.
-        if (parser.size() > 0) {
-            // User settings take precedence. Just stick with the Settings constants, even if there
-            // are invalid values. It's not worth the time to evaluate all the key/value pairs to
-            // make sure there are valid ones before deciding.
-            return Math.max(minValCake,
-                parseCreditValue(parser.getString(key, null), defaultValCake));
-        }
-        if (properties != null) {
-            return Math.max(minValCake,
-                parseCreditValue(properties.getString(key, null), defaultValCake));
-        }
-        return Math.max(minValCake, defaultValCake);
+    protected long getConstantAsCake(String key, long defaultValCake, long minValCake) {
+        return Math.max(minValCake,
+                parseCreditValue(
+                        mUserSettingDeviceConfigMediator.getString(key, null), defaultValCake));
     }
 
     @VisibleForTesting
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 91a291f..69e5736 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -127,7 +127,6 @@
 import android.content.ContentResolver;
 import android.provider.DeviceConfig;
 import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -168,7 +167,6 @@
     private long mMinSatiatedConsumptionLimit;
     private long mMaxSatiatedConsumptionLimit;
 
-    private final KeyValueListParser mParser = new KeyValueListParser(',');
     private final Injector mInjector;
 
     private final SparseArray<Action> mActions = new SparseArray<>();
@@ -274,176 +272,177 @@
         mRewards.clear();
 
         try {
-            mParser.setString(policyValuesString);
+            mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString);
+            mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
         } catch (IllegalArgumentException e) {
             Slog.e(TAG, "Global setting key incorrect: ", e);
         }
 
-        mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
+        mMinSatiatedBalanceOther = getConstantAsCake(
             KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
-        mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
+        mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(
                 KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
                 DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
                 mMinSatiatedBalanceOther);
-        mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+        mMinSatiatedBalanceExempted = getConstantAsCake(
                 KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
                 DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
                 mMinSatiatedBalanceHeadlessSystemApp);
-        mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(mParser, properties,
+        mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(
                 KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
                 DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
-        mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
+        mMaxSatiatedBalance = getConstantAsCake(
             KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
             Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
-        mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+        mMinSatiatedConsumptionLimit = getConstantAsCake(
                 KEY_JS_MIN_CONSUMPTION_LIMIT, DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
                 arcToCake(1));
-        mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+        mInitialSatiatedConsumptionLimit = getConstantAsCake(
                 KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
                 mMinSatiatedConsumptionLimit);
-        mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+        mMaxSatiatedConsumptionLimit = getConstantAsCake(
                 KEY_JS_MAX_CONSUMPTION_LIMIT, DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
                 mInitialSatiatedConsumptionLimit);
 
         mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_MAX_START_CTP,
                         DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
                         DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_HIGH_START_CTP,
                         DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
                         DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
                         DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
                         DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_LOW_START_CTP,
                         DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
                         DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_MIN_START_CTP,
                         DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
                         DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES)));
         mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
                         DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
                         DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES)));
 
         mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
                         DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
                         DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_TOP_ACTIVITY_MAX,
                         DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES)));
         mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
                         DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
                         DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
                         DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
         mRewards.put(REWARD_NOTIFICATION_INTERACTION,
                 new Reward(REWARD_NOTIFICATION_INTERACTION,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
                                 DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
                                 DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
                                 DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
         mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
                         DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
                         DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
-                getConstantAsCake(mParser, properties,
+                getConstantAsCake(
                         KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
                         DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
         mRewards.put(REWARD_OTHER_USER_INTERACTION,
                 new Reward(REWARD_OTHER_USER_INTERACTION,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
         mRewards.put(REWARD_APP_INSTALL,
                 new Reward(REWARD_APP_INSTALL,
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_APP_INSTALL_INSTANT,
                                 DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_APP_INSTALL_ONGOING,
                                 DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES),
-                        getConstantAsCake(mParser, properties,
+                        getConstantAsCake(
                                 KEY_JS_REWARD_APP_INSTALL_MAX,
                                 DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES)));
     }
diff --git a/core/api/current.txt b/core/api/current.txt
index 9d13d8a..5b339fa 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9342,6 +9342,7 @@
     method public String getClassName();
     method public android.content.res.Configuration getConfiguration();
     method public int getEventType();
+    method @FlaggedApi("android.app.usage.user_interaction_type_api") @NonNull public android.os.PersistableBundle getExtras();
     method public String getPackageName();
     method public String getShortcutId();
     method public long getTimeStamp();
@@ -9407,6 +9408,8 @@
     method @FlaggedApi("android.app.usage.filter_based_event_query_api") @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.UsageEvents queryEvents(@NonNull android.app.usage.UsageEventsQuery);
     method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
     method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
+    field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_ACTION = "android.app.usage.extra.EVENT_ACTION";
+    field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_CATEGORY = "android.app.usage.extra.EVENT_CATEGORY";
     field public static final int INTERVAL_BEST = 4; // 0x4
     field public static final int INTERVAL_DAILY = 0; // 0x0
     field public static final int INTERVAL_MONTHLY = 2; // 0x2
@@ -9851,7 +9854,7 @@
     method @NonNull public android.content.AttributionSource build();
     method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
     method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
-    method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
+    method @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
     method @FlaggedApi("android.permission.flags.set_next_attribution_source") @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
     method @NonNull public android.content.AttributionSource.Builder setPid(int);
@@ -10598,6 +10601,7 @@
     field public static final String RESTRICTIONS_SERVICE = "restrictions";
     field public static final String ROLE_SERVICE = "role";
     field public static final String SEARCH_SERVICE = "search";
+    field @FlaggedApi("android.os.security_state_service") public static final String SECURITY_STATE_SERVICE = "security_state";
     field public static final String SENSOR_SERVICE = "sensor";
     field public static final String SHORTCUT_SERVICE = "shortcut";
     field public static final String STATUS_BAR_SERVICE = "statusbar";
@@ -10610,6 +10614,7 @@
     field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
     field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
     field public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
+    field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String TV_AD_SERVICE = "tv_ad";
     field public static final String TV_INPUT_SERVICE = "tv_input";
     field public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
     field public static final String UI_MODE_SERVICE = "uimode";
@@ -11098,6 +11103,7 @@
     field public static final String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
     field @Deprecated public static final String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
     field @Deprecated public static final String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
+    field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
     field @Deprecated public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
     field public static final String ACTION_USER_BACKGROUND = "android.intent.action.USER_BACKGROUND";
     field public static final String ACTION_USER_FOREGROUND = "android.intent.action.USER_FOREGROUND";
@@ -12363,6 +12369,7 @@
   public class PackageInfo implements android.os.Parcelable {
     ctor public PackageInfo();
     method public int describeContents();
+    method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
     method public long getLongVersionCode();
     method public void setLongVersionCode(long);
     method public void writeToParcel(android.os.Parcel, int);
@@ -12419,6 +12426,9 @@
     method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
+    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull String, @NonNull android.content.IntentSender);
     method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull android.content.pm.VersionedPackage, @NonNull android.content.IntentSender);
     method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull android.content.pm.VersionedPackage, int, @NonNull android.content.IntentSender);
@@ -12440,6 +12450,10 @@
     field public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
     field public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
     field public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
+    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ID = "android.content.pm.extra.UNARCHIVE_ID";
+    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
+    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
     field public static final int PACKAGE_SOURCE_DOWNLOADED_FILE = 4; // 0x4
     field public static final int PACKAGE_SOURCE_LOCAL_FILE = 3; // 0x3
     field public static final int PACKAGE_SOURCE_OTHER = 1; // 0x1
@@ -12455,6 +12469,13 @@
     field public static final int STATUS_FAILURE_TIMEOUT = 8; // 0x8
     field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
     field public static final int STATUS_SUCCESS = 0; // 0x0
+    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; // 0x4
+    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; // 0x5
+    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2; // 0x2
+    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3; // 0x3
+    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1; // 0x1
+    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_GENERIC_ERROR = 100; // 0x64
+    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_OK = 0; // 0x0
   }
 
   public static final class PackageInstaller.InstallConstraints implements android.os.Parcelable {
@@ -12618,6 +12639,7 @@
     method @RequiresPermission(android.Manifest.permission.ENFORCE_UPDATE_OWNERSHIP) public void setRequestUpdateOwnership(boolean);
     method public void setRequireUserAction(int);
     method public void setSize(long);
+    method @FlaggedApi("android.content.pm.archiving") public void setUnarchiveId(int);
     method public void setWhitelistedRestrictedPermissions(@Nullable java.util.Set<java.lang.String>);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionParams> CREATOR;
@@ -12647,6 +12669,7 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public int banner;
     field public int icon;
+    field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
     field public int labelRes;
     field public int logo;
     field public android.os.Bundle metaData;
@@ -12769,6 +12792,7 @@
     method public boolean hasSigningCertificate(int, @NonNull byte[], int);
     method public abstract boolean hasSystemFeature(@NonNull String);
     method public abstract boolean hasSystemFeature(@NonNull String, int);
+    method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean isAutoRevokeWhitelisted(@NonNull String);
     method public boolean isAutoRevokeWhitelisted();
     method public boolean isDefaultApplicationIcon(@NonNull android.graphics.drawable.Drawable);
@@ -13010,6 +13034,7 @@
     field public static final int INSTALL_SCENARIO_FAST = 1; // 0x1
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_APEX = 1073741824; // 0x40000000
+    field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
     field public static final int MATCH_DIRECT_BOOT_AUTO = 268435456; // 0x10000000
     field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
@@ -22031,6 +22056,19 @@
     method public void onJetUserIdUpdate(android.media.JetPlayer, int, int);
   }
 
+  @FlaggedApi("android.media.audio.loudness_configurator_api") public class LoudnessCodecConfigurator {
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") public void addMediaCodec(@NonNull android.media.MediaCodec);
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create();
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(@NonNull java.util.concurrent.Executor, @NonNull android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener);
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public android.os.Bundle getLoudnessCodecParams(@NonNull android.media.AudioTrack, @NonNull android.media.MediaCodec);
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") public void removeMediaCodec(@NonNull android.media.MediaCodec);
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") public void setAudioTrack(@Nullable android.media.AudioTrack);
+  }
+
+  @FlaggedApi("android.media.audio.loudness_configurator_api") public static interface LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener {
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public default android.os.Bundle onLoudnessCodecUpdate(@NonNull android.media.MediaCodec, @NonNull android.os.Bundle);
+  }
+
   public class MediaActionSound {
     ctor public MediaActionSound();
     method public void load(int);
@@ -27299,6 +27337,13 @@
 
 }
 
+package android.media.tv.ad {
+
+  @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public class TvAdManager {
+  }
+
+}
+
 package android.media.tv.interactive {
 
   public final class AppLinkInfo implements android.os.Parcelable {
@@ -32837,7 +32882,7 @@
     method public static android.os.Message obtain(android.os.Handler, int, Object);
     method public static android.os.Message obtain(android.os.Handler, int, int, int);
     method public static android.os.Message obtain(android.os.Handler, int, int, int, Object);
-    method public android.os.Bundle peekData();
+    method @Nullable public android.os.Bundle peekData();
     method public void recycle();
     method public void sendToTarget();
     method public void setAsynchronous(boolean);
@@ -33376,6 +33421,13 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
   }
 
+  @FlaggedApi("android.os.security_state_service") public class SecurityStateManager {
+    method @FlaggedApi("android.os.security_state_service") @NonNull public android.os.Bundle getGlobalSecurityState();
+    field public static final String KEY_KERNEL_VERSION = "kernel_version";
+    field public static final String KEY_SYSTEM_SPL = "system_spl";
+    field public static final String KEY_VENDOR_SPL = "vendor_spl";
+  }
+
   public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
     method public void close();
     method @NonNull public static android.os.SharedMemory create(@Nullable String, int) throws android.system.ErrnoException;
@@ -36770,7 +36822,7 @@
     field public static final String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
     field public static final String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
     field public static final String ACTION_CONDITION_PROVIDER_SETTINGS = "android.settings.ACTION_CONDITION_PROVIDER_SETTINGS";
-    field public static final String ACTION_CREDENTIAL_PROVIDER = "android.settings.CREDENTIAL_PROVIDER";
+    field @FlaggedApi("android.credentials.flags.new_settings_intents") public static final String ACTION_CREDENTIAL_PROVIDER = "android.settings.CREDENTIAL_PROVIDER";
     field public static final String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
     field public static final String ACTION_DATA_USAGE_SETTINGS = "android.settings.DATA_USAGE_SETTINGS";
     field public static final String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 285dcc6a..b7714f1 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -30,36 +30,6 @@
 
 }
 
-package android.app.slice {
-
-  public final class Slice implements android.os.Parcelable {
-    field @Deprecated public static final String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
-    field @Deprecated public static final String SUBTYPE_SLIDER = "slider";
-  }
-
-  public static class Slice.Builder {
-    ctor @Deprecated public Slice.Builder(@NonNull android.net.Uri);
-    method @Deprecated public android.app.slice.Slice.Builder addTimestamp(long, @Nullable String, java.util.List<java.lang.String>);
-    method @Deprecated public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
-  }
-
-  public final class SliceItem implements android.os.Parcelable {
-    method @Deprecated public long getTimestamp();
-    field @Deprecated public static final String FORMAT_TIMESTAMP = "long";
-  }
-
-  public class SliceManager {
-    method @Deprecated @Nullable public android.app.slice.Slice bindSlice(@NonNull android.net.Uri, @NonNull java.util.List<android.app.slice.SliceSpec>);
-    method @Deprecated @Nullable public android.app.slice.Slice bindSlice(@NonNull android.content.Intent, @NonNull java.util.List<android.app.slice.SliceSpec>);
-    method @Deprecated public void pinSlice(@NonNull android.net.Uri, @NonNull java.util.List<android.app.slice.SliceSpec>);
-  }
-
-  public abstract class SliceProvider extends android.content.ContentProvider {
-    method @Deprecated public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
-  }
-
-}
-
 package android.app.usage {
 
   public class StorageStatsManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fc23f9b..0497c60 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3614,7 +3614,6 @@
     field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
     field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
     field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
-    field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
     field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
     field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -3862,16 +3861,9 @@
     field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
   }
 
-  public class PackageInfo implements android.os.Parcelable {
-    method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
-  }
-
   public class PackageInstaller {
     method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
     method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
-    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
     field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
     field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
@@ -3882,23 +3874,12 @@
     field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
     field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
     field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
-    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
-    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ID = "android.content.pm.extra.UNARCHIVE_ID";
-    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
-    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
     field public static final int LOCATION_DATA_APP = 0; // 0x0
     field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
     field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
     field public static final int REASON_CONFIRM_PACKAGE_CHANGE = 0; // 0x0
     field public static final int REASON_OWNERSHIP_CHANGED = 1; // 0x1
     field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2
-    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; // 0x4
-    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; // 0x5
-    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2; // 0x2
-    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3; // 0x3
-    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1; // 0x1
-    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_GENERIC_ERROR = 100; // 0x64
-    field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_OK = 0; // 0x0
   }
 
   public static class PackageInstaller.InstallInfo {
@@ -3948,14 +3929,12 @@
     method public void setRequestDowngrade(boolean);
     method @FlaggedApi("android.content.pm.rollback_lifetime") @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void setRollbackLifetimeMillis(long);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
-    method @FlaggedApi("android.content.pm.archiving") public void setUnarchiveId(int);
   }
 
   public class PackageItemInfo {
     method public static void forceSafeLabels();
     method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
     method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
-    field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
   }
 
   public abstract class PackageManager {
@@ -3987,7 +3966,6 @@
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
@@ -4105,7 +4083,6 @@
     field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
     field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
     field public static final int MATCH_ANY_USER = 4194304; // 0x400000
-    field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
     field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
@@ -12214,6 +12191,7 @@
     method public boolean hasExpanded();
     method public boolean hasInteracted();
     method public boolean hasSeen();
+    method @FlaggedApi("android.app.lifetime_extension_refactor") public boolean hasSmartReplied();
     method public boolean hasSnoozed();
     method public boolean hasViewedSettings();
     method public void setDirectReplied();
@@ -12221,6 +12199,7 @@
     method public void setDismissalSurface(int);
     method public void setExpanded();
     method public void setSeen();
+    method @FlaggedApi("android.app.lifetime_extension_refactor") public void setSmartReplied();
     method public void setSnoozed();
     method public void setViewedSettings();
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4f8e8dd..014ddd41 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3484,9 +3484,20 @@
         // only do this if the user already has more than one preferred locale
         if (android.content.res.Flags.defaultLocale()
                 && r.getConfiguration().getLocales().size() > 1) {
-            LocaleConfig lc = getUserId() < 0
-                    ? LocaleConfig.fromContextIgnoringOverride(this)
-                    : new LocaleConfig(this);
+            LocaleConfig lc;
+            if (getUserId() < 0) {
+                lc = LocaleConfig.fromContextIgnoringOverride(this);
+            } else {
+                // This is needed because the app might have locale config overrides that need to
+                // be read from disk in order for resources to correctly choose which values to
+                // load.
+                StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
+                try {
+                    lc = new LocaleConfig(this);
+                } finally {
+                    StrictMode.setThreadPolicy(policy);
+                }
+            }
             mResourcesManager.setLocaleConfig(lc);
         }
     }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 337e3f1..8c5773a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -745,6 +745,16 @@
     @TestApi
     public static final int FLAG_USER_INITIATED_JOB = 0x00008000;
 
+    /**
+     * Bit to be bitwise-ored into the {@link #flags} field that should be
+     * set if this notification has been lifetime extended due to a direct reply.
+     *
+     * This flag is for internal use only; applications cannot set this flag directly.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+    public static final int FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY = 0x00010000;
+
     private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
             BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
             DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 6009c29..24a5157 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -159,7 +159,8 @@
      * Loads {@link ApkAssets} and caches them to prevent their garbage collection while the
      * instance is alive and reachable.
      */
-    private class ApkAssetsSupplier {
+    @VisibleForTesting
+    protected class ApkAssetsSupplier {
         final ArrayMap<ApkKey, ApkAssets> mLocalCache = new ArrayMap<>();
 
         /**
@@ -544,7 +545,10 @@
      * from an {@link ApkAssetsSupplier} if non-null; otherwise ApkAssets are loaded using
      * {@link #loadApkAssets(ApkKey)}.
      */
-    private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
+
+    @VisibleForTesting
+    @UnsupportedAppUsage
+    protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
             @Nullable ApkAssetsSupplier apkSupplier) {
         final AssetManager.Builder builder = new AssetManager.Builder();
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 79a5879..9cf732a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -137,6 +137,8 @@
 import android.media.soundtrigger.SoundTriggerManager;
 import android.media.tv.ITvInputManager;
 import android.media.tv.TvInputManager;
+import android.media.tv.ad.ITvAdManager;
+import android.media.tv.ad.TvAdManager;
 import android.media.tv.interactive.ITvInteractiveAppManager;
 import android.media.tv.interactive.TvInteractiveAppManager;
 import android.media.tv.tunerresourcemanager.ITunerResourceManager;
@@ -174,6 +176,7 @@
 import android.os.IPowerManager;
 import android.os.IPowerStatsService;
 import android.os.IRecoverySystem;
+import android.os.ISecurityStateManager;
 import android.os.ISystemUpdateManager;
 import android.os.IThermalService;
 import android.os.IUserManager;
@@ -182,6 +185,7 @@
 import android.os.PermissionEnforcer;
 import android.os.PowerManager;
 import android.os.RecoverySystem;
+import android.os.SecurityStateManager;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.StatsFrameworkInitializer;
@@ -628,6 +632,17 @@
                         ctx.mMainThread.getHandler());
             }});
 
+        registerService(Context.SECURITY_STATE_SERVICE, SecurityStateManager.class,
+                new CachedServiceFetcher<SecurityStateManager>() {
+                    @Override
+                    public SecurityStateManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(
+                                Context.SECURITY_STATE_SERVICE);
+                        ISecurityStateManager service = ISecurityStateManager.Stub.asInterface(b);
+                        return new SecurityStateManager(service);
+                    }});
+
         registerService(Context.SENSOR_SERVICE, SensorManager.class,
                 new CachedServiceFetcher<SensorManager>() {
             @Override
@@ -960,6 +975,18 @@
                 return new TvInteractiveAppManager(service, ctx.getUserId());
             }});
 
+        registerService(Context.TV_AD_SERVICE, TvAdManager.class,
+                new CachedServiceFetcher<TvAdManager>() {
+                    @Override
+                    public TvAdManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder iBinder =
+                                ServiceManager.getServiceOrThrow(Context.TV_AD_SERVICE);
+                        ITvAdManager service =
+                                ITvAdManager.Stub.asInterface(iBinder);
+                        return new TvAdManager(service, ctx.getUserId());
+                    }});
+
         registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
                 new CachedServiceFetcher<TvInputManager>() {
             @Override
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 6a03c17..ce1d43d 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -180,6 +180,11 @@
 
     @Override
     public void injectInputEventToInputFilter(InputEvent event) throws RemoteException {
+        synchronized (mLock) {
+            throwIfCalledByNotTrustedUidLocked();
+            throwIfShutdownLocked();
+            throwIfNotConnectedLocked();
+        }
         mAccessibilityManager.injectInputEventToInputFilter(event);
     }
 
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 823fdd2..475ee7a 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -195,13 +195,6 @@
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     /**
-     * Key to retrieve an extra added to an intent when the value of a slider is changed.
-     * @deprecated remove once support lib is update to use EXTRA_RANGE_VALUE instead
-     * @removed
-     */
-    @Deprecated
-    public static final String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
-    /**
      * Key to retrieve an extra added to an intent when the value of an input range is changed.
      */
     public static final String EXTRA_RANGE_VALUE = "android.app.slice.extra.RANGE_VALUE";
@@ -223,13 +216,6 @@
      */
     public static final String SUBTYPE_COLOR = "color";
     /**
-     * Subtype to tag an item as representing a slider.
-     * @deprecated remove once support lib is update to use SUBTYPE_RANGE instead
-     * @removed
-     */
-    @Deprecated
-    public static final String SUBTYPE_SLIDER = "slider";
-    /**
      * Subtype to tag an item as representing a range.
      * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE} containing
      * a {@link #SUBTYPE_VALUE} and possibly a {@link #SUBTYPE_MAX}.
@@ -361,15 +347,6 @@
         private SliceSpec mSpec;
 
         /**
-         * @deprecated TO BE REMOVED
-         * @removed
-         */
-        @Deprecated
-        public Builder(@NonNull Uri uri) {
-            mUri = uri;
-        }
-
-        /**
          * Create a builder which will construct a {@link Slice} for the given Uri.
          * @param uri Uri to tag for this slice.
          * @param spec the spec for this slice.
@@ -413,15 +390,6 @@
         }
 
         /**
-         * @deprecated TO BE REMOVED
-         * @removed
-         */
-        public Builder setSpec(SliceSpec spec) {
-            mSpec = spec;
-            return this;
-        }
-
-        /**
          * Add a sub-slice to the slice being constructed
          * @param subType Optional template-specific type information
          * @see SliceItem#getSubType()
@@ -498,16 +466,6 @@
         }
 
         /**
-         * @deprecated TO BE REMOVED.
-         * @removed
-         */
-        @Deprecated
-        public Slice.Builder addTimestamp(long time, @Nullable @SliceSubtype String subType,
-                @SliceHint List<String> hints) {
-            return addLong(time, subType, hints);
-        }
-
-        /**
          * Add a long to the slice being constructed
          * @param subType Optional template-specific type information
          * @see SliceItem#getSubType()
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index ed32a1b..2d6f4a6 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -102,12 +102,6 @@
      */
     public static final String FORMAT_LONG = "long";
     /**
-     * @deprecated TO BE REMOVED
-     * @removed
-     */
-    @Deprecated
-    public static final String FORMAT_TIMESTAMP = FORMAT_LONG;
-    /**
      * A {@link SliceItem} that contains a {@link RemoteInput}.
      */
     public static final String FORMAT_REMOTE_INPUT = "input";
@@ -257,15 +251,6 @@
     }
 
     /**
-     * @deprecated replaced by {@link #getLong()}
-     * @removed
-     */
-    @Deprecated
-    public long getTimestamp() {
-        return (Long) mObj;
-    }
-
-    /**
      * @param hint The hint to check for
      * @return true if this item contains the given hint
      */
@@ -348,7 +333,7 @@
             case FORMAT_INT:
                 dest.writeInt((Integer) obj);
                 break;
-            case FORMAT_TIMESTAMP:
+            case FORMAT_LONG:
                 dest.writeLong((Long) obj);
                 break;
         }
@@ -368,7 +353,7 @@
                         Slice.CREATOR.createFromParcel(in));
             case FORMAT_INT:
                 return in.readInt();
-            case FORMAT_TIMESTAMP:
+            case FORMAT_LONG:
                 return in.readLong();
             case FORMAT_REMOTE_INPUT:
                 return RemoteInput.CREATOR.createFromParcel(in);
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 1e4934e..2e179d0 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -141,15 +141,6 @@
     }
 
     /**
-     * @deprecated TO BE REMOVED
-     * @removed
-     */
-    @Deprecated
-    public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
-        pinSlice(uri, new ArraySet<>(specs));
-    }
-
-    /**
      * Remove a pin for a slice.
      * <p>
      * If the slice has no other pins/callbacks then the slice will be unpinned.
@@ -273,15 +264,6 @@
     }
 
     /**
-     * @deprecated TO BE REMOVED
-     * @removed
-     */
-    @Deprecated
-    public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
-        return bindSlice(uri, new ArraySet<>(supportedSpecs));
-    }
-
-    /**
      * Turns a slice intent into a slice uri. Expects an explicit intent.
      * <p>
      * This goes through a several stage resolution process to determine if any slice
@@ -412,17 +394,6 @@
     }
 
     /**
-     * @deprecated TO BE REMOVED.
-     * @removed
-     */
-    @Deprecated
-    @Nullable
-    public Slice bindSlice(@NonNull Intent intent,
-            @NonNull List<SliceSpec> supportedSpecs) {
-        return bindSlice(intent, new ArraySet<>(supportedSpecs));
-    }
-
-    /**
      * Determine whether a particular process and user ID has been granted
      * permission to access a specific slice URI.
      *
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 63835cb..42c3aa6 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -209,15 +209,6 @@
      * @see Slice#HINT_PARTIAL
      */
     public Slice onBindSlice(Uri sliceUri, Set<SliceSpec> supportedSpecs) {
-        return onBindSlice(sliceUri, new ArrayList<>(supportedSpecs));
-    }
-
-    /**
-     * @deprecated TO BE REMOVED
-     * @removed
-     */
-    @Deprecated
-    public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
         return null;
     }
 
@@ -479,7 +470,7 @@
         } finally {
             Handler.getMain().removeCallbacks(mAnr);
         }
-        Slice.Builder parent = new Slice.Builder(sliceUri);
+        Slice.Builder parent = new Slice.Builder(sliceUri, null);
         Slice.Builder childAction = new Slice.Builder(parent)
                 .addIcon(Icon.createWithResource(context,
                         com.android.internal.R.drawable.ic_permission), null,
@@ -492,7 +483,8 @@
                 .getTheme().resolveAttribute(android.R.attr.colorAccent, tv, true);
         int deviceDefaultAccent = tv.data;
 
-        parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+        Uri subSliceUri = sliceUri.buildUpon().appendPath("permission").build();
+        Slice.Builder subSlice = new Slice.Builder(subSliceUri, null)
                 .addIcon(Icon.createWithResource(context,
                         com.android.internal.R.drawable.ic_arrow_forward), null,
                         Collections.emptyList())
@@ -500,8 +492,8 @@
                         Collections.emptyList())
                 .addInt(deviceDefaultAccent, SUBTYPE_COLOR,
                         Collections.emptyList())
-                .addSubSlice(childAction.build(), null)
-                .build(), null);
+                .addSubSlice(childAction.build(), null);
+        parent.addSubSlice(subSlice.build(), null);
         return parent.addHints(Arrays.asList(Slice.HINT_PERMISSION_REQUEST)).build();
     }
 
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index ebd5d64..cf19178 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -22,6 +22,7 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEventsQuery;
 import android.content.pm.ParceledListSlice;
+import android.os.PersistableBundle;
 
 /**
  * System private API for talking with the UsageStatsManagerService.
@@ -77,6 +78,8 @@
             String callingPackage);
     void reportUsageStop(in IBinder activity, String token, String callingPackage);
     void reportUserInteraction(String packageName, int userId);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)")
+    void reportUserInteractionWithBundle(String packageName, int userId, in PersistableBundle eventExtras);
     int getUsageSource();
     void forceUsageSourceSettingRead();
     long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
diff --git a/core/java/android/app/usage/ParcelableUsageEventList.java b/core/java/android/app/usage/ParcelableUsageEventList.java
index aa32392..7bc6cb9 100644
--- a/core/java/android/app/usage/ParcelableUsageEventList.java
+++ b/core/java/android/app/usage/ParcelableUsageEventList.java
@@ -223,6 +223,7 @@
         event.mContentAnnotations = null;
         event.mNotificationChannelId = null;
         event.mLocusId = null;
+        event.mExtras = null;
 
         switch (event.mEventType) {
             case Event.CONFIGURATION_CHANGE -> {
@@ -237,6 +238,11 @@
             case Event.STANDBY_BUCKET_CHANGED -> event.mBucketAndReason = in.readInt();
             case Event.NOTIFICATION_INTERRUPTION -> event.mNotificationChannelId = in.readString();
             case Event.LOCUS_ID_SET -> event.mLocusId = in.readString();
+            case Event.USER_INTERACTION -> {
+                if (in.readInt() != 0) {
+                    event.mExtras = in.readPersistableBundle(getClass().getClassLoader());
+                }
+            }
         }
         event.mFlags = in.readInt();
 
@@ -263,6 +269,14 @@
             case Event.STANDBY_BUCKET_CHANGED -> dest.writeInt(event.mBucketAndReason);
             case Event.NOTIFICATION_INTERRUPTION -> dest.writeString(event.mNotificationChannelId);
             case Event.LOCUS_ID_SET -> dest.writeString(event.mLocusId);
+            case Event.USER_INTERACTION -> {
+                if (event.mExtras != null) {
+                    dest.writeInt(1);
+                    dest.writePersistableBundle(event.mExtras);
+                } else {
+                    dest.writeInt(0);
+                }
+            }
         }
         dest.writeInt(event.mFlags);
     }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6c7eba0..9eb73b3 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,7 +16,9 @@
 package android.app.usage;
 
 import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +26,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -551,6 +554,22 @@
         public int mLocusIdToken = UNASSIGNED_TOKEN;
 
         /** @hide */
+        public PersistableBundle mExtras = null;
+
+        /** @hide */
+        public static class UserInteractionEventExtrasToken {
+            public int mCategoryToken = UNASSIGNED_TOKEN;
+            public int mActionToken = UNASSIGNED_TOKEN;
+
+            public UserInteractionEventExtrasToken() {
+                // Do nothing.
+            }
+        }
+
+        /** @hide */
+        public UserInteractionEventExtrasToken mUserInteractionExtrasToken = null;
+
+        /** @hide */
         @EventFlags
         public int mFlags;
 
@@ -650,6 +669,21 @@
         }
 
         /**
+         * Retrieves a map of extended data from the event if the event is of type
+         * {@link #USER_INTERACTION}.
+         *
+         * @return the map of all extras that associated with the reported user interaction
+         *         event. The returned {@link PersistableBundle} will contain the extras
+         *         {@link UsageStatsManager#EXTRA_EVENT_CATEGORY} and
+         *         {@link UsageStatsManager#EXTRA_EVENT_ACTION}. {@link PersistableBundle#EMPTY}
+         *         will be returned if the details are not available.
+         */
+        @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+        public @NonNull PersistableBundle getExtras() {
+            return mExtras == null ? PersistableBundle.EMPTY : mExtras;
+        }
+
+        /**
          * Returns a {@link Configuration} for this event if the event is of type
          * {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
          */
@@ -747,6 +781,7 @@
             mBucketAndReason = orig.mBucketAndReason;
             mNotificationChannelId = orig.mNotificationChannelId;
             mLocusId = orig.mLocusId;
+            mExtras = orig.mExtras;
         }
     }
 
@@ -802,6 +837,7 @@
         if (mEventCount != mEventsToWrite.size()) {
             Log.w(TAG, "Partial usage event list received: " + mEventCount + " != "
                     + mEventsToWrite.size());
+            mEventCount = mEventsToWrite.size();
         }
     }
 
@@ -987,6 +1023,14 @@
             case Event.LOCUS_ID_SET:
                 p.writeString(event.mLocusId);
                 break;
+            case Event.USER_INTERACTION:
+                if (event.mExtras != null) {
+                    p.writeInt(1);
+                    p.writePersistableBundle(event.mExtras);
+                } else {
+                    p.writeInt(0);
+                }
+                break;
         }
         p.writeInt(event.mFlags);
     }
@@ -1036,6 +1080,7 @@
         eventOut.mContentAnnotations = null;
         eventOut.mNotificationChannelId = null;
         eventOut.mLocusId = null;
+        eventOut.mExtras = null;
 
         switch (eventOut.mEventType) {
             case Event.CONFIGURATION_CHANGE:
@@ -1059,6 +1104,11 @@
             case Event.LOCUS_ID_SET:
                 eventOut.mLocusId = p.readString();
                 break;
+            case Event.USER_INTERACTION:
+                if (p.readInt() != 0) {
+                    eventOut.mExtras = p.readPersistableBundle(getClass().getClassLoader());
+                }
+                break;
         }
         eventOut.mFlags = p.readInt();
     }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 4f1c993..85d223d 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -28,6 +28,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.BroadcastOptions;
 import android.app.PendingIntent;
@@ -35,6 +36,7 @@
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.Build;
+import android.os.PersistableBundle;
 import android.os.PowerWhitelistManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -392,6 +394,23 @@
     @SystemApi
     public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
 
+    /**
+     * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
+     * the category of the user interaction associated with the event. The category cannot
+     * be more than 127 characters, longer value will be truncated to 127 characters.
+     */
+    @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+    public static final String EXTRA_EVENT_CATEGORY =
+            "android.app.usage.extra.EVENT_CATEGORY";
+
+    /**
+     * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
+     * the action of the user interaction associated with the event. The action cannot be
+     * more than 127 characters, longer value will be truncated to 127 characters.
+     */
+    @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+    public static final String EXTRA_EVENT_ACTION =
+            "android.app.usage.extra.EVENT_ACTION";
 
     /**
      * App usage observers will consider the task root package the source of usage.
@@ -562,10 +581,10 @@
      * then {@code null} will be returned.</em>
      *
      * @param beginTime The inclusive beginning of the range of events to include in the results.
-     *                 Defined in terms of "Unix time", see
-     *                 {@link java.lang.System#currentTimeMillis}.
+     *                  Defined in terms of "Unix time", see
+     *                  {@link java.lang.System#currentTimeMillis}.
      * @param endTime The exclusive end of the range of events to include in the results. Defined
-     *               in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+     *                in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
      * @return A {@link UsageEvents}.
      */
     public UsageEvents queryEvents(long beginTime, long endTime) {
@@ -611,10 +630,10 @@
      * then {@code null} will be returned.</em>
      *
      * @param beginTime The inclusive beginning of the range of events to include in the results.
-     *                 Defined in terms of "Unix time", see
-     *                 {@link java.lang.System#currentTimeMillis}.
+     *                  Defined in terms of "Unix time", see
+     *                  {@link java.lang.System#currentTimeMillis}.
      * @param endTime The exclusive end of the range of events to include in the results. Defined
-     *               in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+     *                in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
      * @return A {@link UsageEvents} object.
      *
      * @see #queryEvents(long, long)
@@ -1128,6 +1147,7 @@
      * Reports user interaction with a given package in the given user.
      *
      * <p><em>This method is only for use by the system</em>
+     *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
@@ -1140,6 +1160,38 @@
     }
 
     /**
+     * Reports user interaction with given package and a particular {@code extras}
+     * in the given user.
+     *
+     * <p>
+     * Note: The structure of {@code extras} is a {@link PersistableBundle} with the
+     * category {@link #EXTRA_EVENT_CATEGORY} and the action {@link #EXTRA_EVENT_ACTION}.
+     * Category provides additional detail about the user interaction, the value
+     * is defined in namespace based. Example: android.app.notification could be used to
+     * indicate that the reported user interaction is related to notification. Action
+     * indicates the general action that performed.
+     * </p>
+     *
+     * @param packageName The package name of the app
+     * @param userId The user id who triggers the user interaction
+     * @param extras The {@link PersistableBundle} that will be used to specify the
+     *               extra details for the user interaction event. The {@link PersistableBundle}
+     *               must contain the extras {@link #EXTRA_EVENT_CATEGORY},
+     *               {@link #EXTRA_EVENT_ACTION}. Cannot be empty.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+    @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
+    public void reportUserInteraction(@NonNull String packageName, @UserIdInt int userId,
+            @NonNull PersistableBundle extras) {
+        try {
+            mService.reportUserInteractionWithBundle(packageName, userId, extras);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Report usage associated with a particular {@code token} has started. Tokens are app defined
      * strings used to represent usage of in-app features. Apps with the {@link
      * android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 51a7f1c..6e45147 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -558,7 +558,7 @@
                             }
                         }).toArray(ComponentName[]::new));
             } catch (Exception e) {
-                Log.e(TAG, "Nofity service of inheritance info", e);
+                Log.e(TAG, "Notify service of inheritance info", e);
             }
         });
     }
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 4b2cee6..697c25c 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -730,10 +730,7 @@
 
         /**
          * The next app to receive the permission protected data.
-         *
-         * @deprecated Use {@link #setNextAttributionSource} instead.
          */
-        @Deprecated
         public @NonNull Builder setNext(@Nullable AttributionSource value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x20;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1c6c7b5..b75c64d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -72,6 +72,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Flags;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.IBinder;
@@ -4214,6 +4215,7 @@
             DEVICE_LOCK_SERVICE,
             VIRTUALIZATION_SERVICE,
             GRAMMATICAL_INFLECTION_SERVICE,
+            SECURITY_STATE_SERVICE,
 
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -5818,6 +5820,17 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.tv.ad.TvAdManager} for interacting with TV client-side advertisement
+     * services on the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.tv.ad.TvAdManager
+     */
+    @FlaggedApi(android.media.tv.flags.Flags.FLAG_ENABLE_AD_SERVICE_FW)
+    public static final String TV_AD_SERVICE = "tv_ad";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.media.tv.TunerResourceManager} for interacting with TV
      * tuner resources on the device.
      *
@@ -6478,6 +6491,16 @@
     public static final String SHARED_CONNECTIVITY_SERVICE = "shared_connectivity";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.SecurityStateManager} for accessing the security state manager service.
+     *
+     * @see #getSystemService(String)
+     * @see android.os.SecurityStateManager
+     */
+    @FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+    public static final String SECURITY_STATE_SERVICE = "security_state";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c7a86fb..38bcfa2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5353,11 +5353,11 @@
      * Broadcast Action: Sent to the responsible installer of an archived package when unarchival
      * is requested.
      *
-     * @see android.content.pm.PackageInstaller#requestUnarchive(String)
-     * @hide
+     * @see android.content.pm.PackageInstaller#requestUnarchive
      */
-    @SystemApi
     @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
+    @BroadcastBehavior(explicitOnly = true)
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
 
     // ---------------------------------------------------------------------
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 1cfdb8b..5736a6d 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -20,7 +20,6 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -522,9 +521,7 @@
     /**
      * Returns the time at which the app was archived for the user.  Units are as
      * per {@link System#currentTimeMillis()}.
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public @CurrentTimeMillisLong long getArchiveTimeMillis() {
         return mArchiveTimeMillis;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 6df1f60..d35c392 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -354,10 +354,7 @@
     /**
      * Extra field for the package name of a package that is requested to be unarchived. Sent as
      * part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final String EXTRA_UNARCHIVE_PACKAGE_NAME =
             "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
@@ -366,22 +363,16 @@
      * Extra field for the unarchive ID. Sent as
      * part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
      *
-     * @see Session#setUnarchiveId(int)
-     *
-     * @hide
+     * @see SessionParams#setUnarchiveId
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final String EXTRA_UNARCHIVE_ID =
             "android.content.pm.extra.UNARCHIVE_ID";
 
     /**
      * If true, the requestor of the unarchival has specified that the app should be unarchived
-     * for {@link android.os.UserHandle#ALL}.
-     *
-     * @hide
+     * for all users.
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final String EXTRA_UNARCHIVE_ALL_USERS =
             "android.content.pm.extra.UNARCHIVE_ALL_USERS";
@@ -398,9 +389,7 @@
      * failure dialog.
      *
      * @see #requestUnarchive
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
 
@@ -675,10 +664,7 @@
      *
      * <p> Note that this does not mean that the unarchival has completed. This status should be
      * sent before any longer asynchronous action (e.g. app download) is started.
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final int UNARCHIVAL_OK = 0;
 
@@ -687,10 +673,7 @@
      *
      * <p> An example use case for this could be that the user needs to login to allow the
      * download for a paid app.
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1;
 
@@ -700,19 +683,13 @@
      * <p> The installer can optionally provide a {@code userActionIntent} for a space-clearing
      * dialog. If no action is provided, then a generic intent
      * {@link android.os.storage.StorageManager#ACTION_MANAGE_STORAGE} is started instead.
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2;
 
     /**
      * The device is not connected to the internet
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3;
 
@@ -720,10 +697,7 @@
      * The installer responsible for the unarchival is disabled.
      *
      * <p> Should only be used by the system.
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4;
 
@@ -731,19 +705,13 @@
      * The installer responsible for the unarchival has been uninstalled
      *
      * <p> Should only be used by the system.
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5;
 
     /**
      * Generic error: The app cannot be unarchived.
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public static final int UNARCHIVAL_GENERIC_ERROR = 100;
 
@@ -2364,12 +2332,10 @@
      * @param statusReceiver Callback used to notify when the operation is completed.
      * @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
      *                                              available to the caller or isn't archived.
-     * @hide
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.DELETE_PACKAGES,
             Manifest.permission.REQUEST_DELETE_PACKAGES})
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
             throws PackageManager.NameNotFoundException {
@@ -2395,19 +2361,16 @@
      *
      * @param statusReceiver Callback used to notify whether the installer has accepted the
      *                       unarchival request or an error has occurred. The status update will be
-     *                       sent though {@link EXTRA_UNARCHIVE_STATUS}. Only one status will be
+     *                       sent though {@link #EXTRA_UNARCHIVE_STATUS}. Only one status will be
      *                       sent.
      * @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
      *                                              visible to the caller or if the package has no
      *                                              installer on the device anymore to unarchive it.
      * @throws IOException If parameters were unsatisfiable, such as lack of disk space.
-     *
-     * @hide
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.INSTALL_PACKAGES,
             Manifest.permission.REQUEST_INSTALL_PACKAGES})
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public void requestUnarchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
             throws IOException, PackageManager.NameNotFoundException {
@@ -2435,12 +2398,10 @@
      * @param userActionIntent     Optional intent to start a follow up action required to
      *                             facilitate the unarchival flow (e.g. user needs to log in).
      * @throws PackageManager.NameNotFoundException if no unarchival with {@code unarchiveId} exists
-     * @hide
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.INSTALL_PACKAGES,
             Manifest.permission.REQUEST_INSTALL_PACKAGES})
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public void reportUnarchivalStatus(int unarchiveId, @UnarchivalStatus int status,
             long requiredStorageBytes, @Nullable PendingIntent userActionIntent)
@@ -3454,11 +3415,8 @@
          * <p> The ID should be retrieved from the unarchive intent and passed into the
          * session that's being created to unarchive the app in question. Used to link the unarchive
          * intent and the install session to disambiguate.
-         *
-         * @hide
          */
         @FlaggedApi(Flags.FLAG_ARCHIVING)
-        @SystemApi
         public void setUnarchiveId(int unarchiveId) {
             this.unarchiveId = unarchiveId;
         }
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index c7091ad..70e6f98 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -177,12 +177,10 @@
     /**
      * Whether the package is currently in an archived state.
      *
-     * <p>Packages can be archived through {@link PackageArchiver} and do not have any APKs stored
-     * on the device, but do keep the data directory.
-     * @hide
+     * <p>Packages can be archived through {@link PackageInstaller#requestArchive} and do not have
+     * any APKs stored on the device, but do keep the data directory.
+     *
      */
-    // TODO(b/278553670) Unhide and update @links before launch.
-    @SystemApi
     @FlaggedApi(Flags.FLAG_ARCHIVING)
     public boolean isArchived;
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6775f9b..a22fe3f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1263,18 +1263,14 @@
 
     /**
      * Flag parameter to also retrieve some information about archived packages.
-     * Packages can be archived through
-     * {@link PackageInstaller#requestArchive(String, IntentSender)} and do not have any APKs stored
-     * on the device, but do keep the data directory.
+     * Packages can be archived through {@link PackageInstaller#requestArchive} and do not have any
+     * APKs stored on the device, but do keep the data directory.
      * <p> Note: Archived apps are a subset of apps returned by {@link #MATCH_UNINSTALLED_PACKAGES}.
      * <p> Note: this flag may cause less information about currently installed
      * applications to be returned.
      * <p> Note: use of this flag requires the android.permission.QUERY_ALL_PACKAGES
      * permission to see uninstalled packages.
-     * @hide
      */
-    // TODO(b/278553670) Unhide and update @links before launch.
-    @SystemApi
     @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
     public static final long MATCH_ARCHIVED_PACKAGES = 1L << 32;
 
@@ -8969,10 +8965,7 @@
      *
      * @throws NameNotFoundException if the given package name is not available to the caller.
      * @see PackageInstaller#requestArchive(String, IntentSender)
-     *
-     * @hide
      */
-    @SystemApi
     @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
     public boolean isAppArchivable(@NonNull String packageName) throws NameNotFoundException {
         throw new UnsupportedOperationException("isAppArchivable not implemented");
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index a565f68..d21b818 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -94,3 +94,10 @@
     description: "Feature flag to read install related information from an APK."
     bug: "275658500"
 }
+
+flag {
+    name: "use_pia_v2"
+    namespace: "package_manager_service"
+    description: "Feature flag to enable the refactored Package Installer app with updated UI."
+    bug: "182205982"
+}
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index 1b8eb07..40592a1 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -15,3 +15,12 @@
     description: "Feature flag for passing in an AssetFileDescriptor to create an frro"
     bug: "304478666"
 }
+
+flag {
+    name: "manifest_flagging"
+    namespace: "resource_manager"
+    description: "Feature flag for flagging manifest entries"
+    bug: "297373084"
+    # This flag is read in PackageParser at boot time, and in aapt2 which is a build tool.
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index d786d9a..18c95bfb 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -66,6 +66,13 @@
             + ".extra.sensor";
 
     /**
+     * An extra containing the notification id that triggered the intent
+     * @hide
+     */
+    public static final String EXTRA_NOTIFICATION_ID = SensorPrivacyManager.class.getName()
+            + ".extra.notification_id";
+
+    /**
      * An extra indicating if all sensors are affected
      * @hide
      */
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index d4ce0eb..5cbb0bb 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -16,8 +16,6 @@
 
 package android.hardware.camera2.params;
 
-import static com.android.internal.R.string.hardware;
-
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index ed31002..eabe13b 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -123,6 +123,12 @@
 
     private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
 
+    private static final int[] UID_USAGE_TIME_PROCESS_STATES = {
+            BatteryConsumer.PROCESS_STATE_FOREGROUND,
+            BatteryConsumer.PROCESS_STATE_BACKGROUND,
+            BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE
+    };
+
     private final int mDischargePercentage;
     private final double mBatteryCapacityMah;
     private final long mStatsStartTimestampMs;
@@ -516,6 +522,22 @@
             proto.write(
                     BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS,
                     bgMs);
+            for (int processState : UID_USAGE_TIME_PROCESS_STATES) {
+                final long timeInStateMillis = consumer.getTimeInProcessStateMs(processState);
+                if (timeInStateMillis <= 0) {
+                    continue;
+                }
+                final long timeInStateToken = proto.start(
+                        BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_STATE);
+                proto.write(
+                        BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState.PROCESS_STATE,
+                        processState);
+                proto.write(
+                        BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState
+                                .TIME_IN_STATE_MILLIS,
+                        timeInStateMillis);
+                proto.end(timeInStateToken);
+            }
             proto.end(token);
 
             if (proto.getRawSize() >= maxRawSize) {
diff --git a/core/java/android/os/ISecurityStateManager.aidl b/core/java/android/os/ISecurityStateManager.aidl
new file mode 100644
index 0000000..8ae624d
--- /dev/null
+++ b/core/java/android/os/ISecurityStateManager.aidl
@@ -0,0 +1,26 @@
+/* //device/java/android/android/os/ISecurityStateManager.aidl
+**
+** Copyright 2023, 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.os;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+/** @hide */
+interface ISecurityStateManager {
+    Bundle getGlobalSecurityState();
+}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index da647e2..161951e 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -437,6 +438,7 @@
      * @see #getData()
      * @see #setData(Bundle)
      */
+    @Nullable
     public Bundle peekData() {
         return data;
     }
diff --git a/core/java/android/os/SecurityStateManager.java b/core/java/android/os/SecurityStateManager.java
new file mode 100644
index 0000000..4fa61e0
--- /dev/null
+++ b/core/java/android/os/SecurityStateManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.os;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * SecurityStateManager provides the functionality to query the security status of the system and
+ * platform components. For example, this includes the system and vendor security patch level.
+ */
+@FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+@SystemService(Context.SECURITY_STATE_SERVICE)
+public class SecurityStateManager {
+
+    /**
+     * The system SPL key returned as part of the {@code Bundle} from
+     * {@code getGlobalSecurityState}.
+     */
+    public static final String KEY_SYSTEM_SPL = "system_spl";
+
+    /**
+     * The vendor SPL key returned as part of the {@code Bundle} from
+     * {@code getGlobalSecurityState}.
+     */
+    public static final String KEY_VENDOR_SPL = "vendor_spl";
+
+    /**
+     * The kernel version key returned as part of the {@code Bundle} from
+     * {@code getGlobalSecurityState}.
+     */
+    public static final String KEY_KERNEL_VERSION = "kernel_version";
+
+    private final ISecurityStateManager mService;
+
+    /**
+     * @hide
+     */
+    public SecurityStateManager(ISecurityStateManager service) {
+        mService = requireNonNull(service, "missing ISecurityStateManager");
+    }
+
+    /**
+     * Returns the current global security state. Each key-value pair is a mapping of a component
+     * of the global security state to its current version/SPL (security patch level). For example,
+     * the {@code KEY_SYSTEM_SPL} key will map to the SPL of the system as defined in
+     * {@link android.os.Build.VERSION}. The bundle will also include mappings from WebView packages
+     * and packages listed under config {@code config_securityStatePackages} to their respective
+     * versions as defined in {@link android.content.pm.PackageInfo#versionName}.
+     *
+     * @return A {@code Bundle} that contains the global security state information as
+     * string-to-string key-value pairs.
+     */
+    @FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+    @NonNull
+    public Bundle getGlobalSecurityState() {
+        try {
+            return mService.getGlobalSecurityState();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index a78f221..c085f33 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -57,6 +57,13 @@
 }
 
 flag {
+    name: "security_state_service"
+    namespace: "dynamic_spl"
+    description: "Guards the Security State API."
+    bug: "302189431"
+}
+
+flag {
     name: "battery_saver_supported_check_api"
     namespace: "backstage_power"
     description: "Guards a new API in PowerManager to check if battery saver is supported or not."
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 6853892..78a12f7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1738,23 +1738,6 @@
         return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
     }
 
-    /** {@hide}
-     * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    @Deprecated
-    public static boolean isFileEncryptedNativeOnly() {
-        return isFileEncrypted();
-    }
-
-    /** {@hide}
-     * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
-     */
-    @Deprecated
-    public static boolean isFileEncryptedNativeOrEmulated() {
-        return isFileEncrypted();
-    }
-
     /** {@hide} */
     public static boolean hasAdoptable() {
         switch (SystemProperties.get(PROP_ADOPTABLE)) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ff6ec29..1a33b768 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2542,6 +2542,7 @@
      * ComponentName)} and only use this action to start an activity if they return {@code false}.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @FlaggedApi(android.credentials.flags.Flags.FLAG_NEW_SETTINGS_INTENTS)
     public static final String ACTION_CREDENTIAL_PROVIDER =
             "android.settings.CREDENTIAL_PROVIDER";
 
@@ -8845,6 +8846,24 @@
                 "reduce_bright_colors_persist_across_reboots";
 
         /**
+         * Setting that specifies whether Even Dimmer - a feature that allows the brightness
+         * slider to go below what the display can conventionally do, should be enabled.
+         *
+         * @hide
+         */
+        public static final String EVEN_DIMMER_ACTIVATED =
+                "even_dimmer_activated";
+
+        /**
+         * Setting that specifies which nits level Even Dimmer should allow the screen brightness
+         * to go down to.
+         *
+         * @hide
+         */
+        public static final String EVEN_DIMMER_MIN_NITS =
+                "even_dimmer_min_nits";
+
+        /**
          * List of the enabled print services.
          *
          * N and beyond uses {@link #DISABLED_PRINT_SERVICES}. But this might be used in an upgrade
@@ -14975,6 +14994,38 @@
         public static final String APP_OPS_CONSTANTS = "app_ops_constants";
 
         /**
+         * Device Idle (Doze) specific settings.
+         * This is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "inactive_to=60000,sensing_to=400000"
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * inactive_to                      (long)
+         * sensing_to                       (long)
+         * motion_inactive_to               (long)
+         * idle_after_inactive_to           (long)
+         * idle_pending_to                  (long)
+         * max_idle_pending_to              (long)
+         * idle_pending_factor              (float)
+         * quick_doze_delay_to              (long)
+         * idle_to                          (long)
+         * max_idle_to                      (long)
+         * idle_factor                      (float)
+         * min_time_to_alarm                (long)
+         * max_temp_app_whitelist_duration  (long)
+         * notification_whitelist_duration  (long)
+         * </pre>
+         *
+         * <p>
+         * Type: string
+         * @hide
+         * @see com.android.server.DeviceIdleController.Constants
+         */
+        public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
+
+        /**
          * Battery Saver specific settings
          * This is encoded as a key=value list, separated by commas. Ex:
          *
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 33a67ae..533d459 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -8,4 +8,4 @@
 per-file Confirmation*.java = file:/keystore/OWNERS
 per-file FileIntegrityManager.java = file:platform/system/security:/fsverity/OWNERS
 per-file IFileIntegrityService.aidl = file:platform/system/security:/fsverity/OWNERS
-per-file *.aconfig = victorhsieh@google.com
+per-file *.aconfig = victorhsieh@google.com,eranm@google.com
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 2a4cbaf..46ea158 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -15,7 +15,6 @@
  */
 package android.service.notification;
 
-import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.Notification;
 import android.os.Bundle;
@@ -26,6 +25,7 @@
 import android.system.OsConstants;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import java.nio.ByteBuffer;
@@ -75,10 +75,6 @@
                 }
                 // We only need read-only access to the shared memory region.
                 buffer = mRankingMapFd.mapReadOnly();
-                if (buffer == null) {
-                    mRankingMap = null;
-                    return;
-                }
                 byte[] payload = new byte[buffer.remaining()];
                 buffer.get(payload);
                 mapParcel.unmarshall(payload, 0, payload.length);
@@ -98,7 +94,7 @@
             } finally {
                 mapParcel.recycle();
                 if (buffer != null && mRankingMapFd != null) {
-                    mRankingMapFd.unmap(buffer);
+                    SharedMemory.unmap(buffer);
                     mRankingMapFd.close();
                 }
             }
@@ -210,6 +206,7 @@
                                     new NotificationListenerService.Ranking[0]
                             )
                     );
+            ByteBuffer buffer = null;
 
             try {
                 // Parcels the ranking map and measures its size.
@@ -217,13 +214,10 @@
                 int mapSize = mapParcel.dataSize();
 
                 // Creates a new SharedMemory object with enough space to hold the ranking map.
-                SharedMemory mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize);
-                if (mRankingMapFd == null) {
-                    return;
-                }
+                mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize);
 
                 // Gets a read/write buffer mapping the entire shared memory region.
-                final ByteBuffer buffer = mRankingMapFd.mapReadWrite();
+                buffer = mRankingMapFd.mapReadWrite();
                 // Puts the ranking map into the shared memory region buffer.
                 buffer.put(mapParcel.marshall(), 0, mapSize);
                 // Protects the region from being written to, by setting it to be read-only.
@@ -238,6 +232,12 @@
                 throw new RuntimeException(e);
             } finally {
                 mapParcel.recycle();
+                // To prevent memory leaks, we can close the ranking map fd here.
+                // Because a reference to this still exists
+                if (buffer != null && mRankingMapFd != null) {
+                    SharedMemory.unmap(buffer);
+                    mRankingMapFd.close();
+                }
             }
         } else {
             out.writeParcelable(mRankingMap, flags);
@@ -247,7 +247,7 @@
     /**
      * @hide
      */
-    public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
+    public static final @NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
             = new Parcelable.Creator<NotificationRankingUpdate>() {
         public NotificationRankingUpdate createFromParcel(Parcel parcel) {
             return new NotificationRankingUpdate(parcel);
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index e5ad85c..07367df 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -15,10 +15,12 @@
  */
 package android.service.notification;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.Flags;
 import android.app.RemoteInput;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -36,6 +38,7 @@
     private boolean mSeen;
     private boolean mExpanded;
     private boolean mDirectReplied;
+    private boolean mSmartReplied;
     private boolean mSnoozed;
     private boolean mViewedSettings;
     private boolean mInteracted;
@@ -125,6 +128,9 @@
         mSeen = in.readByte() != 0;
         mExpanded = in.readByte() != 0;
         mDirectReplied = in.readByte() != 0;
+        if (Flags.lifetimeExtensionRefactor()) {
+            mSmartReplied = in.readByte() != 0;
+        }
         mSnoozed = in.readByte() != 0;
         mViewedSettings = in.readByte() != 0;
         mInteracted = in.readByte() != 0;
@@ -137,6 +143,9 @@
         dest.writeByte((byte) (mSeen ? 1 : 0));
         dest.writeByte((byte) (mExpanded ? 1 : 0));
         dest.writeByte((byte) (mDirectReplied ? 1 : 0));
+        if (Flags.lifetimeExtensionRefactor()) {
+            dest.writeByte((byte) (mSmartReplied ? 1 : 0));
+        }
         dest.writeByte((byte) (mSnoozed ? 1 : 0));
         dest.writeByte((byte) (mViewedSettings ? 1 : 0));
         dest.writeByte((byte) (mInteracted ? 1 : 0));
@@ -210,6 +219,23 @@
     }
 
     /**
+     * Returns whether the user has replied to a notification that has a smart reply at least once.
+     */
+    @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+    public boolean hasSmartReplied() {
+        return mSmartReplied;
+    }
+
+    /**
+     * Records that the user has replied to a notification that has a smart reply at least once.
+     */
+    @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+    public void setSmartReplied() {
+        mSmartReplied = true;
+        mInteracted = true;
+    }
+
+    /**
      * Returns whether the user has snoozed this notification at least once.
      */
     public boolean hasSnoozed() {
@@ -286,6 +312,9 @@
         if (mSeen != that.mSeen) return false;
         if (mExpanded != that.mExpanded) return false;
         if (mDirectReplied != that.mDirectReplied) return false;
+        if (Flags.lifetimeExtensionRefactor()) {
+            if (mSmartReplied != that.mSmartReplied) return false;
+        }
         if (mSnoozed != that.mSnoozed) return false;
         if (mViewedSettings != that.mViewedSettings) return false;
         if (mInteracted != that.mInteracted) return false;
@@ -297,6 +326,9 @@
         int result = (mSeen ? 1 : 0);
         result = 31 * result + (mExpanded ? 1 : 0);
         result = 31 * result + (mDirectReplied ? 1 : 0);
+        if (Flags.lifetimeExtensionRefactor()) {
+            result = 31 * result + (mSmartReplied ? 1 : 0);
+        }
         result = 31 * result + (mSnoozed ? 1 : 0);
         result = 31 * result + (mViewedSettings ? 1 : 0);
         result = 31 * result + (mInteracted ? 1 : 0);
@@ -311,6 +343,9 @@
         sb.append("mSeen=").append(mSeen);
         sb.append(", mExpanded=").append(mExpanded);
         sb.append(", mDirectReplied=").append(mDirectReplied);
+        if (Flags.lifetimeExtensionRefactor()) {
+            sb.append(", mSmartReplied=").append(mSmartReplied);
+        }
         sb.append(", mSnoozed=").append(mSnoozed);
         sb.append(", mViewedSettings=").append(mViewedSettings);
         sb.append(", mInteracted=").append(mInteracted);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 90663c7..147c15b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -216,6 +216,14 @@
         default CompatibilityInfo.Translator getTranslator() {
             return null;
         }
+
+        /**
+         * Notifies when the state of running animation is changed. The state is either "running" or
+         * "idle".
+         *
+         * @param running {@code true} if there is any animation running; {@code false} otherwise.
+         */
+        default void notifyAnimationRunningStateChanged(boolean running) {}
     }
 
     private static final String TAG = "InsetsController";
@@ -749,6 +757,9 @@
                     final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner(
                             mFrame, state1, mToState, RESIZE_INTERPOLATOR,
                             ANIMATION_DURATION_RESIZE, mTypes, InsetsController.this);
+                    if (mRunningAnimations.isEmpty()) {
+                        mHost.notifyAnimationRunningStateChanged(true);
+                    }
                     mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType()));
                 }
             };
@@ -1382,6 +1393,9 @@
             }
         }
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_RUNNING);
+        if (mRunningAnimations.isEmpty()) {
+            mHost.notifyAnimationRunningStateChanged(true);
+        }
         mRunningAnimations.add(new RunningAnimation(runner, animationType));
         if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
                 + useInsetsAnimationThread);
@@ -1588,6 +1602,9 @@
                 break;
             }
         }
+        if (mRunningAnimations.isEmpty()) {
+            mHost.notifyAnimationRunningStateChanged(false);
+        }
         onAnimationStateChanged(removedTypes, false /* running */);
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7a6c292..ff1e831 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -816,6 +816,8 @@
     private long mFpsPrevTime = -1;
     private int mFpsNumFrames;
 
+    private boolean mInsetsAnimationRunning;
+
     /**
      * The resolved pointer icon type requested by this window.
      * A null value indicates the resolved pointer icon has not yet been calculated.
@@ -991,7 +993,7 @@
     // for idleness handling.
     private boolean mHasIdledMessage = false;
     // time for touch boost period.
-    private static final int FRAME_RATE_TOUCH_BOOST_TIME = 1500;
+    private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000;
     // time for checking idle status periodically.
     private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
     // time for revaluating the idle status before lowering the frame rate.
@@ -2179,6 +2181,10 @@
         }
     }
 
+    void notifyInsetsAnimationRunningStateChanged(boolean running) {
+        mInsetsAnimationRunning = running;
+    }
+
     @Override
     public void requestLayout() {
         if (!mHandlingLayoutInLayoutRequest) {
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index a2708ee..40730e8 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -279,6 +279,13 @@
         return null;
     }
 
+    @Override
+    public void notifyAnimationRunningStateChanged(boolean running) {
+        if (mViewRoot != null) {
+            mViewRoot.notifyInsetsAnimationRunningStateChanged(running);
+        }
+    }
+
     private boolean isVisibleToUser() {
         return mViewRoot.getHostVisibility() == View.VISIBLE;
     }
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index a11c6d0..a404bd6 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -54,7 +54,7 @@
      * @param displayId the logical display id.
      * @param scale magnification scale.
      */
-    void setScale(int displayId, float scale);
+    void setScaleForWindowMagnification(int displayId, float scale);
 
      /**
      * Disables window magnification on specified display with animation.
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index e177731..c6bd20c 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -79,4 +79,9 @@
      * Used by Settings to enable/disable multiprocess.
      */
     void enableMultiProcess(boolean enable);
+
+    /**
+     * Used by Settings to get the default WebView package.
+     */
+    WebViewProviderInfo getDefaultWebViewPackage();
 }
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index e10f7c8..478aeec 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -70,20 +70,4 @@
 
     /** Updates a state of camera compat control for stretched issues in the viewfinder. */
     void updateCameraCompatControlState(in WindowContainerToken task, int state);
-
-    /**
-     * Controls whether ignore orientation request logic in {@link
-     * com.android.server.wm.DisplayArea} is disabled at runtime and how to optionally map some
-     * requested orientations to others.
-     *
-     * @param isDisabled when {@code true}, the system always ignores the value of {@link
-     *                   com.android.server.wm.DisplayArea#getIgnoreOrientationRequest} and app
-     *                   requested orientation is respected.
-     * @param fromOrientations The orientations we want to map to the correspondent orientations
-     *                        in toOrientation.
-     * @param toOrientations The orientations we map to the ones in fromOrientations at the same
-     *                       index
-     */
-     void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled,
-            in int[] fromOrientations, in int[] toOrientations);
 }
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index 4e0f9a5..0ec9ffe 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -108,6 +108,18 @@
      */
     public static final int OP_TYPE_REORDER_TO_TOP_OF_TASK = 13;
 
+    /**
+     * Creates a decor surface in the parent Task of the TaskFragment. The created decor surface
+     * will be provided in {@link TaskFragmentTransaction#TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED}
+     * event callback.
+     */
+    public static final int OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE = 14;
+
+    /**
+     * Removes the decor surface in the parent Task of the TaskFragment.
+     */
+    public static final int OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE = 15;
+
     @IntDef(prefix = { "OP_TYPE_" }, value = {
             OP_TYPE_UNKNOWN,
             OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -124,6 +136,8 @@
             OP_TYPE_SET_ISOLATED_NAVIGATION,
             OP_TYPE_REORDER_TO_BOTTOM_OF_TASK,
             OP_TYPE_REORDER_TO_TOP_OF_TASK,
+            OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE,
+            OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface OperationType {}
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index e6eeca4..a77c234 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -22,6 +22,9 @@
 import android.content.res.Configuration;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+import java.util.Objects;
 
 /**
  * The information about the parent Task of a particular TaskFragment
@@ -37,12 +40,15 @@
 
     private final boolean mHasDirectActivity;
 
+    @Nullable private final SurfaceControl mDecorSurface;
+
     public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
-            boolean visible, boolean hasDirectActivity) {
+            boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) {
         mConfiguration.setTo(configuration);
         mDisplayId = displayId;
         mVisible = visible;
         mHasDirectActivity = hasDirectActivity;
+        mDecorSurface = decorSurface;
     }
 
     public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
@@ -50,6 +56,7 @@
         mDisplayId = info.mDisplayId;
         mVisible = info.mVisible;
         mHasDirectActivity = info.mHasDirectActivity;
+        mDecorSurface = info.mDecorSurface;
     }
 
     /** The {@link Configuration} of the parent Task */
@@ -92,7 +99,13 @@
             return false;
         }
         return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
-                && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity;
+                && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity
+                && mDecorSurface == that.mDecorSurface;
+    }
+
+    @Nullable
+    public SurfaceControl getDecorSurface() {
+        return mDecorSurface;
     }
 
     @WindowConfiguration.WindowingMode
@@ -107,6 +120,7 @@
                 + ", displayId=" + mDisplayId
                 + ", visible=" + mVisible
                 + ", hasDirectActivity=" + mHasDirectActivity
+                + ", decorSurface=" + mDecorSurface
                 + "}";
     }
 
@@ -128,7 +142,8 @@
         return mConfiguration.equals(that.mConfiguration)
                 && mDisplayId == that.mDisplayId
                 && mVisible == that.mVisible
-                && mHasDirectActivity == that.mHasDirectActivity;
+                && mHasDirectActivity == that.mHasDirectActivity
+                && mDecorSurface == that.mDecorSurface;
     }
 
     @Override
@@ -137,6 +152,7 @@
         result = 31 * result + mDisplayId;
         result = 31 * result + (mVisible ? 1 : 0);
         result = 31 * result + (mHasDirectActivity ? 1 : 0);
+        result = 31 * result + Objects.hashCode(mDecorSurface);
         return result;
     }
 
@@ -146,6 +162,7 @@
         dest.writeInt(mDisplayId);
         dest.writeBoolean(mVisible);
         dest.writeBoolean(mHasDirectActivity);
+        dest.writeTypedObject(mDecorSurface, flags);
     }
 
     private TaskFragmentParentInfo(Parcel in) {
@@ -153,6 +170,7 @@
         mDisplayId = in.readInt();
         mVisible = in.readBoolean();
         mHasDirectActivity = in.readBoolean();
+        mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR);
     }
 
     public static final Creator<TaskFragmentParentInfo> CREATOR =
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index cd1275c..6d36b57 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -266,31 +266,6 @@
     }
 
     /**
-     * Controls whether ignore orientation request logic in {@link
-     * com.android.server.wm.DisplayArea} is disabled at runtime and how to optionally map some
-     * requested orientation to others.
-     *
-     * @param isIgnoreOrientationRequestDisabled when {@code true}, the system always ignores the
-     *           value of  {@link com.android.server.wm.DisplayArea#getIgnoreOrientationRequest}
-     *           and app requested orientation is respected.
-     * @param fromOrientations The orientations we want to map to the correspondent orientations
-     *                        in toOrientation.
-     * @param toOrientations The orientations we map to the ones in fromOrientations at the same
-     *                       index
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
-    public void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled,
-            @Nullable int[] fromOrientations, @Nullable int[] toOrientations) {
-        try {
-            mTaskOrganizerController.setOrientationRequestPolicy(isIgnoreOrientationRequestDisabled,
-                    fromOrientations, toOrientations);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Gets the executor to run callbacks on.
      * @hide
      */
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
index f5fe12e..e55c641 100644
--- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
+++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
@@ -37,22 +37,11 @@
      * @return The highest refresh rate
      */
     public static float findHighestRefreshRateForDefaultDisplay(Context context) {
-        return findHighestRefreshRate(context, Display.DEFAULT_DISPLAY);
-    }
-
-    /**
-     * Find the highest refresh rate among all the modes of the specified display.
-     *
-     * @param context The context
-     * @param displayId The display ID
-     * @return The highest refresh rate
-     */
-    public static float findHighestRefreshRate(Context context, int displayId) {
         final DisplayManager dm = context.getSystemService(DisplayManager.class);
-        final Display display = dm.getDisplay(displayId);
+        final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
 
         if (display == null) {
-            Log.w(TAG, "No valid display device with ID = " + displayId);
+            Log.w(TAG, "No valid default display device");
             return DEFAULT_REFRESH_RATE;
         }
 
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 4d6ed80..3887dd7 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -268,6 +268,13 @@
 
     optional SettingProto enhanced_voice_privacy_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    message EvenDimmer {
+        optional SettingProto even_dimmer_activated = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto even_dimmer_min_nits = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+
+    optional EvenDimmer even_dimmer = 98;
+
     optional SettingProto font_weight_adjustment = 85 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message Gesture {
@@ -712,5 +719,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 98;
+    // Next tag = 99;
 }
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index d5cf60d..c242c9c 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -110,6 +110,7 @@
   optional int32 task_root_package_token = 11;
   optional int32 task_root_class_token = 12;
   optional int32 locus_id_token = 13;
+  optional ObfuscatedUserInteractionExtrasProto interaction_extras = 14;
 }
 
 /**
@@ -129,6 +130,7 @@
   optional string task_root_package = 11;
   optional string task_root_class = 12;
   optional string locus_id = 13 [(.android.privacy).dest = DEST_EXPLICIT];
+  optional bytes extras = 14;
 }
 
 /**
@@ -145,3 +147,11 @@
   // Stores the mappings for every package
   repeated PackagesMap packages_map = 2;
 }
+
+/**
+ * Store the relevant information from extra details for user interaction event.
+ */
+message ObfuscatedUserInteractionExtrasProto {
+  optional int32 category_token = 1;
+  optional int32 action_token = 2;
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 98897d8..1d4e01a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1522,6 +1522,10 @@
     config_screenBrightnessSettingDefaultFloat instead -->
     <integer name="config_screenBrightnessSettingDefault">102</integer>
 
+    <!-- Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+    The value must be in the range [0, 255]. -->
+    <integer name="config_screenBrightnessCapForWearBedtimeMode">20</integer>
+
     <!-- Minimum screen brightness setting allowed by power manager.
          -2 is invalid so setting will resort to int value specified above.
          Set this to 0.0 to allow screen to go to minimal brightness.
@@ -4989,6 +4993,11 @@
     <!-- Component name for the default module metadata provider on this device -->
     <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
 
+    <!-- Packages that contain a security state.
+         {@link SecurityStateManager#getGlobalSecurityState} will read and report the state/version
+          of these packages. -->
+    <string-array name="config_securityStatePackages" translatable="false" />
+
     <!-- Package name for the default Health Connect app.
          OEMs can set this with their own health app package name to define a default app with high
          priority for the app to store the health data. If set the app always has priority of 1
diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml
index bc9ca3d..7a707c0 100644
--- a/core/res/res/values/config_device_idle.xml
+++ b/core/res/res/values/config_device_idle.xml
@@ -28,7 +28,7 @@
     <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">240000</integer>
+    <integer name="device_idle_light_after_inactive_to_ms">60000</integer>
 
     <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_TIMEOUT -->
     <integer name="device_idle_light_idle_to_ms">300000</integer>
@@ -43,7 +43,7 @@
     <item name="device_idle_light_idle_factor" format="float" type="integer">2.0</item>
 
     <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_INCREASE_LINEARLY -->
-    <bool name="device_idle_light_idle_increase_linearly">false</bool>
+    <bool name="device_idle_light_idle_increase_linearly">true</bool>
 
     <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS -->
     <integer name="device_idle_light_idle_linear_increase_factor_ms">300000</integer>
@@ -52,7 +52,7 @@
     <integer name="device_idle_light_idle_flex_linear_increase_factor_ms">60000</integer>
 
     <!-- Default for DeviceIdleController.Constants.LIGHT_MAX_IDLE_TIMEOUT -->
-    <integer name="device_idle_light_max_idle_to_ms">900000</integer>
+    <integer name="device_idle_light_max_idle_to_ms">1800000</integer>
 
     <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET -->
     <integer name="device_idle_light_idle_maintenance_min_budget_ms">60000</integer>
@@ -67,13 +67,13 @@
     <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>
+    <integer name="device_idle_inactive_to_ms">60000</integer>
 
     <!-- Default for DeviceIdleController.Constants.SENSING_TIMEOUT -->
-    <integer name="device_idle_sensing_to_ms">240000</integer>
+    <integer name="device_idle_sensing_to_ms">30000</integer>
 
     <!-- Default for DeviceIdleController.Constants.LOCATING_TIMEOUT -->
-    <integer name="device_idle_locating_to_ms">30000</integer>
+    <integer name="device_idle_locating_to_ms">15000</integer>
 
     <!-- Default for DeviceIdleController.Constants.LOCATION_ACCURACY -->
     <item name="device_idle_location_accuracy" format="float" type="integer">20.0</item>
@@ -85,7 +85,7 @@
     <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>
+    <integer name="device_idle_idle_after_inactive_to_ms">60000</integer>
 
     <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_TIMEOUT -->
     <integer name="device_idle_idle_pending_to_ms">300000</integer>
@@ -100,7 +100,7 @@
     <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>
+    <integer name="device_idle_idle_to_ms">900000</integer>
 
     <!-- Default for DeviceIdleController.Constants.MAX_IDLE_TIMEOUT -->
     <integer name="device_idle_max_idle_to_ms">21600000</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 017688a..4b0fa4b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1274,6 +1274,7 @@
   <java-symbol type="array" name="policy_exempt_apps" />
   <java-symbol type="array" name="vendor_policy_exempt_apps" />
   <java-symbol type="array" name="cloneable_apps" />
+  <java-symbol type="array" name="config_securityStatePackages" />
 
   <java-symbol type="drawable" name="default_wallpaper" />
   <java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -2078,6 +2079,7 @@
   <java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
   <java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
   <java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
+  <java-symbol type="integer" name="config_screenBrightnessCapForWearBedtimeMode" />
   <java-symbol type="dimen" name="config_screenBrightnessSettingMinimumFloat" />
   <java-symbol type="dimen" name="config_screenBrightnessSettingMaximumFloat" />
   <java-symbol type="dimen" name="config_screenBrightnessSettingDefaultFloat" />
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index aaaa3c7..ac1f7d0 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -48,6 +48,11 @@
     private static final int UID_1 = 2000;
     private static final int UID_2 = 3000;
     private static final int UID_3 = 4000;
+    private static final int[] UID_USAGE_TIME_PROCESS_STATES = {
+            BatteryConsumer.PROCESS_STATE_FOREGROUND,
+            BatteryConsumer.PROCESS_STATE_BACKGROUND,
+            BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE
+    };
 
     @Test
     public void testGetStatsProto() {
@@ -195,6 +200,20 @@
         assertEquals("For uid " + uid,
                 uidConsumer.getTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND),
                 uidConsumerProto.timeInBackgroundMillis);
+        for (int processState : UID_USAGE_TIME_PROCESS_STATES) {
+            final long timeInStateMillis = uidConsumer.getTimeInProcessStateMs(processState);
+            if (timeInStateMillis <= 0) {
+                continue;
+            }
+            assertEquals("For uid " + uid + ", process state " + processState,
+                    timeInStateMillis,
+                    Arrays.stream(uidConsumerProto.timeInState)
+                            .filter(timeInState -> timeInState.processState == processState)
+                            .findFirst()
+                            .orElseThrow()
+                            .timeInStateMillis);
+        }
+
         if (expectNullBatteryConsumerData) {
             assertNull("For uid " + uid, uidConsumerProto.batteryConsumerData);
         } else {
@@ -250,8 +269,8 @@
         final UidBatteryConsumer.Builder uidBuilder = builder
                 .getOrCreateUidBatteryConsumerBuilder(UID_0)
                 .setPackageWithHighestDrain("myPackage0")
-                .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
-                .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000)
+                .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000)
+                .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 2000)
                 .setConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
                 .setConsumedPower(
@@ -285,7 +304,7 @@
 
         builder.getOrCreateUidBatteryConsumerBuilder(UID_1)
                 .setPackageWithHighestDrain("myPackage1")
-                .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1234);
+                .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1234);
 
         builder.getOrCreateUidBatteryConsumerBuilder(UID_2)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
@@ -331,8 +350,10 @@
         // significantly larger than 50 Kb
         for (int i = 0; i < 3000; i++) {
             builder.getOrCreateUidBatteryConsumerBuilder(i)
-                    .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1 * 60 * 1000)
-                    .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 2 * 60 * 1000)
+                    .setTimeInProcessStateMs(
+                            BatteryConsumer.PROCESS_STATE_FOREGROUND, 1 * 60 * 1000)
+                    .setTimeInProcessStateMs(
+                            BatteryConsumer.PROCESS_STATE_BACKGROUND, 2 * 60 * 1000)
                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 30)
                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 40);
         }
@@ -340,16 +361,16 @@
         // Add a UID with much larger battery footprint
         final int largeConsumerUid = 3001;
         builder.getOrCreateUidBatteryConsumerBuilder(largeConsumerUid)
-                .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 10 * 60 * 1000)
-                .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 20 * 60 * 1000)
+                .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 10 * 60 * 1000)
+                .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 20 * 60 * 1000)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
 
         // Add a UID with much larger usage duration
         final int highUsageUid = 3002;
         builder.getOrCreateUidBatteryConsumerBuilder(highUsageUid)
-                .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 60 * 60 * 1000)
-                .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 120 * 60 * 1000)
+                .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 60 * 60 * 1000)
+                .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 120 * 60 * 1000)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 3)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 4);
 
diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
index 5a202c5..9dce899 100644
--- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
@@ -28,6 +28,7 @@
 import android.app.usage.UsageEvents.Event;
 import android.content.res.Configuration;
 import android.os.Parcel;
+import android.os.PersistableBundle;
 import android.test.suitebuilder.annotation.LargeTest;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -140,6 +141,12 @@
             case Event.LOCUS_ID_SET:
                 event.mLocusId = anyString();
                 break;
+            case Event.USER_INTERACTION:
+                PersistableBundle extras = new PersistableBundle();
+                extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, anyString());
+                extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, anyString());
+                event.mExtras = extras;
+                break;
         }
 
         event.mFlags = anyInt();
@@ -176,6 +183,14 @@
             case Event.LOCUS_ID_SET:
                 assertEquals(ue1.mLocusId, ue2.mLocusId);
                 break;
+            case Event.USER_INTERACTION:
+                final PersistableBundle extras1 = ue1.getExtras();
+                final PersistableBundle extras2 = ue2.getExtras();
+                assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+                        extras2.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY));
+                assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+                        extras2.getString(UsageStatsManager.EXTRA_EVENT_ACTION));
+                break;
         }
 
         assertEquals(ue1.mFlags, ue2.mFlags);
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 083e37a..fae7148 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -72,7 +72,7 @@
             "mShortcutId", "mShortcutIdToken", "mBucketAndReason", "mInstanceId",
             "mNotificationChannelId", "mNotificationChannelIdToken", "mTaskRootPackage",
             "mTaskRootPackageToken", "mTaskRootClass", "mTaskRootClassToken", "mLocusId",
-            "mLocusIdToken"};
+            "mLocusIdToken", "mExtras", "mUserInteractionExtrasToken"};
     // All fields in this list are defined in UsageEvents.Event but not persisted
     private static final String[] USAGEEVENTS_IGNORED_FIELDS = {"mAction", "mContentAnnotations",
             "mContentType", "DEVICE_EVENT_PACKAGE_NAME", "FLAG_IS_PACKAGE_INSTANT_APP",
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 87c167c..4a9cb71 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -83,6 +83,12 @@
             }
 
             @Override
+            protected AssetManager createAssetManager(@NonNull final ResourcesKey key,
+                    ResourcesManager.ApkAssetsSupplier apkSupplier) {
+                return createAssetManager(key);
+            }
+
+            @Override
             protected DisplayMetrics getDisplayMetrics(int displayId, DisplayAdjustments daj) {
                 return mDisplayMetricsMap.get(displayId);
             }
@@ -100,7 +106,7 @@
                 null, APP_ONE_RES_DIR, null, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(newResources);
-        assertSame(resources, newResources);
+        assertSame(resources.getImpl(), newResources.getImpl());
     }
 
     @SmallTest
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index 0855268..1bdb006 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -472,6 +472,9 @@
         NotificationRankingUpdate nru = generateUpdate(getContext());
         Parcel parcel = Parcel.obtain();
         nru.writeToParcel(parcel, 0);
+        if (Flags.rankingUpdateAshmem()) {
+            assertTrue(nru.isFdNotNullAndClosed());
+        }
         parcel.setDataPosition(0);
         NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel);
         // The rankingUpdate file descriptor is only non-null in the new path.
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 1048742..e7e1740 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -500,7 +500,7 @@
 
 key ESCAPE {
     base:                               none
-    alt, meta:                          fallback HOME
+    alt:                                fallback HOME
     ctrl:                               fallback MENU
 }
 
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 5e16bce..dd703f5 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -33,7 +33,6 @@
 import android.util.Log;
 
 import java.util.Calendar;
-import java.util.Objects;
 
 /**
  * @hide This should not be made public in its present form because it
@@ -139,13 +138,25 @@
         return new KeyStore2();
     }
 
+    /**
+     * Gets the {@link IKeystoreService} that should be started in early_hal in Android.
+     *
+     * @throws IllegalStateException if the KeystoreService is not available or has not
+     * been initialized when called. This is a state that should not happen and indicates
+     * and error somewhere in the stack or with the calling processes access permissions.
+     */
     @NonNull private synchronized IKeystoreService getService(boolean retryLookup) {
         if (mBinder == null || retryLookup) {
             mBinder = IKeystoreService.Stub.asInterface(ServiceManager
-                    .getService(KEYSTORE2_SERVICE_NAME));
-            Binder.allowBlocking(mBinder.asBinder());
+                .getService(KEYSTORE2_SERVICE_NAME));
         }
-        return Objects.requireNonNull(mBinder);
+        if (mBinder == null) {
+            throw new IllegalStateException(
+                    "Could not connect to Keystore service. Keystore may have crashed or not been"
+                            + " initialized");
+        }
+        Binder.allowBlocking(mBinder.asBinder());
+        return mBinder;
     }
 
     void delete(KeyDescriptor descriptor) throws KeyStoreException {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 50cfd94..4c2433f 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -443,7 +443,8 @@
         assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer);
 
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                null /* decorSurface */));
 
         mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
 
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 02031a6..8c274a2 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1139,7 +1139,8 @@
     public void testOnTransactionReady_taskFragmentParentInfoChanged() {
         final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
         final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */);
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                null /* decorSurface */);
         transaction.addChange(new TaskFragmentTransaction.Change(
                 TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
                 .setTaskId(TASK_ID)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index e56c8ab..7b77235 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -79,14 +79,16 @@
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                null /* decorSurface */));
 
         assertEquals(WINDOWING_MODE_MULTI_WINDOW,
                 taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                null /* decorSurface */));
 
         assertEquals(WINDOWING_MODE_FREEFORM,
                 taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
@@ -106,13 +108,15 @@
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                null /* decorSurface */));
 
         assertFalse(taskContainer.isInPictureInPicture());
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                null /* decorSurface */));
 
         assertTrue(taskContainer.isInPictureInPicture());
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index f5b877a..a3eb429 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -412,6 +412,23 @@
         setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
     }
 
+
+    /** Updates the width of the task view if it changed. */
+    void updateTaskViewContentWidth() {
+        if (mTaskView != null) {
+            int width = getContentWidth();
+            if (mTaskView.getWidth() != width) {
+                FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(width, MATCH_PARENT);
+                mTaskView.setLayoutParams(lp);
+            }
+        }
+    }
+
+    private int getContentWidth() {
+        boolean isStackOnLeft = mPositioner.isStackOnLeft(mStackView.getStackPosition());
+        return mPositioner.getTaskViewContentWidth(isStackOnLeft);
+    }
+
     /**
      * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need
      * to be called after view inflate.
@@ -438,7 +455,12 @@
                     mController.getTaskViewTransitions(), mController.getSyncTransactionQueue());
             mTaskView = new TaskView(mContext, mTaskViewTaskController);
             mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
-            mExpandedViewContainer.addView(mTaskView);
+
+            // set a fixed width so it is not recalculated as part of a rotation. the width will be
+            // updated manually after the rotation.
+            FrameLayout.LayoutParams lp =
+                    new FrameLayout.LayoutParams(getContentWidth(), MATCH_PARENT);
+            mExpandedViewContainer.addView(mTaskView, lp);
             bringChildToFront(mTaskView);
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 1efd9df..baa52a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -375,6 +375,13 @@
         }
     }
 
+    /** Returns the width of the task view content. */
+    public int getTaskViewContentWidth(boolean onLeft) {
+        int[] paddings = getExpandedViewContainerPadding(onLeft, /* isOverflow = */ false);
+        int pointerOffset = showBubblesVertically() ? getPointerSize() : 0;
+        return mPositionRect.width() - paddings[0] - paddings[2] - pointerOffset;
+    }
+
     /** Gets the y position of the expanded view if it was top-aligned. */
     public float getExpandedViewYTopAligned() {
         final int top = getAvailableRect().top;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 8f904c4..91a8ce7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -537,8 +537,8 @@
                 return;
             }
 
-            final boolean clickedBubbleIsCurrentlyExpandedBubble =
-                    clickedBubble.getKey().equals(mExpandedBubble.getKey());
+            final boolean clickedBubbleIsCurrentlyExpandedBubble = mExpandedBubble != null
+                            && clickedBubble.getKey().equals(mExpandedBubble.getKey());
 
             if (isExpanded()) {
                 mExpandedAnimationController.onGestureFinished();
@@ -3288,6 +3288,7 @@
             mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
                     mPositioner.showBubblesVertically() ? p.y : p.x));
             mExpandedViewContainer.setTranslationX(0f);
+            mExpandedBubble.getExpandedView().updateTaskViewContentWidth();
             mExpandedBubble.getExpandedView().updateView(
                     mExpandedViewContainer.getLocationOnScreen());
             updatePointerPosition(false /* forIme */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 271a3b2..79c2076 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -590,7 +590,7 @@
                 cancel("transit_sleep");
                 return;
             }
-            if (mKeyguardLocked) {
+            if (mKeyguardLocked || (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                         "[%d] RecentsController.merge: keyguard is locked", mInstanceId);
                 // We will not accept new changes if we are swiping over the keyguard.
@@ -627,7 +627,8 @@
                         && mRecentsTask.equals(change.getContainer());
                 hasTaskChange = hasTaskChange || isRootTask;
                 final boolean isLeafTask = leafTaskFilter.test(change);
-                if (TransitionUtil.isOpeningType(change.getMode())) {
+                if (TransitionUtil.isOpeningType(change.getMode())
+                        || TransitionUtil.isOrderOnly(change)) {
                     if (isRecentsTask) {
                         recentsOpening = change;
                     } else if (isRootTask || isLeafTask) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 8cbcde3..53ec201 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -49,7 +49,6 @@
 import android.view.ViewConfiguration;
 import android.view.WindowManagerGlobal;
 
-import com.android.internal.view.BaseIWindow;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 
@@ -70,7 +69,9 @@
     private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
 
     private final int mDisplayId;
-    private final BaseIWindow mFakeWindow;
+
+    private final IBinder mClientToken;
+
     private final IBinder mFocusGrantToken;
     private final SurfaceControl mDecorationSurface;
     private final InputChannel mInputChannel;
@@ -78,7 +79,7 @@
     private final DragPositioningCallback mCallback;
 
     private final SurfaceControl mInputSinkSurface;
-    private final BaseIWindow mFakeSinkWindow;
+    private final IBinder mSinkClientToken;
     private final InputChannel mSinkInputChannel;
     private final DisplayController mDisplayController;
 
@@ -116,17 +117,14 @@
         mTaskCornerRadius = taskCornerRadius;
         mDecorationSurface = decorationSurface;
         mDisplayController = displayController;
-        // Use a fake window as the backing surface is a container layer, and we don't want to
-        // create a buffer layer for it, so we can't use ViewRootImpl.
-        mFakeWindow = new BaseIWindow();
-        mFakeWindow.setSession(mWindowSession);
+        mClientToken = new Binder();
         mFocusGrantToken = new Binder();
         mInputChannel = new InputChannel();
         try {
             mWindowSession.grantInputChannel(
                     mDisplayId,
                     mDecorationSurface,
-                    mFakeWindow.asBinder(),
+                    mClientToken,
                     null /* hostInputToken */,
                     FLAG_NOT_FOCUSABLE,
                     PRIVATE_FLAG_TRUSTED_OVERLAY,
@@ -155,13 +153,13 @@
                 .setLayer(mInputSinkSurface, WindowDecoration.INPUT_SINK_Z_ORDER)
                 .show(mInputSinkSurface)
                 .apply();
-        mFakeSinkWindow = new BaseIWindow();
+        mSinkClientToken = new Binder();
         mSinkInputChannel = new InputChannel();
         try {
             mWindowSession.grantInputChannel(
                     mDisplayId,
                     mInputSinkSurface,
-                    mFakeSinkWindow.asBinder(),
+                    mSinkClientToken,
                     null /* hostInputToken */,
                     FLAG_NOT_FOCUSABLE,
                     0 /* privateFlags */,
@@ -324,14 +322,14 @@
         mInputEventReceiver.dispose();
         mInputChannel.dispose();
         try {
-            mWindowSession.remove(mFakeWindow.asBinder());
+            mWindowSession.remove(mClientToken);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
 
         mSinkInputChannel.dispose();
         try {
-            mWindowSession.remove(mFakeSinkWindow.asBinder());
+            mWindowSession.remove(mSinkClientToken);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index 386983c..b9b56c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -132,5 +132,6 @@
 
 csuite_test {
     name: "csuite-1p3p-pip-flickers",
+    test_plan_include: "csuitePlan.xml",
     test_config_template: "csuiteDefaultTemplate.xml",
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index 6429b00..f5a8655 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -45,12 +45,15 @@
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="run-command" value="settings put global package_verifier_user_consent -1"/>
         <option name="teardown-command"
                 value="settings delete secure show_ime_with_hard_keyboard"/>
         <option name="teardown-command" value="settings delete system show_touches"/>
         <option name="teardown-command" value="settings delete system pointer_location"/>
         <option name="teardown-command"
                 value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+        <option name="teardown-command"
+                value="settings put global package_verifier_user_consent 1"/>
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuitePlan.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuitePlan.xml
new file mode 100644
index 0000000..a2fc6b4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuitePlan.xml
@@ -0,0 +1,3 @@
+<configuration description="Flicker tests C-Suite Crawler Test Plan">
+  <target_preparer class="com.android.csuite.core.AppCrawlTesterHostPreparer"/>
+</configuration>
\ No newline at end of file
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 47a7f35..2f28363 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -63,15 +63,21 @@
         "AssetsProvider.cpp",
         "AttributeResolution.cpp",
         "BigBuffer.cpp",
+        "BigBufferStream.cpp",
         "ChunkIterator.cpp",
         "ConfigDescription.cpp",
+        "FileStream.cpp",
         "Idmap.cpp",
         "LoadedArsc.cpp",
         "Locale.cpp",
         "LocaleData.cpp",
         "misc.cpp",
+        "NinePatch.cpp",
         "ObbFile.cpp",
         "PosixUtils.cpp",
+        "Png.cpp",
+        "PngChunkFilter.cpp",
+        "PngCrunch.cpp",
         "ResourceTimer.cpp",
         "ResourceTypes.cpp",
         "ResourceUtils.cpp",
@@ -84,7 +90,10 @@
     ],
     export_include_dirs: ["include"],
     export_shared_lib_headers: ["libz"],
-    static_libs: ["libincfs-utils"],
+    static_libs: [
+        "libincfs-utils",
+        "libpng",
+    ],
     whole_static_libs: [
         "libandroidfw_pathutils",
         "libincfs-utils",
@@ -198,9 +207,11 @@
         "tests/ConfigDescription_test.cpp",
         "tests/ConfigLocale_test.cpp",
         "tests/DynamicRefTable_test.cpp",
+        "tests/FileStream_test.cpp",
         "tests/Idmap_test.cpp",
         "tests/LoadedArsc_test.cpp",
         "tests/Locale_test.cpp",
+        "tests/NinePatch_test.cpp",
         "tests/ResourceTimer_test.cpp",
         "tests/ResourceUtils_test.cpp",
         "tests/ResTable_test.cpp",
diff --git a/tools/aapt2/io/BigBufferStream.cpp b/libs/androidfw/BigBufferStream.cpp
similarity index 69%
rename from tools/aapt2/io/BigBufferStream.cpp
rename to libs/androidfw/BigBufferStream.cpp
index 9704caa..f18199c 100644
--- a/tools/aapt2/io/BigBufferStream.cpp
+++ b/libs/androidfw/BigBufferStream.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include "io/BigBufferStream.h"
+#include "androidfw/BigBufferStream.h"
 
-namespace aapt {
-namespace io {
+#include <algorithm>
+
+namespace android {
 
 //
 // BigBufferInputStream
@@ -76,6 +77,34 @@
   return buffer_->size();
 }
 
+bool BigBufferInputStream::ReadFullyAtOffset(void* data, size_t byte_count, off64_t offset) {
+  if (byte_count == 0) {
+    return true;
+  }
+  if (offset < 0) {
+    return false;
+  }
+  if (offset > std::numeric_limits<off64_t>::max() - byte_count) {
+    return false;
+  }
+  if (offset + byte_count > buffer_->size()) {
+    return false;
+  }
+  auto p = reinterpret_cast<uint8_t*>(data);
+  for (auto iter = buffer_->begin(); iter != buffer_->end() && byte_count > 0; ++iter) {
+    if (offset < iter->size) {
+      size_t to_read = std::min(byte_count, (size_t)(iter->size - offset));
+      memcpy(p, iter->buffer.get() + offset, to_read);
+      byte_count -= to_read;
+      p += to_read;
+      offset = 0;
+    } else {
+      offset -= iter->size;
+    }
+  }
+  return byte_count == 0;
+}
+
 //
 // BigBufferOutputStream
 //
@@ -97,5 +126,4 @@
   return false;
 }
 
-}  // namespace io
-}  // namespace aapt
+}  // namespace android
diff --git a/tools/aapt2/io/FileStream.cpp b/libs/androidfw/FileStream.cpp
similarity index 95%
rename from tools/aapt2/io/FileStream.cpp
rename to libs/androidfw/FileStream.cpp
index 27529bc..b86c9cb 100644
--- a/tools/aapt2/io/FileStream.cpp
+++ b/libs/androidfw/FileStream.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "io/FileStream.h"
+#include "androidfw/FileStream.h"
 
 #include <errno.h>   // for errno
 #include <fcntl.h>   // for O_RDONLY
@@ -34,8 +34,7 @@
 using ::android::base::SystemErrorCodeToString;
 using ::android::base::unique_fd;
 
-namespace aapt {
-namespace io {
+namespace android {
 
 FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity)
     : buffer_capacity_(buffer_capacity) {
@@ -108,6 +107,10 @@
   return error_;
 }
 
+bool FileInputStream::ReadFullyAtOffset(void* data, size_t byte_count, off64_t offset) {
+  return base::ReadFullyAtOffset(fd_, data, byte_count, offset);
+}
+
 FileOutputStream::FileOutputStream(const std::string& path, size_t buffer_capacity)
     : buffer_capacity_(buffer_capacity) {
   int mode = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;
@@ -199,5 +202,4 @@
   return error_;
 }
 
-}  // namespace io
-}  // namespace aapt
+}  // namespace android
diff --git a/tools/aapt2/compile/NinePatch.cpp b/libs/androidfw/NinePatch.cpp
similarity index 80%
rename from tools/aapt2/compile/NinePatch.cpp
rename to libs/androidfw/NinePatch.cpp
index 4538ecc..1fdbebf 100644
--- a/tools/aapt2/compile/NinePatch.cpp
+++ b/libs/androidfw/NinePatch.cpp
@@ -14,20 +14,17 @@
  * limitations under the License.
  */
 
-#include "compile/Image.h"
-
 #include <sstream>
 #include <string>
 #include <vector>
 
+#include "androidfw/Image.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/StringPiece.h"
 
-#include "util/Util.h"
-
 using android::StringPiece;
 
-namespace aapt {
+namespace android {
 
 // Colors in the format 0xAARRGGBB (the way 9-patch expects it).
 constexpr static const uint32_t kColorOpaqueWhite = 0xffffffffu;
@@ -90,10 +87,8 @@
 // };
 //
 template <typename ImageLine>
-static bool FillRanges(const ImageLine* image_line,
-                       const ColorValidator* color_validator,
-                       std::vector<Range>* primary_ranges,
-                       std::vector<Range>* secondary_ranges,
+static bool FillRanges(const ImageLine* image_line, const ColorValidator* color_validator,
+                       std::vector<Range>* primary_ranges, std::vector<Range>* secondary_ranges,
                        std::string* out_err) {
   const int32_t length = image_line->GetLength();
 
@@ -133,11 +128,13 @@
  */
 class HorizontalImageLine {
  public:
-  explicit HorizontalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
-                               int32_t length)
-      : rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {}
+  explicit HorizontalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset, int32_t length)
+      : rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {
+  }
 
-  inline int32_t GetLength() const { return length_; }
+  inline int32_t GetLength() const {
+    return length_;
+  }
 
   inline uint32_t GetColor(int32_t idx) const {
     return NinePatch::PackRGBA(rows_[yoffset_] + (idx + xoffset_) * 4);
@@ -156,11 +153,13 @@
  */
 class VerticalImageLine {
  public:
-  explicit VerticalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
-                             int32_t length)
-      : rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {}
+  explicit VerticalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset, int32_t length)
+      : rows_(rows), xoffset_(xoffset), yoffset_(yoffset), length_(length) {
+  }
 
-  inline int32_t GetLength() const { return length_; }
+  inline int32_t GetLength() const {
+    return length_;
+  }
 
   inline uint32_t GetColor(int32_t idx) const {
     return NinePatch::PackRGBA(rows_[yoffset_ + idx] + (xoffset_ * 4));
@@ -175,20 +174,22 @@
 
 class DiagonalImageLine {
  public:
-  explicit DiagonalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset,
-                             int32_t xstep, int32_t ystep, int32_t length)
+  explicit DiagonalImageLine(uint8_t** rows, int32_t xoffset, int32_t yoffset, int32_t xstep,
+                             int32_t ystep, int32_t length)
       : rows_(rows),
         xoffset_(xoffset),
         yoffset_(yoffset),
         xstep_(xstep),
         ystep_(ystep),
-        length_(length) {}
+        length_(length) {
+  }
 
-  inline int32_t GetLength() const { return length_; }
+  inline int32_t GetLength() const {
+    return length_;
+  }
 
   inline uint32_t GetColor(int32_t idx) const {
-    return NinePatch::PackRGBA(rows_[yoffset_ + (idx * ystep_)] +
-                               ((idx + xoffset_) * xstep_) * 4);
+    return NinePatch::PackRGBA(rows_[yoffset_ + (idx * ystep_)] + ((idx + xoffset_) * xstep_) * 4);
   }
 
  private:
@@ -243,8 +244,7 @@
 
   if (layout_bounds.size() > 2) {
     std::stringstream err_stream;
-    err_stream << "too many layout bounds sections on " << edge_name
-               << " border";
+    err_stream << "too many layout bounds sections on " << edge_name << " border";
     *out_err = err_stream.str();
     return false;
   }
@@ -258,8 +258,7 @@
     // end at length.
     if (range.start != 0 && range.end != length) {
       std::stringstream err_stream;
-      err_stream << "layout bounds on " << edge_name
-                 << " border must start at edge";
+      err_stream << "layout bounds on " << edge_name << " border must start at edge";
       *out_err = err_stream.str();
       return false;
     }
@@ -269,8 +268,7 @@
       const Range& range = layout_bounds.back();
       if (range.end != length) {
         std::stringstream err_stream;
-        err_stream << "layout bounds on " << edge_name
-                   << " border must start at edge";
+        err_stream << "layout bounds on " << edge_name << " border must start at edge";
         *out_err = err_stream.str();
         return false;
       }
@@ -280,8 +278,7 @@
   return true;
 }
 
-static int32_t CalculateSegmentCount(const std::vector<Range>& stretch_regions,
-                                     int32_t length) {
+static int32_t CalculateSegmentCount(const std::vector<Range>& stretch_regions, int32_t length) {
   if (stretch_regions.size() == 0) {
     return 0;
   }
@@ -299,8 +296,7 @@
 
 static uint32_t GetRegionColor(uint8_t** rows, const Bounds& region) {
   // Sample the first pixel to compare against.
-  const uint32_t expected_color =
-      NinePatch::PackRGBA(rows[region.top] + region.left * 4);
+  const uint32_t expected_color = NinePatch::PackRGBA(rows[region.top] + region.left * 4);
   for (int32_t y = region.top; y < region.bottom; y++) {
     const uint8_t* row = rows[y];
     for (int32_t x = region.left; x < region.right; x++) {
@@ -336,10 +332,11 @@
 // the indices must be offset by 1.
 //
 // width and height also include the 9-patch 1px border.
-static void CalculateRegionColors(
-    uint8_t** rows, const std::vector<Range>& horizontal_stretch_regions,
-    const std::vector<Range>& vertical_stretch_regions, const int32_t width,
-    const int32_t height, std::vector<uint32_t>* out_colors) {
+static void CalculateRegionColors(uint8_t** rows,
+                                  const std::vector<Range>& horizontal_stretch_regions,
+                                  const std::vector<Range>& vertical_stretch_regions,
+                                  const int32_t width, const int32_t height,
+                                  std::vector<uint32_t>* out_colors) {
   int32_t next_top = 0;
   Bounds bounds;
   auto row_iter = vertical_stretch_regions.begin();
@@ -401,8 +398,7 @@
 // alpha value begins
 // (on both sides).
 template <typename ImageLine>
-static void FindOutlineInsets(const ImageLine* image_line, int32_t* out_start,
-                              int32_t* out_end) {
+static void FindOutlineInsets(const ImageLine* image_line, int32_t* out_start, int32_t* out_end) {
   *out_start = 0;
   *out_end = 0;
 
@@ -455,10 +451,8 @@
   return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
 }
 
-std::unique_ptr<NinePatch> NinePatch::Create(uint8_t** rows,
-                                             const int32_t width,
-                                             const int32_t height,
-                                             std::string* out_err) {
+std::unique_ptr<NinePatch> NinePatch::Create(uint8_t** rows, const int32_t width,
+                                             const int32_t height, std::string* out_err) {
   if (width < 3 || height < 3) {
     *out_err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
     return {};
@@ -472,12 +466,11 @@
   std::unique_ptr<ColorValidator> color_validator;
 
   if (rows[0][3] == 0) {
-    color_validator = util::make_unique<TransparentNeutralColorValidator>();
+    color_validator = std::make_unique<TransparentNeutralColorValidator>();
   } else if (PackRGBA(rows[0]) == kColorOpaqueWhite) {
-    color_validator = util::make_unique<WhiteNeutralColorValidator>();
+    color_validator = std::make_unique<WhiteNeutralColorValidator>();
   } else {
-    *out_err =
-        "top-left corner pixel must be either opaque white or transparent";
+    *out_err = "top-left corner pixel must be either opaque white or transparent";
     return {};
   }
 
@@ -485,9 +478,8 @@
   auto nine_patch = std::unique_ptr<NinePatch>(new NinePatch());
 
   HorizontalImageLine top_row(rows, 0, 0, width);
-  if (!FillRanges(&top_row, color_validator.get(),
-                  &nine_patch->horizontal_stretch_regions, &unexpected_ranges,
-                  out_err)) {
+  if (!FillRanges(&top_row, color_validator.get(), &nine_patch->horizontal_stretch_regions,
+                  &unexpected_ranges, out_err)) {
     return {};
   }
 
@@ -501,9 +493,8 @@
   }
 
   VerticalImageLine left_col(rows, 0, 0, height);
-  if (!FillRanges(&left_col, color_validator.get(),
-                  &nine_patch->vertical_stretch_regions, &unexpected_ranges,
-                  out_err)) {
+  if (!FillRanges(&left_col, color_validator.get(), &nine_patch->vertical_stretch_regions,
+                  &unexpected_ranges, out_err)) {
     return {};
   }
 
@@ -522,32 +513,28 @@
   }
 
   if (!PopulateBounds(horizontal_padding, horizontal_layout_bounds,
-                      nine_patch->horizontal_stretch_regions, width - 2,
-                      &nine_patch->padding.left, &nine_patch->padding.right,
-                      &nine_patch->layout_bounds.left,
+                      nine_patch->horizontal_stretch_regions, width - 2, &nine_patch->padding.left,
+                      &nine_patch->padding.right, &nine_patch->layout_bounds.left,
                       &nine_patch->layout_bounds.right, "bottom", out_err)) {
     return {};
   }
 
   VerticalImageLine right_col(rows, width - 1, 0, height);
-  if (!FillRanges(&right_col, color_validator.get(), &vertical_padding,
-                  &vertical_layout_bounds, out_err)) {
+  if (!FillRanges(&right_col, color_validator.get(), &vertical_padding, &vertical_layout_bounds,
+                  out_err)) {
     return {};
   }
 
   if (!PopulateBounds(vertical_padding, vertical_layout_bounds,
-                      nine_patch->vertical_stretch_regions, height - 2,
-                      &nine_patch->padding.top, &nine_patch->padding.bottom,
-                      &nine_patch->layout_bounds.top,
+                      nine_patch->vertical_stretch_regions, height - 2, &nine_patch->padding.top,
+                      &nine_patch->padding.bottom, &nine_patch->layout_bounds.top,
                       &nine_patch->layout_bounds.bottom, "right", out_err)) {
     return {};
   }
 
   // Fill the region colors of the 9-patch.
-  const int32_t num_rows =
-      CalculateSegmentCount(nine_patch->horizontal_stretch_regions, width - 2);
-  const int32_t num_cols =
-      CalculateSegmentCount(nine_patch->vertical_stretch_regions, height - 2);
+  const int32_t num_rows = CalculateSegmentCount(nine_patch->horizontal_stretch_regions, width - 2);
+  const int32_t num_cols = CalculateSegmentCount(nine_patch->vertical_stretch_regions, height - 2);
   if ((int64_t)num_rows * (int64_t)num_cols > 0x7f) {
     *out_err = "too many regions in 9-patch";
     return {};
@@ -555,40 +542,35 @@
 
   nine_patch->region_colors.reserve(num_rows * num_cols);
   CalculateRegionColors(rows, nine_patch->horizontal_stretch_regions,
-                        nine_patch->vertical_stretch_regions, width - 2,
-                        height - 2, &nine_patch->region_colors);
+                        nine_patch->vertical_stretch_regions, width - 2, height - 2,
+                        &nine_patch->region_colors);
 
   // Compute the outline based on opacity.
 
   // Find left and right extent of 9-patch content on center row.
   HorizontalImageLine mid_row(rows, 1, height / 2, width - 2);
-  FindOutlineInsets(&mid_row, &nine_patch->outline.left,
-                    &nine_patch->outline.right);
+  FindOutlineInsets(&mid_row, &nine_patch->outline.left, &nine_patch->outline.right);
 
   // Find top and bottom extent of 9-patch content on center column.
   VerticalImageLine mid_col(rows, width / 2, 1, height - 2);
-  FindOutlineInsets(&mid_col, &nine_patch->outline.top,
-                    &nine_patch->outline.bottom);
+  FindOutlineInsets(&mid_col, &nine_patch->outline.top, &nine_patch->outline.bottom);
 
-  const int32_t outline_width =
-      (width - 2) - nine_patch->outline.left - nine_patch->outline.right;
+  const int32_t outline_width = (width - 2) - nine_patch->outline.left - nine_patch->outline.right;
   const int32_t outline_height =
       (height - 2) - nine_patch->outline.top - nine_patch->outline.bottom;
 
   // Find the largest alpha value within the outline area.
-  HorizontalImageLine outline_mid_row(
-      rows, 1 + nine_patch->outline.left,
-      1 + nine_patch->outline.top + (outline_height / 2), outline_width);
-  VerticalImageLine outline_mid_col(
-      rows, 1 + nine_patch->outline.left + (outline_width / 2),
-      1 + nine_patch->outline.top, outline_height);
+  HorizontalImageLine outline_mid_row(rows, 1 + nine_patch->outline.left,
+                                      1 + nine_patch->outline.top + (outline_height / 2),
+                                      outline_width);
+  VerticalImageLine outline_mid_col(rows, 1 + nine_patch->outline.left + (outline_width / 2),
+                                    1 + nine_patch->outline.top, outline_height);
   nine_patch->outline_alpha =
       std::max(FindMaxAlpha(&outline_mid_row), FindMaxAlpha(&outline_mid_col));
 
   // Assuming the image is a round rect, compute the radius by marching
   // diagonally from the top left corner towards the center.
-  DiagonalImageLine diagonal(rows, 1 + nine_patch->outline.left,
-                             1 + nine_patch->outline.top, 1, 1,
+  DiagonalImageLine diagonal(rows, 1 + nine_patch->outline.left, 1 + nine_patch->outline.top, 1, 1,
                              std::min(outline_width, outline_height));
   int32_t top_left, bottom_right;
   FindOutlineInsets(&diagonal, &top_left, &bottom_right);
@@ -614,10 +596,9 @@
   data.paddingBottom = padding.bottom;
 
   auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
-  android::Res_png_9patch::serialize(
-      data, (const int32_t*)horizontal_stretch_regions.data(),
-      (const int32_t*)vertical_stretch_regions.data(), region_colors.data(),
-      buffer.get());
+  android::Res_png_9patch::serialize(data, (const int32_t*)horizontal_stretch_regions.data(),
+                                     (const int32_t*)vertical_stretch_regions.data(),
+                                     region_colors.data(), buffer.get());
   // Convert to file endianness.
   reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
 
@@ -625,8 +606,7 @@
   return buffer;
 }
 
-std::unique_ptr<uint8_t[]> NinePatch::SerializeLayoutBounds(
-    size_t* out_len) const {
+std::unique_ptr<uint8_t[]> NinePatch::SerializeLayoutBounds(size_t* out_len) const {
   size_t chunk_len = sizeof(uint32_t) * 4;
   auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunk_len]);
   uint8_t* cursor = buffer.get();
@@ -647,8 +627,7 @@
   return buffer;
 }
 
-std::unique_ptr<uint8_t[]> NinePatch::SerializeRoundedRectOutline(
-    size_t* out_len) const {
+std::unique_ptr<uint8_t[]> NinePatch::SerializeRoundedRectOutline(size_t* out_len) const {
   size_t chunk_len = sizeof(uint32_t) * 6;
   auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunk_len]);
   uint8_t* cursor = buffer.get();
@@ -679,20 +658,25 @@
 }
 
 ::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds) {
-  return out << "l=" << bounds.left << " t=" << bounds.top
-             << " r=" << bounds.right << " b=" << bounds.bottom;
+  return out << "l=" << bounds.left << " t=" << bounds.top << " r=" << bounds.right
+             << " b=" << bounds.bottom;
+}
+
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
+  for (int i = 0; i < v.size(); ++i) {
+    os << v[i];
+    if (i != v.size() - 1) os << " ";
+  }
+  return os;
 }
 
 ::std::ostream& operator<<(::std::ostream& out, const NinePatch& nine_patch) {
-  return out << "horizontalStretch:"
-             << util::Joiner(nine_patch.horizontal_stretch_regions, " ")
-             << " verticalStretch:"
-             << util::Joiner(nine_patch.vertical_stretch_regions, " ")
-             << " padding: " << nine_patch.padding
-             << ", bounds: " << nine_patch.layout_bounds
-             << ", outline: " << nine_patch.outline
-             << " rad=" << nine_patch.outline_radius
+  return out << "horizontalStretch:" << nine_patch.horizontal_stretch_regions
+             << " verticalStretch:" << nine_patch.vertical_stretch_regions
+             << " padding: " << nine_patch.padding << ", bounds: " << nine_patch.layout_bounds
+             << ", outline: " << nine_patch.outline << " rad=" << nine_patch.outline_radius
              << " alpha=" << nine_patch.outline_alpha;
 }
 
-}  // namespace aapt
+}  // namespace android
diff --git a/tools/aapt2/compile/Png.cpp b/libs/androidfw/Png.cpp
similarity index 77%
rename from tools/aapt2/compile/Png.cpp
rename to libs/androidfw/Png.cpp
index 76db815..fb45cd9 100644
--- a/tools/aapt2/compile/Png.cpp
+++ b/libs/androidfw/Png.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Png.h"
+#include "androidfw/Png.h"
 
 #include <png.h>
 #include <zlib.h>
@@ -24,13 +24,12 @@
 #include <string>
 #include <vector>
 
+#include "android-base/strings.h"
 #include "androidfw/BigBuffer.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/Source.h"
-#include "trace/TraceBuffer.h"
-#include "util/Util.h"
 
-namespace aapt {
+namespace android {
 
 constexpr bool kDebug = false;
 
@@ -47,9 +46,8 @@
   }
 
   void* serialize9Patch() {
-    void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs,
-                                                          yDivs, colors.data());
-    reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile();
+    void* serialized = Res_png_9patch::serialize(info9Patch, xDivs, yDivs, colors.data());
+    reinterpret_cast<Res_png_9patch*>(serialized)->deviceToFile();
     return serialized;
   }
 
@@ -58,7 +56,7 @@
   std::vector<png_bytep> rows;
 
   bool is9Patch = false;
-  android::Res_png_9patch info9Patch;
+  Res_png_9patch info9Patch;
   int32_t* xDivs = nullptr;
   int32_t* yDivs = nullptr;
   std::vector<uint32_t> colors;
@@ -79,34 +77,30 @@
   uint8_t outlineAlpha;
 };
 
-static void readDataFromStream(png_structp readPtr, png_bytep data,
-                               png_size_t length) {
-  std::istream* input =
-      reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
+static void readDataFromStream(png_structp readPtr, png_bytep data, png_size_t length) {
+  std::istream* input = reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
   if (!input->read(reinterpret_cast<char*>(data), length)) {
     png_error(readPtr, strerror(errno));
   }
 }
 
-static void writeDataToStream(png_structp writePtr, png_bytep data,
-                              png_size_t length) {
-  android::BigBuffer* outBuffer = reinterpret_cast<android::BigBuffer*>(png_get_io_ptr(writePtr));
+static void writeDataToStream(png_structp writePtr, png_bytep data, png_size_t length) {
+  BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
   png_bytep buf = outBuffer->NextBlock<png_byte>(length);
   memcpy(buf, data, length);
 }
 
-static void flushDataToStream(png_structp /*writePtr*/) {}
-
-static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
-  android::IDiagnostics* diag =
-      reinterpret_cast<android::IDiagnostics*>(png_get_error_ptr(readPtr));
-  diag->Warn(android::DiagMessage() << warningMessage);
+static void flushDataToStream(png_structp /*writePtr*/) {
 }
 
-static bool readPng(android::IDiagnostics* diag, png_structp readPtr, png_infop infoPtr,
-                    PngInfo* outInfo) {
+static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
+  IDiagnostics* diag = reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr));
+  diag->Warn(DiagMessage() << warningMessage);
+}
+
+static bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr, PngInfo* outInfo) {
   if (setjmp(png_jmpbuf(readPtr))) {
-    diag->Error(android::DiagMessage() << "failed reading png");
+    diag->Error(DiagMessage() << "failed reading png");
     return false;
   }
 
@@ -114,8 +108,8 @@
   png_read_info(readPtr, infoPtr);
 
   int colorType, bitDepth, interlaceType, compressionType;
-  png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth,
-               &colorType, &interlaceType, &compressionType, nullptr);
+  png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth, &colorType,
+               &interlaceType, &compressionType, nullptr);
 
   if (colorType == PNG_COLOR_TYPE_PALETTE) {
     png_set_palette_to_rgb(readPtr);
@@ -137,8 +131,7 @@
     png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
   }
 
-  if (colorType == PNG_COLOR_TYPE_GRAY ||
-      colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+  if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
     png_set_gray_to_rgb(readPtr);
   }
 
@@ -156,12 +149,11 @@
   return true;
 }
 
-static void checkNinePatchSerialization(android::Res_png_9patch* inPatch,
-                                        void* data) {
+static void checkNinePatchSerialization(Res_png_9patch* inPatch, void* data) {
   size_t patchSize = inPatch->serializedSize();
   void* newData = malloc(patchSize);
   memcpy(newData, data, patchSize);
-  android::Res_png_9patch* outPatch = inPatch->deserialize(newData);
+  Res_png_9patch* outPatch = inPatch->deserialize(newData);
   outPatch->fileToDevice();
   // deserialization is done in place, so outPatch == newData
   assert(outPatch == newData);
@@ -244,10 +236,9 @@
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 #define ABS(a) ((a) < 0 ? -(a) : (a))
 
-static void analyze_image(android::IDiagnostics* diag, const PngInfo& imageInfo,
-                          int grayscaleTolerance, png_colorp rgbPalette, png_bytep alphaPalette,
-                          int* paletteEntries, bool* hasTransparency, int* colorType,
-                          png_bytepp outRows) {
+static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo, int grayscaleTolerance,
+                          png_colorp rgbPalette, png_bytep alphaPalette, int* paletteEntries,
+                          bool* hasTransparency, int* colorType, png_bytepp outRows) {
   int w = imageInfo.width;
   int h = imageInfo.height;
   int i, j, rr, gg, bb, aa, idx;
@@ -284,8 +275,8 @@
       maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
       if (maxGrayDeviation > odev) {
         if (kDebug) {
-          printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
-                 maxGrayDeviation, i, j, rr, gg, bb, aa);
+          printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n", maxGrayDeviation, i, j,
+                 rr, gg, bb, aa);
         }
       }
 
@@ -293,8 +284,7 @@
       if (isGrayscale) {
         if (rr != gg || rr != bb) {
           if (kDebug) {
-            printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", i, j,
-                   rr, gg, bb, aa);
+            printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", i, j, rr, gg, bb, aa);
           }
           isGrayscale = false;
         }
@@ -304,8 +294,7 @@
       if (isOpaque) {
         if (aa != 0xff) {
           if (kDebug) {
-            printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", i, j,
-                   rr, gg, bb, aa);
+            printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", i, j, rr, gg, bb, aa);
           }
           isOpaque = false;
         }
@@ -349,10 +338,9 @@
     printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
     printf("isOpaque = %s\n", isOpaque ? "true" : "false");
     printf("isPalette = %s\n", isPalette ? "true" : "false");
-    printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", paletteSize,
-           2 * w * h, bpp * w * h);
-    printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation,
-           grayscaleTolerance);
+    printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", paletteSize, 2 * w * h,
+           bpp * w * h);
+    printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
   }
 
   // Choose the best color type for the image.
@@ -381,8 +369,8 @@
     *colorType = PNG_COLOR_TYPE_PALETTE;
   } else {
     if (maxGrayDeviation <= grayscaleTolerance) {
-      diag->Note(android::DiagMessage()
-                 << "forcing image to gray (max deviation = " << maxGrayDeviation << ")");
+      diag->Note(DiagMessage() << "forcing image to gray (max deviation = " << maxGrayDeviation
+                               << ")");
       *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
     } else {
       *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
@@ -404,8 +392,7 @@
       rgbPalette[idx].blue = (png_byte)((col >> 8) & 0xff);
       alphaPalette[idx] = (png_byte)(col & 0xff);
     }
-  } else if (*colorType == PNG_COLOR_TYPE_GRAY ||
-             *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+  } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
     // If the image is gray or gray + alpha, compact the pixels into outRows
     for (j = 0; j < h; j++) {
       const png_byte* row = imageInfo.rows[j];
@@ -429,10 +416,10 @@
   }
 }
 
-static bool writePng(android::IDiagnostics* diag, png_structp writePtr, png_infop infoPtr,
-                     PngInfo* info, int grayScaleTolerance) {
+static bool writePng(IDiagnostics* diag, png_structp writePtr, png_infop infoPtr, PngInfo* info,
+                     int grayScaleTolerance) {
   if (setjmp(png_jmpbuf(writePtr))) {
-    diag->Error(android::DiagMessage() << "failed to write png");
+    diag->Error(DiagMessage() << "failed to write png");
     return false;
   }
 
@@ -444,8 +431,7 @@
   unknowns[1].data = nullptr;
   unknowns[2].data = nullptr;
 
-  png_bytepp outRows =
-      (png_bytepp)malloc((int)info->height * sizeof(png_bytep));
+  png_bytepp outRows = (png_bytepp)malloc((int)info->height * sizeof(png_bytep));
   if (outRows == (png_bytepp)0) {
     printf("Can't allocate output buffer!\n");
     exit(1);
@@ -461,8 +447,7 @@
   png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
 
   if (kDebug) {
-    diag->Note(android::DiagMessage()
-               << "writing image: w = " << info->width << ", h = " << info->height);
+    diag->Note(DiagMessage() << "writing image: w = " << info->width << ", h = " << info->height);
   }
 
   png_color rgbPalette[256];
@@ -470,48 +455,45 @@
   bool hasTransparency;
   int paletteEntries;
 
-  analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette,
-                &paletteEntries, &hasTransparency, &colorType, outRows);
+  analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette, &paletteEntries,
+                &hasTransparency, &colorType, outRows);
 
   // If the image is a 9-patch, we need to preserve it as a ARGB file to make
   // sure the pixels will not be pre-dithered/clamped until we decide they are
-  if (info->is9Patch &&
-      (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY ||
-       colorType == PNG_COLOR_TYPE_PALETTE)) {
+  if (info->is9Patch && (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY ||
+                         colorType == PNG_COLOR_TYPE_PALETTE)) {
     colorType = PNG_COLOR_TYPE_RGB_ALPHA;
   }
 
   if (kDebug) {
     switch (colorType) {
       case PNG_COLOR_TYPE_PALETTE:
-        diag->Note(android::DiagMessage() << "has " << paletteEntries << " colors"
-                                          << (hasTransparency ? " (with alpha)" : "")
-                                          << ", using PNG_COLOR_TYPE_PALLETTE");
+        diag->Note(DiagMessage() << "has " << paletteEntries << " colors"
+                                 << (hasTransparency ? " (with alpha)" : "")
+                                 << ", using PNG_COLOR_TYPE_PALLETTE");
         break;
       case PNG_COLOR_TYPE_GRAY:
-        diag->Note(android::DiagMessage() << "is opaque gray, using PNG_COLOR_TYPE_GRAY");
+        diag->Note(DiagMessage() << "is opaque gray, using PNG_COLOR_TYPE_GRAY");
         break;
       case PNG_COLOR_TYPE_GRAY_ALPHA:
-        diag->Note(android::DiagMessage() << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
+        diag->Note(DiagMessage() << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
         break;
       case PNG_COLOR_TYPE_RGB:
-        diag->Note(android::DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
+        diag->Note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
         break;
       case PNG_COLOR_TYPE_RGB_ALPHA:
-        diag->Note(android::DiagMessage() << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
+        diag->Note(DiagMessage() << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
         break;
     }
   }
 
-  png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
-               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
-               PNG_FILTER_TYPE_DEFAULT);
+  png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType, PNG_INTERLACE_NONE,
+               PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 
   if (colorType == PNG_COLOR_TYPE_PALETTE) {
     png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries);
     if (hasTransparency) {
-      png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries,
-                   (png_color_16p)0);
+      png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries, (png_color_16p)0);
     }
     png_set_filter(writePtr, 0, PNG_NO_FILTERS);
   } else {
@@ -526,13 +508,12 @@
 
     // Chunks ordered thusly because older platforms depend on the base 9 patch
     // data being last
-    png_bytep chunkNames = info->haveLayoutBounds
-                               ? (png_bytep) "npOl\0npLb\0npTc\0"
-                               : (png_bytep) "npOl\0npTc";
+    png_bytep chunkNames =
+        info->haveLayoutBounds ? (png_bytep) "npOl\0npLb\0npTc\0" : (png_bytep) "npOl\0npTc";
 
     // base 9 patch data
     if (kDebug) {
-      diag->Note(android::DiagMessage() << "adding 9-patch info..");
+      diag->Note(DiagMessage() << "adding 9-patch info..");
     }
     memcpy((char*)unknowns[pIndex].name, "npTc", 5);
     unknowns[pIndex].data = (png_byte*)info->serialize9Patch();
@@ -563,8 +544,7 @@
     for (int i = 0; i < chunkCount; i++) {
       unknowns[i].location = PNG_HAVE_PLTE;
     }
-    png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, chunkNames,
-                                chunkCount);
+    png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, chunkNames, chunkCount);
     png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount);
 
 #if PNG_LIBPNG_VER < 10600
@@ -579,8 +559,7 @@
   png_write_info(writePtr, infoPtr);
 
   png_bytepp rows;
-  if (colorType == PNG_COLOR_TYPE_RGB ||
-      colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
+  if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
     if (colorType == PNG_COLOR_TYPE_RGB) {
       png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
     }
@@ -605,14 +584,13 @@
   free(unknowns[1].data);
   free(unknowns[2].data);
 
-  png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType,
-               &interlaceType, &compressionType, nullptr);
+  png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceType,
+               &compressionType, nullptr);
 
   if (kDebug) {
-    diag->Note(android::DiagMessage()
-               << "image written: w = " << width << ", h = " << height << ", d = " << bitDepth
-               << ", colors = " << colorType << ", inter = " << interlaceType
-               << ", comp = " << compressionType);
+    diag->Note(DiagMessage() << "image written: w = " << width << ", h = " << height
+                             << ", d = " << bitDepth << ", colors = " << colorType
+                             << ", inter = " << interlaceType << ", comp = " << compressionType);
   }
   return true;
 }
@@ -673,9 +651,8 @@
 
 enum class TickState { kStart, kInside1, kOutside1 };
 
-static bool getHorizontalTicks(png_bytep row, int width, bool transparent,
-                               bool required, int32_t* outLeft,
-                               int32_t* outRight, const char** outError,
+static bool getHorizontalTicks(png_bytep row, int width, bool transparent, bool required,
+                               int32_t* outLeft, int32_t* outRight, const char** outError,
                                uint8_t* outDivs, bool multipleAllowed) {
   *outLeft = *outRight = -1;
   TickState state = TickState::kStart;
@@ -683,8 +660,7 @@
 
   for (int i = 1; i < width - 1; i++) {
     if (tickType(row + i * 4, transparent, outError) == TickType::kTick) {
-      if (state == TickState::kStart ||
-          (state == TickState::kOutside1 && multipleAllowed)) {
+      if (state == TickState::kStart || (state == TickState::kOutside1 && multipleAllowed)) {
         *outLeft = i - 1;
         *outRight = width - 2;
         found = true;
@@ -719,18 +695,16 @@
   return true;
 }
 
-static bool getVerticalTicks(png_bytepp rows, int offset, int height,
-                             bool transparent, bool required, int32_t* outTop,
-                             int32_t* outBottom, const char** outError,
-                             uint8_t* outDivs, bool multipleAllowed) {
+static bool getVerticalTicks(png_bytepp rows, int offset, int height, bool transparent,
+                             bool required, int32_t* outTop, int32_t* outBottom,
+                             const char** outError, uint8_t* outDivs, bool multipleAllowed) {
   *outTop = *outBottom = -1;
   TickState state = TickState::kStart;
   bool found = false;
 
   for (int i = 1; i < height - 1; i++) {
     if (tickType(rows[i] + offset, transparent, outError) == TickType::kTick) {
-      if (state == TickState::kStart ||
-          (state == TickState::kOutside1 && multipleAllowed)) {
+      if (state == TickState::kStart || (state == TickState::kOutside1 && multipleAllowed)) {
         *outTop = i - 1;
         *outBottom = height - 2;
         found = true;
@@ -765,10 +739,8 @@
   return true;
 }
 
-static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width,
-                                           bool transparent,
-                                           bool /* required */,
-                                           int32_t* outLeft, int32_t* outRight,
+static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width, bool transparent,
+                                           bool /* required */, int32_t* outLeft, int32_t* outRight,
                                            const char** outError) {
   *outLeft = *outRight = 0;
 
@@ -779,23 +751,20 @@
     while (i < width - 1) {
       (*outLeft)++;
       i++;
-      if (tickType(row + i * 4, transparent, outError) !=
-          TickType::kLayoutBounds) {
+      if (tickType(row + i * 4, transparent, outError) != TickType::kLayoutBounds) {
         break;
       }
     }
   }
 
   // Look for right tick
-  if (tickType(row + (width - 2) * 4, transparent, outError) ==
-      TickType::kLayoutBounds) {
+  if (tickType(row + (width - 2) * 4, transparent, outError) == TickType::kLayoutBounds) {
     // Ending with a layout padding tick
     int i = width - 2;
     while (i > 1) {
       (*outRight)++;
       i--;
-      if (tickType(row + i * 4, transparent, outError) !=
-          TickType::kLayoutBounds) {
+      if (tickType(row + i * 4, transparent, outError) != TickType::kLayoutBounds) {
         break;
       }
     }
@@ -803,38 +772,32 @@
   return true;
 }
 
-static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset,
-                                         int height, bool transparent,
-                                         bool /* required */, int32_t* outTop,
-                                         int32_t* outBottom,
+static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset, int height, bool transparent,
+                                         bool /* required */, int32_t* outTop, int32_t* outBottom,
                                          const char** outError) {
   *outTop = *outBottom = 0;
 
   // Look for top tick
-  if (tickType(rows[1] + offset, transparent, outError) ==
-      TickType::kLayoutBounds) {
+  if (tickType(rows[1] + offset, transparent, outError) == TickType::kLayoutBounds) {
     // Starting with a layout padding tick
     int i = 1;
     while (i < height - 1) {
       (*outTop)++;
       i++;
-      if (tickType(rows[i] + offset, transparent, outError) !=
-          TickType::kLayoutBounds) {
+      if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
         break;
       }
     }
   }
 
   // Look for bottom tick
-  if (tickType(rows[height - 2] + offset, transparent, outError) ==
-      TickType::kLayoutBounds) {
+  if (tickType(rows[height - 2] + offset, transparent, outError) == TickType::kLayoutBounds) {
     // Ending with a layout padding tick
     int i = height - 2;
     while (i > 1) {
       (*outBottom)++;
       i--;
-      if (tickType(rows[i] + offset, transparent, outError) !=
-          TickType::kLayoutBounds) {
+      if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
         break;
       }
     }
@@ -842,13 +805,12 @@
   return true;
 }
 
-static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX,
-                           int endY, int dX, int dY, int* outInset) {
+static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX, int endY, int dX,
+                           int dY, int* outInset) {
   uint8_t maxOpacity = 0;
   int inset = 0;
   *outInset = 0;
-  for (int x = startX, y = startY; x != endX && y != endY;
-       x += dX, y += dY, inset++) {
+  for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
     png_byte* color = rows[y] + x * 4;
     uint8_t opacity = color[3];
     if (opacity > maxOpacity) {
@@ -868,8 +830,7 @@
   return maxAlpha;
 }
 
-static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY,
-                               int endY) {
+static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY, int endY) {
   uint8_t maxAlpha = 0;
   for (int y = startY; y < endY; y++) {
     uint8_t alpha = (rows[y] + offsetX * 4)[3];
@@ -886,10 +847,8 @@
 
   // find left and right extent of nine patch content on center row
   if (image->width > 4) {
-    findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0,
-                   &image->outlineInsetsLeft);
-    findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0,
-                   &image->outlineInsetsRight);
+    findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
+    findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0, &image->outlineInsetsRight);
   } else {
     image->outlineInsetsLeft = 0;
     image->outlineInsetsRight = 0;
@@ -897,10 +856,8 @@
 
   // find top and bottom extent of nine patch content on center column
   if (image->height > 4) {
-    findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1,
-                   &image->outlineInsetsTop);
-    findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1,
-                   &image->outlineInsetsBottom);
+    findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
+    findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1, &image->outlineInsetsBottom);
   } else {
     image->outlineInsetsTop = 0;
     image->outlineInsetsBottom = 0;
@@ -915,13 +872,13 @@
 
   // assuming the image is a round rect, compute the radius by marching
   // diagonally from the top left corner towards the center
-  image->outlineAlpha = std::max(
-      maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
-      maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
+  image->outlineAlpha =
+      std::max(maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
+               maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
 
   int diagonalInset = 0;
-  findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX,
-                 innerMidY, 1, 1, &diagonalInset);
+  findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
+                 &diagonalInset);
 
   /* Determine source radius based upon inset:
    *     sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
@@ -932,19 +889,17 @@
   image->outlineRadius = 3.4142f * diagonalInset;
 
   if (kDebug) {
-    printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
-           image->outlineInsetsLeft, image->outlineInsetsTop,
-           image->outlineInsetsRight, image->outlineInsetsBottom,
+    printf("outline insets %d %d %d %d, rad %f, alpha %x\n", image->outlineInsetsLeft,
+           image->outlineInsetsTop, image->outlineInsetsRight, image->outlineInsetsBottom,
            image->outlineRadius, image->outlineAlpha);
   }
 }
 
-static uint32_t getColor(png_bytepp rows, int left, int top, int right,
-                         int bottom) {
+static uint32_t getColor(png_bytepp rows, int left, int top, int right, int bottom) {
   png_bytep color = rows[top] + left * 4;
 
   if (left > right || top > bottom) {
-    return android::Res_png_9patch::TRANSPARENT_COLOR;
+    return Res_png_9patch::TRANSPARENT_COLOR;
   }
 
   while (top <= bottom) {
@@ -952,18 +907,17 @@
       png_bytep p = rows[top] + i * 4;
       if (color[3] == 0) {
         if (p[3] != 0) {
-          return android::Res_png_9patch::NO_COLOR;
+          return Res_png_9patch::NO_COLOR;
         }
-      } else if (p[0] != color[0] || p[1] != color[1] || p[2] != color[2] ||
-                 p[3] != color[3]) {
-        return android::Res_png_9patch::NO_COLOR;
+      } else if (p[0] != color[0] || p[1] != color[1] || p[2] != color[2] || p[3] != color[3]) {
+        return Res_png_9patch::NO_COLOR;
       }
     }
     top++;
   }
 
   if (color[3] == 0) {
-    return android::Res_png_9patch::TRANSPARENT_COLOR;
+    return Res_png_9patch::TRANSPARENT_COLOR;
   }
   return (color[3] << 24) | (color[0] << 16) | (color[1] << 8) | color[2];
 }
@@ -1014,23 +968,22 @@
   }
 
   // Validate frame...
-  if (!transparent &&
-      (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
+  if (!transparent && (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
     errorMsg = "Must have one-pixel frame that is either transparent or white";
     goto getout;
   }
 
   // Find left and right of sizing areas...
-  if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1],
-                          &errorMsg, &numXDivs, true)) {
+  if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1], &errorMsg, &numXDivs,
+                          true)) {
     errorPixel = xDivs[0];
     errorEdge = "top";
     goto getout;
   }
 
   // Find top and bottom of sizing areas...
-  if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0],
-                        &yDivs[1], &errorMsg, &numYDivs, true)) {
+  if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0], &yDivs[1],
+                        &errorMsg, &numYDivs, true)) {
     errorPixel = yDivs[0];
     errorEdge = "left";
     goto getout;
@@ -1041,10 +994,8 @@
   image->info9Patch.numYDivs = numYDivs;
 
   // Find left and right of padding area...
-  if (!getHorizontalTicks(image->rows[H - 1], W, transparent, false,
-                          &image->info9Patch.paddingLeft,
-                          &image->info9Patch.paddingRight, &errorMsg, nullptr,
-                          false)) {
+  if (!getHorizontalTicks(image->rows[H - 1], W, transparent, false, &image->info9Patch.paddingLeft,
+                          &image->info9Patch.paddingRight, &errorMsg, nullptr, false)) {
     errorPixel = image->info9Patch.paddingLeft;
     errorEdge = "bottom";
     goto getout;
@@ -1052,9 +1003,8 @@
 
   // Find top and bottom of padding area...
   if (!getVerticalTicks(image->rows.data(), (W - 1) * 4, H, transparent, false,
-                        &image->info9Patch.paddingTop,
-                        &image->info9Patch.paddingBottom, &errorMsg, nullptr,
-                        false)) {
+                        &image->info9Patch.paddingTop, &image->info9Patch.paddingBottom, &errorMsg,
+                        nullptr, false)) {
     errorPixel = image->info9Patch.paddingTop;
     errorEdge = "right";
     goto getout;
@@ -1062,22 +1012,18 @@
 
   // Find left and right of layout padding...
   getHorizontalLayoutBoundsTicks(image->rows[H - 1], W, transparent, false,
-                                 &image->layoutBoundsLeft,
-                                 &image->layoutBoundsRight, &errorMsg);
+                                 &image->layoutBoundsLeft, &image->layoutBoundsRight, &errorMsg);
 
-  getVerticalLayoutBoundsTicks(image->rows.data(), (W - 1) * 4, H, transparent,
-                               false, &image->layoutBoundsTop,
-                               &image->layoutBoundsBottom, &errorMsg);
+  getVerticalLayoutBoundsTicks(image->rows.data(), (W - 1) * 4, H, transparent, false,
+                               &image->layoutBoundsTop, &image->layoutBoundsBottom, &errorMsg);
 
-  image->haveLayoutBounds =
-      image->layoutBoundsLeft != 0 || image->layoutBoundsRight != 0 ||
-      image->layoutBoundsTop != 0 || image->layoutBoundsBottom != 0;
+  image->haveLayoutBounds = image->layoutBoundsLeft != 0 || image->layoutBoundsRight != 0 ||
+                            image->layoutBoundsTop != 0 || image->layoutBoundsBottom != 0;
 
   if (image->haveLayoutBounds) {
     if (kDebug) {
-      printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft,
-             image->layoutBoundsTop, image->layoutBoundsRight,
-             image->layoutBoundsBottom);
+      printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
+             image->layoutBoundsRight, image->layoutBoundsBottom);
     }
   }
 
@@ -1189,7 +1135,7 @@
       c = getColor(image->rows.data(), left, top, right - 1, bottom - 1);
       image->colors[colorIndex++] = c;
       if (kDebug) {
-        if (c != android::Res_png_9patch::NO_COLOR) {
+        if (c != Res_png_9patch::NO_COLOR) {
           hasColor = true;
         }
       }
@@ -1214,8 +1160,7 @@
     if (errorEdge) {
       err << "." << std::endl;
       if (errorPixel >= 0) {
-        err << "Found at pixel #" << errorPixel << " along " << errorEdge
-            << " edge";
+        err << "Found at pixel #" << errorPixel << " along " << errorEdge << " edge";
       } else {
         err << "Found along " << errorEdge << " edge";
       }
@@ -1226,20 +1171,19 @@
   return true;
 }
 
-bool Png::process(const android::Source& source, std::istream* input, android::BigBuffer* outBuffer,
+bool Png::process(const Source& source, std::istream* input, BigBuffer* outBuffer,
                   const PngOptions& options) {
-  TRACE_CALL();
   png_byte signature[kPngSignatureSize];
 
   // Read the PNG signature first.
   if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
-    mDiag->Error(android::DiagMessage() << strerror(errno));
+    mDiag->Error(DiagMessage() << strerror(errno));
     return false;
   }
 
   // If the PNG signature doesn't match, bail early.
   if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
-    mDiag->Error(android::DiagMessage() << "not a valid png file");
+    mDiag->Error(DiagMessage() << "not a valid png file");
     return false;
   }
 
@@ -1252,18 +1196,17 @@
 
   readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
   if (!readPtr) {
-    mDiag->Error(android::DiagMessage() << "failed to allocate read ptr");
+    mDiag->Error(DiagMessage() << "failed to allocate read ptr");
     goto bail;
   }
 
   infoPtr = png_create_info_struct(readPtr);
   if (!infoPtr) {
-    mDiag->Error(android::DiagMessage() << "failed to allocate info ptr");
+    mDiag->Error(DiagMessage() << "failed to allocate info ptr");
     goto bail;
   }
 
-  png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr,
-                   logWarning);
+  png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr, logWarning);
 
   // Set the read function to read from std::istream.
   png_set_read_fn(readPtr, (png_voidp)input, readDataFromStream);
@@ -1272,35 +1215,32 @@
     goto bail;
   }
 
-  if (util::EndsWith(source.path, ".9.png")) {
+  if (android::base::EndsWith(source.path, ".9.png")) {
     std::string errorMsg;
     if (!do9Patch(&pngInfo, &errorMsg)) {
-      mDiag->Error(android::DiagMessage() << errorMsg);
+      mDiag->Error(DiagMessage() << errorMsg);
       goto bail;
     }
   }
 
-  writePtr =
-      png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
+  writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
   if (!writePtr) {
-    mDiag->Error(android::DiagMessage() << "failed to allocate write ptr");
+    mDiag->Error(DiagMessage() << "failed to allocate write ptr");
     goto bail;
   }
 
   writeInfoPtr = png_create_info_struct(writePtr);
   if (!writeInfoPtr) {
-    mDiag->Error(android::DiagMessage() << "failed to allocate write info ptr");
+    mDiag->Error(DiagMessage() << "failed to allocate write info ptr");
     goto bail;
   }
 
   png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
 
   // Set the write function to write to std::ostream.
-  png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream,
-                   flushDataToStream);
+  png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream, flushDataToStream);
 
-  if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo,
-                options.grayscale_tolerance)) {
+  if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo, options.grayscale_tolerance)) {
     goto bail;
   }
 
@@ -1316,4 +1256,4 @@
   return result;
 }
 
-}  // namespace aapt
+}  // namespace android
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/libs/androidfw/PngChunkFilter.cpp
similarity index 95%
rename from tools/aapt2/compile/PngChunkFilter.cpp
rename to libs/androidfw/PngChunkFilter.cpp
index 2e55d0c..331b948 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/libs/androidfw/PngChunkFilter.cpp
@@ -14,25 +14,22 @@
  * limitations under the License.
  */
 
-#include "compile/Png.h"
-
 #include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+#include "androidfw/Png.h"
+#include "androidfw/Streams.h"
 #include "androidfw/StringPiece.h"
 
-#include "io/Io.h"
-
-using ::android::StringPiece;
 using ::android::base::StringPrintf;
 
-namespace aapt {
+namespace android {
 
 static constexpr const char* kPngSignature = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a";
 
 // Useful helper function that encodes individual bytes into a uint32
 // at compile time.
 constexpr uint32_t u32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
-  return (((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)c) << 8) |
-         ((uint32_t)d);
+  return (((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)c) << 8) | ((uint32_t)d);
 }
 
 // Allow list of PNG chunk types that we want to keep in the resulting PNG.
@@ -71,7 +68,7 @@
 }
 
 PngChunkFilter::PngChunkFilter(StringPiece data) : data_(data) {
-  if (util::StartsWith(data_, kPngSignature)) {
+  if (android::base::StartsWith(data_, kPngSignature)) {
     window_start_ = 0;
     window_end_ = kPngSignatureSize;
   } else {
@@ -176,5 +173,4 @@
   window_end_ = kPngSignatureSize;
   return true;
 }
-
-}  // namespace aapt
+}  // namespace android
diff --git a/tools/aapt2/compile/PngCrunch.cpp b/libs/androidfw/PngCrunch.cpp
similarity index 87%
rename from tools/aapt2/compile/PngCrunch.cpp
rename to libs/androidfw/PngCrunch.cpp
index 4ef87ba..cf3c0ee 100644
--- a/tools/aapt2/compile/PngCrunch.cpp
+++ b/libs/androidfw/PngCrunch.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include "compile/Png.h"
-
 #include <png.h>
 #include <zlib.h>
 
@@ -26,16 +24,16 @@
 #include "android-base/errors.h"
 #include "android-base/logging.h"
 #include "android-base/macros.h"
+#include "androidfw/Png.h"
 
-#include "trace/TraceBuffer.h"
-
-namespace aapt {
+namespace android {
 
 // Custom deleter that destroys libpng read and info structs.
 class PngReadStructDeleter {
  public:
   PngReadStructDeleter(png_structp read_ptr, png_infop info_ptr)
-      : read_ptr_(read_ptr), info_ptr_(info_ptr) {}
+      : read_ptr_(read_ptr), info_ptr_(info_ptr) {
+  }
 
   ~PngReadStructDeleter() {
     png_destroy_read_struct(&read_ptr_, &info_ptr_, nullptr);
@@ -52,7 +50,8 @@
 class PngWriteStructDeleter {
  public:
   PngWriteStructDeleter(png_structp write_ptr, png_infop info_ptr)
-      : write_ptr_(write_ptr), info_ptr_(info_ptr) {}
+      : write_ptr_(write_ptr), info_ptr_(info_ptr) {
+  }
 
   ~PngWriteStructDeleter() {
     png_destroy_write_struct(&write_ptr_, &info_ptr_);
@@ -83,7 +82,7 @@
 }
 
 static void ReadDataFromStream(png_structp png_ptr, png_bytep buffer, png_size_t len) {
-  io::InputStream* in = (io::InputStream*)png_get_io_ptr(png_ptr);
+  InputStream* in = (InputStream*)png_get_io_ptr(png_ptr);
 
   const void* in_buffer;
   size_t in_len;
@@ -108,7 +107,7 @@
 }
 
 static void WriteDataToStream(png_structp png_ptr, png_bytep buffer, png_size_t len) {
-  io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(png_ptr);
+  OutputStream* out = (OutputStream*)png_get_io_ptr(png_ptr);
 
   void* out_buffer;
   size_t out_len;
@@ -143,28 +142,22 @@
   }
 }
 
-std::unique_ptr<Image> ReadPng(IAaptContext* context, const android::Source& source,
-                               io::InputStream* in) {
-  TRACE_CALL();
-  // Create a diagnostics that has the source information encoded.
-  android::SourcePathDiagnostics source_diag(source, context->GetDiagnostics());
-
+std::unique_ptr<Image> ReadPng(InputStream* in, IDiagnostics* diag) {
   // Read the first 8 bytes of the file looking for the PNG signature.
   // Bail early if it does not match.
   const png_byte* signature;
   size_t buffer_size;
   if (!in->Next((const void**)&signature, &buffer_size)) {
     if (in->HadError()) {
-      source_diag.Error(android::DiagMessage()
-                        << "failed to read PNG signature: " << in->GetError());
+      diag->Error(android::DiagMessage() << "failed to read PNG signature: " << in->GetError());
     } else {
-      source_diag.Error(android::DiagMessage() << "not enough data for PNG signature");
+      diag->Error(android::DiagMessage() << "not enough data for PNG signature");
     }
     return {};
   }
 
   if (buffer_size < kPngSignatureSize || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
-    source_diag.Error(android::DiagMessage() << "file signature does not match PNG signature");
+    diag->Error(android::DiagMessage() << "file signature does not match PNG signature");
     return {};
   }
 
@@ -176,14 +169,14 @@
   // version of libpng.
   png_structp read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
   if (read_ptr == nullptr) {
-    source_diag.Error(android::DiagMessage() << "failed to create libpng read png_struct");
+    diag->Error(android::DiagMessage() << "failed to create libpng read png_struct");
     return {};
   }
 
   // Create and initialize the memory for image header and data.
   png_infop info_ptr = png_create_info_struct(read_ptr);
   if (info_ptr == nullptr) {
-    source_diag.Error(android::DiagMessage() << "failed to create libpng read png_info");
+    diag->Error(android::DiagMessage() << "failed to create libpng read png_info");
     png_destroy_read_struct(&read_ptr, nullptr, nullptr);
     return {};
   }
@@ -199,7 +192,7 @@
   }
 
   // Handle warnings ourselves via IDiagnostics.
-  png_set_error_fn(read_ptr, (png_voidp)&source_diag, LogError, LogWarning);
+  png_set_error_fn(read_ptr, (png_voidp)&diag, LogError, LogWarning);
 
   // Set up the read functions which read from our custom data sources.
   png_set_read_fn(read_ptr, (png_voidp)in, ReadDataFromStream);
@@ -213,8 +206,8 @@
   // Extract image meta-data from the various chunk headers.
   uint32_t width, height;
   int bit_depth, color_type, interlace_method, compression_method, filter_method;
-  png_get_IHDR(read_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
-               &interlace_method, &compression_method, &filter_method);
+  png_get_IHDR(read_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_method,
+               &compression_method, &filter_method);
 
   // When the image is read, expand it so that it is in RGBA 8888 format
   // so that image handling is uniform.
@@ -239,8 +232,7 @@
     png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
   }
 
-  if (color_type == PNG_COLOR_TYPE_GRAY ||
-      color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+  if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
     png_set_gray_to_rgb(read_ptr);
   }
 
@@ -256,12 +248,12 @@
   // something
   // that can always be represented by 9-patch.
   if (width > std::numeric_limits<int32_t>::max() || height > std::numeric_limits<int32_t>::max()) {
-    source_diag.Error(android::DiagMessage()
-                      << "PNG image dimensions are too large: " << width << "x" << height);
+    diag->Error(android::DiagMessage()
+                << "PNG image dimensions are too large: " << width << "x" << height);
     return {};
   }
 
-  std::unique_ptr<Image> output_image = util::make_unique<Image>();
+  std::unique_ptr<Image> output_image = std::make_unique<Image>();
   output_image->width = static_cast<int32_t>(width);
   output_image->height = static_cast<int32_t>(height);
 
@@ -272,7 +264,7 @@
   output_image->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * row_bytes]);
 
   // Create an array of rows that index into the data block.
-  output_image->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
+  output_image->rows = std::unique_ptr<uint8_t*[]>(new uint8_t*[height]);
   for (uint32_t h = 0; h < height; h++) {
     output_image->rows[h] = output_image->data.get() + (h * row_bytes);
   }
@@ -332,8 +324,7 @@
       // This grayscale has alpha and can fit within a palette.
       // See if it is worth fitting into a palette.
       const size_t palette_threshold = palette_chunk_size + alpha_chunk_size +
-                                       palette_data_chunk_size +
-                                       kPaletteOverheadConstant;
+                                       palette_data_chunk_size + kPaletteOverheadConstant;
       if (grayscale_alpha_data_chunk_size > palette_threshold) {
         return PNG_COLOR_TYPE_PALETTE;
       }
@@ -343,16 +334,14 @@
 
   if (color_palette_size <= 256 && !has_nine_patch) {
     // This image can fit inside a palette. Let's see if it is worth it.
-    size_t total_size_with_palette =
-        palette_data_chunk_size + palette_chunk_size;
+    size_t total_size_with_palette = palette_data_chunk_size + palette_chunk_size;
     size_t total_size_without_palette = color_data_chunk_size;
     if (alpha_palette_size > 0) {
       total_size_with_palette += alpha_palette_size;
       total_size_without_palette = color_alpha_data_chunk_size;
     }
 
-    if (total_size_without_palette >
-        total_size_with_palette + kPaletteOverheadConstant) {
+    if (total_size_without_palette > total_size_with_palette + kPaletteOverheadConstant) {
       return PNG_COLOR_TYPE_PALETTE;
     }
   }
@@ -482,26 +471,22 @@
   png_set_unknown_chunks(write_ptr, write_info_ptr, unknown_chunks, index);
 }
 
-bool WritePng(IAaptContext* context, const Image* image,
-              const NinePatch* nine_patch, io::OutputStream* out,
-              const PngOptions& options) {
-  TRACE_CALL();
+bool WritePng(const Image* image, const NinePatch* nine_patch, OutputStream* out,
+              const PngOptions& options, IDiagnostics* diag, bool verbose) {
   // Create and initialize the write png_struct with the default error and
   // warning handlers.
   // The header version is also passed in to ensure that this was built against the same
   // version of libpng.
   png_structp write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
   if (write_ptr == nullptr) {
-    context->GetDiagnostics()->Error(android::DiagMessage()
-                                     << "failed to create libpng write png_struct");
+    diag->Error(android::DiagMessage() << "failed to create libpng write png_struct");
     return false;
   }
 
   // Allocate memory to store image header data.
   png_infop write_info_ptr = png_create_info_struct(write_ptr);
   if (write_info_ptr == nullptr) {
-    context->GetDiagnostics()->Error(android::DiagMessage()
-                                     << "failed to create libpng write png_info");
+    diag->Error(android::DiagMessage() << "failed to create libpng write png_info");
     png_destroy_write_struct(&write_ptr, nullptr);
     return false;
   }
@@ -516,7 +501,7 @@
   }
 
   // Handle warnings with our IDiagnostics.
-  png_set_error_fn(write_ptr, (png_voidp)context->GetDiagnostics(), LogError, LogWarning);
+  png_set_error_fn(write_ptr, (png_voidp)&diag, LogError, LogWarning);
 
   // Set up the write functions which write to our custom data sources.
   png_set_write_fn(write_ptr, (png_voidp)out, WriteDataToStream, nullptr);
@@ -578,22 +563,21 @@
     }
   }
 
-  if (context->IsVerbose()) {
+  if (verbose) {
     android::DiagMessage msg;
-    msg << " paletteSize=" << color_palette.size()
-        << " alphaPaletteSize=" << alpha_palette.size()
+    msg << " paletteSize=" << color_palette.size() << " alphaPaletteSize=" << alpha_palette.size()
         << " maxGrayDeviation=" << max_gray_deviation
         << " grayScale=" << (grayscale ? "true" : "false");
-    context->GetDiagnostics()->Note(msg);
+    diag->Note(msg);
   }
 
   const bool convertible_to_grayscale = max_gray_deviation <= options.grayscale_tolerance;
 
-  const int new_color_type = PickColorType(
-      image->width, image->height, grayscale, convertible_to_grayscale,
-      nine_patch != nullptr, color_palette.size(), alpha_palette.size());
+  const int new_color_type =
+      PickColorType(image->width, image->height, grayscale, convertible_to_grayscale,
+                    nine_patch != nullptr, color_palette.size(), alpha_palette.size());
 
-  if (context->IsVerbose()) {
+  if (verbose) {
     android::DiagMessage msg;
     msg << "encoding PNG ";
     if (nine_patch) {
@@ -619,12 +603,11 @@
         msg << "unknown type " << new_color_type;
         break;
     }
-    context->GetDiagnostics()->Note(msg);
+    diag->Note(msg);
   }
 
-  png_set_IHDR(write_ptr, write_info_ptr, image->width, image->height, 8,
-               new_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
-               PNG_FILTER_TYPE_DEFAULT);
+  png_set_IHDR(write_ptr, write_info_ptr, image->width, image->height, 8, new_color_type,
+               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 
   if (new_color_type & PNG_COLOR_MASK_PALETTE) {
     // Assigns indices to the palette, and writes the encoded palette to the
@@ -666,11 +649,9 @@
       }
       png_write_row(write_ptr, out_row.get());
     }
-  } else if (new_color_type == PNG_COLOR_TYPE_GRAY ||
-             new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+  } else if (new_color_type == PNG_COLOR_TYPE_GRAY || new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
     const size_t bpp = new_color_type == PNG_COLOR_TYPE_GRAY ? 1 : 2;
-    auto out_row =
-        std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+    auto out_row = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
 
     for (int32_t y = 0; y < image->height; y++) {
       png_const_bytep in_row = image->rows[y];
@@ -691,8 +672,7 @@
           // The image is convertible to grayscale, use linear-luminance of
           // sRGB colorspace:
           // https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
-          out_row[x * bpp] =
-              (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
+          out_row[x * bpp] = (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
         }
 
         if (bpp == 2) {
@@ -747,4 +727,4 @@
   return true;
 }
 
-}  // namespace aapt
+}  // namespace android
diff --git a/tools/aapt2/io/BigBufferStream.h b/libs/androidfw/include/androidfw/BigBufferStream.h
similarity index 74%
rename from tools/aapt2/io/BigBufferStream.h
rename to libs/androidfw/include/androidfw/BigBufferStream.h
index 63a5e57..e55fe0d 100644
--- a/tools/aapt2/io/BigBufferStream.h
+++ b/libs/androidfw/include/androidfw/BigBufferStream.h
@@ -14,18 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_IO_BIGBUFFERSTREAM_H
-#define AAPT_IO_BIGBUFFERSTREAM_H
+#pragma once
 
-#include "androidfw/BigBuffer.h"
-#include "io/Io.h"
+#include "BigBuffer.h"
+#include "Streams.h"
 
-namespace aapt {
-namespace io {
+namespace android {
 
 class BigBufferInputStream : public KnownSizeInputStream {
  public:
-  inline explicit BigBufferInputStream(const android::BigBuffer* buffer)
+  inline explicit BigBufferInputStream(const BigBuffer* buffer)
       : buffer_(buffer), iter_(buffer->begin()) {
   }
   virtual ~BigBufferInputStream() = default;
@@ -44,18 +42,20 @@
 
   size_t TotalSize() const override;
 
+  bool ReadFullyAtOffset(void* data, size_t byte_count, off64_t offset) override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(BigBufferInputStream);
 
-  const android::BigBuffer* buffer_;
-  android::BigBuffer::const_iterator iter_;
+  const BigBuffer* buffer_;
+  BigBuffer::const_iterator iter_;
   size_t offset_ = 0;
   size_t bytes_read_ = 0;
 };
 
 class BigBufferOutputStream : public OutputStream {
  public:
-  inline explicit BigBufferOutputStream(android::BigBuffer* buffer) : buffer_(buffer) {
+  inline explicit BigBufferOutputStream(BigBuffer* buffer) : buffer_(buffer) {
   }
   virtual ~BigBufferOutputStream() = default;
 
@@ -70,10 +70,7 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
 
-  android::BigBuffer* buffer_;
+  BigBuffer* buffer_;
 };
 
-}  // namespace io
-}  // namespace aapt
-
-#endif  // AAPT_IO_BIGBUFFERSTREAM_H
+}  // namespace android
\ No newline at end of file
diff --git a/tools/aapt2/io/FileStream.h b/libs/androidfw/include/androidfw/FileStream.h
similarity index 93%
rename from tools/aapt2/io/FileStream.h
rename to libs/androidfw/include/androidfw/FileStream.h
index 62d910f..fb84a91 100644
--- a/tools/aapt2/io/FileStream.h
+++ b/libs/androidfw/include/androidfw/FileStream.h
@@ -14,19 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_IO_FILESTREAM_H
-#define AAPT_IO_FILESTREAM_H
-
-#include "io/Io.h"
+#pragma once
 
 #include <memory>
 #include <string>
 
+#include "Streams.h"
 #include "android-base/macros.h"
 #include "android-base/unique_fd.h"
 
-namespace aapt {
-namespace io {
+namespace android {
 
 constexpr size_t kDefaultBufferCapacity = 4096u;
 
@@ -48,6 +45,8 @@
 
   std::string GetError() const override;
 
+  bool ReadFullyAtOffset(void* data, size_t byte_count, off64_t offset) override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(FileInputStream);
 
@@ -101,7 +100,4 @@
   size_t total_byte_count_ = 0u;
 };
 
-}  // namespace io
-}  // namespace aapt
-
-#endif  // AAPT_IO_FILESTREAM_H
+}  // namespace android
\ No newline at end of file
diff --git a/tools/aapt2/compile/Image.h b/libs/androidfw/include/androidfw/Image.h
similarity index 92%
rename from tools/aapt2/compile/Image.h
rename to libs/androidfw/include/androidfw/Image.h
index db0b945..c18c34c 100644
--- a/tools/aapt2/compile/Image.h
+++ b/libs/androidfw/include/androidfw/Image.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_COMPILE_IMAGE_H
-#define AAPT_COMPILE_IMAGE_H
+#pragma once
 
 #include <cstdint>
 #include <memory>
@@ -24,7 +23,7 @@
 
 #include "android-base/macros.h"
 
-namespace aapt {
+namespace android {
 
 /**
  * An in-memory image, loaded from disk, with pixels in RGBA_8888 format.
@@ -37,7 +36,7 @@
    * A `height` sized array of pointers, where each element points to a
    * `width` sized row of RGBA_8888 pixels.
    */
-  std::unique_ptr<uint8_t* []> rows;
+  std::unique_ptr<uint8_t*[]> rows;
 
   /**
    * The width of the image in RGBA_8888 pixels. This is int32_t because of
@@ -72,7 +71,8 @@
   int32_t end = 0;
 
   explicit Range() = default;
-  inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {}
+  inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {
+  }
 };
 
 inline bool operator==(const Range& left, const Range& right) {
@@ -93,7 +93,8 @@
 
   explicit Bounds() = default;
   inline explicit Bounds(int32_t l, int32_t t, int32_t r, int32_t b)
-      : left(l), top(t), right(r), bottom(b) {}
+      : left(l), top(t), right(r), bottom(b) {
+  }
 
   bool nonZero() const;
 };
@@ -103,8 +104,8 @@
 }
 
 inline bool operator==(const Bounds& left, const Bounds& right) {
-  return left.left == right.left && left.top == right.top &&
-         left.right == right.right && left.bottom == right.bottom;
+  return left.left == right.left && left.top == right.top && left.right == right.right &&
+         left.bottom == right.bottom;
 }
 
 /**
@@ -115,8 +116,7 @@
 class NinePatch {
  public:
   static std::unique_ptr<NinePatch> Create(uint8_t** rows, const int32_t width,
-                                           const int32_t height,
-                                           std::string* err_out);
+                                           const int32_t height, std::string* err_out);
 
   /**
    * Packs the RGBA_8888 data pointed to by pixel into a uint32_t
@@ -204,6 +204,4 @@
 ::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds);
 ::std::ostream& operator<<(::std::ostream& out, const NinePatch& nine_patch);
 
-}  // namespace aapt
-
-#endif /* AAPT_COMPILE_IMAGE_H */
+}  // namespace android
\ No newline at end of file
diff --git a/tools/aapt2/compile/Png.h b/libs/androidfw/include/androidfw/Png.h
similarity index 62%
rename from tools/aapt2/compile/Png.h
rename to libs/androidfw/include/androidfw/Png.h
index a8b7dd1..2ece43e 100644
--- a/tools/aapt2/compile/Png.h
+++ b/libs/androidfw/include/androidfw/Png.h
@@ -14,22 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_PNG_H
-#define AAPT_PNG_H
+#pragma once
 
-#include <iostream>
 #include <string>
 
+#include "BigBuffer.h"
+#include "IDiagnostics.h"
+#include "Image.h"
+#include "Source.h"
+#include "Streams.h"
 #include "android-base/macros.h"
-#include "androidfw/BigBuffer.h"
-#include "androidfw/IDiagnostics.h"
-#include "androidfw/Source.h"
-#include "compile/Image.h"
-#include "io/Io.h"
-#include "process/IResourceTableConsumer.h"
 
-namespace aapt {
-
+namespace android {
 // Size in bytes of the PNG signature.
 constexpr size_t kPngSignatureSize = 8u;
 
@@ -42,32 +38,36 @@
  */
 class Png {
  public:
-  explicit Png(android::IDiagnostics* diag) : mDiag(diag) {
+  explicit Png(IDiagnostics* diag) : mDiag(diag) {
   }
 
-  bool process(const android::Source& source, std::istream* input, android::BigBuffer* outBuffer,
+  bool process(const Source& source, std::istream* input, BigBuffer* outBuffer,
                const PngOptions& options);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Png);
 
-  android::IDiagnostics* mDiag;
+  IDiagnostics* mDiag;
 };
 
 /**
  * An InputStream that filters out unimportant PNG chunks.
  */
-class PngChunkFilter : public io::InputStream {
+class PngChunkFilter : public InputStream {
  public:
-  explicit PngChunkFilter(android::StringPiece data);
+  explicit PngChunkFilter(StringPiece data);
   virtual ~PngChunkFilter() = default;
 
   bool Next(const void** buffer, size_t* len) override;
   void BackUp(size_t count) override;
 
-  bool CanRewind() const override { return true; }
+  bool CanRewind() const override {
+    return true;
+  }
   bool Rewind() override;
-  size_t ByteCount() const override { return window_start_; }
+  size_t ByteCount() const override {
+    return window_start_;
+  }
 
   bool HadError() const override {
     return !error_msg_.empty();
@@ -81,26 +81,20 @@
 
   bool ConsumeWindow(const void** buffer, size_t* len);
 
-  android::StringPiece data_;
+  StringPiece data_;
   size_t window_start_ = 0;
   size_t window_end_ = 0;
   std::string error_msg_;
 };
-
 /**
  * Reads a PNG from the InputStream into memory as an RGBA Image.
  */
-std::unique_ptr<Image> ReadPng(IAaptContext* context, const android::Source& source,
-                               io::InputStream* in);
+std::unique_ptr<Image> ReadPng(InputStream* in, IDiagnostics* diag);
 
 /**
  * Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream
  * as a PNG.
  */
-bool WritePng(IAaptContext* context, const Image* image,
-              const NinePatch* nine_patch, io::OutputStream* out,
-              const PngOptions& options);
-
-}  // namespace aapt
-
-#endif  // AAPT_PNG_H
+bool WritePng(const Image* image, const NinePatch* nine_patch, OutputStream* out,
+              const PngOptions& options, IDiagnostics* diag, bool verbose);
+}  // namespace android
\ No newline at end of file
diff --git a/tools/aapt2/io/Io.h b/libs/androidfw/include/androidfw/Streams.h
similarity index 87%
rename from tools/aapt2/io/Io.h
rename to libs/androidfw/include/androidfw/Streams.h
index e1df23a6..89f1620 100644
--- a/tools/aapt2/io/Io.h
+++ b/libs/androidfw/include/androidfw/Streams.h
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_IO_IO_H
-#define AAPT_IO_IO_H
+#pragma once
 
 #include <string>
 
-namespace aapt {
-namespace io {
+namespace android {
 
 // InputStream interface that mimics protobuf's ZeroCopyInputStream,
 // with added error handling methods to better report issues.
@@ -41,21 +39,34 @@
   virtual void BackUp(size_t count) = 0;
 
   // Returns true if this InputStream can rewind. If so, Rewind() can be called.
-  virtual bool CanRewind() const { return false; };
+  virtual bool CanRewind() const {
+    return false;
+  };
 
   // Rewinds the stream to the beginning so it can be read again.
   // Returns true if the rewind succeeded.
   // This does nothing if CanRewind() returns false.
-  virtual bool Rewind() { return false; }
+  virtual bool Rewind() {
+    return false;
+  }
 
   // Returns the number of bytes that have been read from the stream.
   virtual size_t ByteCount() const = 0;
 
   // Returns an error message if HadError() returned true.
-  virtual std::string GetError() const { return {}; }
+  virtual std::string GetError() const {
+    return {};
+  }
 
   // Returns true if an error occurred. Errors are permanent.
   virtual bool HadError() const = 0;
+
+  virtual bool ReadFullyAtOffset(void* data, size_t byte_count, off64_t offset) {
+    (void)data;
+    (void)byte_count;
+    (void)offset;
+    return false;
+  }
 };
 
 // A sub-InputStream interface that knows the total size of its stream.
@@ -87,13 +98,12 @@
   virtual size_t ByteCount() const = 0;
 
   // Returns an error message if HadError() returned true.
-  virtual std::string GetError() const { return {}; }
+  virtual std::string GetError() const {
+    return {};
+  }
 
   // Returns true if an error occurred. Errors are permanent.
   virtual bool HadError() const = 0;
 };
 
-}  // namespace io
-}  // namespace aapt
-
-#endif /* AAPT_IO_IO_H */
+}  // namespace android
\ No newline at end of file
diff --git a/tools/aapt2/io/FileStream_test.cpp b/libs/androidfw/tests/FileStream_test.cpp
similarity index 95%
rename from tools/aapt2/io/FileStream_test.cpp
rename to libs/androidfw/tests/FileStream_test.cpp
index cc9cd28..9785975 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/libs/androidfw/tests/FileStream_test.cpp
@@ -14,20 +14,21 @@
  * limitations under the License.
  */
 
-#include "io/FileStream.h"
+#include "androidfw/FileStream.h"
+#include "androidfw/StringPiece.h"
 
 #include "android-base/file.h"
 #include "android-base/macros.h"
 
-#include "test/Test.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
 
 using ::android::StringPiece;
 using ::testing::Eq;
 using ::testing::NotNull;
 using ::testing::StrEq;
 
-namespace aapt {
-namespace io {
+namespace android {
 
 TEST(FileInputStreamTest, NextAndBackup) {
   std::string input = "this is a cool string";
@@ -123,5 +124,4 @@
   EXPECT_THAT(actual, StrEq(input));
 }
 
-}  // namespace io
-}  // namespace aapt
+}  // namespace android
diff --git a/tools/aapt2/compile/NinePatch_test.cpp b/libs/androidfw/tests/NinePatch_test.cpp
similarity index 69%
rename from tools/aapt2/compile/NinePatch_test.cpp
rename to libs/androidfw/tests/NinePatch_test.cpp
index f54bb2e..7ee8e9e 100644
--- a/tools/aapt2/compile/NinePatch_test.cpp
+++ b/libs/androidfw/tests/NinePatch_test.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "compile/Image.h"
+#include "androidfw/Image.h"
+#include "androidfw/ResourceTypes.h"
+#include "gtest/gtest.h"
 
-#include "test/Test.h"
-
-namespace aapt {
+namespace android {
 
 // Pixels are in RGBA_8888 packing.
 
@@ -33,16 +33,19 @@
 #define TRANS "\x00\x00\x00\x00"
 
 static uint8_t* k2x2[] = {
-    (uint8_t*)WHITE WHITE, (uint8_t*)WHITE WHITE,
+    (uint8_t*)WHITE WHITE,
+    (uint8_t*)WHITE WHITE,
 };
 
 static uint8_t* kMixedNeutralColor3x3[] = {
-    (uint8_t*)WHITE BLACK TRANS, (uint8_t*)TRANS RED TRANS,
+    (uint8_t*)WHITE BLACK TRANS,
+    (uint8_t*)TRANS RED TRANS,
     (uint8_t*)WHITE WHITE WHITE,
 };
 
 static uint8_t* kTransparentNeutralColor3x3[] = {
-    (uint8_t*)TRANS BLACK TRANS, (uint8_t*)BLACK RED BLACK,
+    (uint8_t*)TRANS BLACK TRANS,
+    (uint8_t*)BLACK RED BLACK,
     (uint8_t*)TRANS BLACK TRANS,
 };
 
@@ -66,55 +69,44 @@
 };
 
 static uint8_t* kPadding6x5[] = {
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE BLACK,
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE, (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE BLACK, (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
     (uint8_t*)WHITE WHITE BLACK BLACK WHITE WHITE,
 };
 
 static uint8_t* kLayoutBoundsWrongEdge3x3[] = {
-    (uint8_t*)WHITE RED WHITE, (uint8_t*)RED WHITE WHITE,
+    (uint8_t*)WHITE RED WHITE,
+    (uint8_t*)RED WHITE WHITE,
     (uint8_t*)WHITE WHITE WHITE,
 };
 
 static uint8_t* kLayoutBoundsNotEdgeAligned5x5[] = {
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE RED,
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE, (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+    (uint8_t*)WHITE WHITE WHITE WHITE RED,   (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
     (uint8_t*)WHITE WHITE RED WHITE WHITE,
 };
 
 static uint8_t* kLayoutBounds5x5[] = {
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE RED,
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE RED,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE, (uint8_t*)WHITE WHITE WHITE WHITE RED,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE, (uint8_t*)WHITE WHITE WHITE WHITE RED,
     (uint8_t*)WHITE RED WHITE RED WHITE,
 };
 
 static uint8_t* kAsymmetricLayoutBounds5x5[] = {
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE RED,
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE, (uint8_t*)WHITE WHITE WHITE WHITE RED,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE, (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
     (uint8_t*)WHITE RED WHITE WHITE WHITE,
 };
 
 static uint8_t* kPaddingAndLayoutBounds5x5[] = {
-    (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
-    (uint8_t*)WHITE WHITE WHITE WHITE RED,
-    (uint8_t*)WHITE WHITE WHITE WHITE BLACK,
-    (uint8_t*)WHITE WHITE WHITE WHITE RED,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE, (uint8_t*)WHITE WHITE WHITE WHITE RED,
+    (uint8_t*)WHITE WHITE WHITE WHITE BLACK, (uint8_t*)WHITE WHITE WHITE WHITE RED,
     (uint8_t*)WHITE RED BLACK RED WHITE,
 };
 
 static uint8_t* kColorfulImage5x5[] = {
-    (uint8_t*)WHITE BLACK WHITE BLACK WHITE,
-    (uint8_t*)BLACK RED BLUE GREEN WHITE,
-    (uint8_t*)BLACK RED GREEN GREEN WHITE,
-    (uint8_t*)WHITE TRANS BLUE GREEN WHITE,
+    (uint8_t*)WHITE BLACK WHITE BLACK WHITE, (uint8_t*)BLACK RED BLUE GREEN WHITE,
+    (uint8_t*)BLACK RED GREEN GREEN WHITE,   (uint8_t*)WHITE TRANS BLUE GREEN WHITE,
     (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
 };
 
@@ -145,33 +137,21 @@
 };
 
 static uint8_t* kOutlineOffsetTranslucent12x10[] = {
-    (uint8_t*)
-        WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
-    (uint8_t*)
-        WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
-    (uint8_t*)
-        WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
-    (uint8_t*)
-        WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
-    (uint8_t*)
-        WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
-    (uint8_t*)
-        WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
-    (uint8_t*)
-        WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
-    (uint8_t*)
-        WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
-    (uint8_t*)
-        WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
-    (uint8_t*)
-        WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+    (uint8_t*)WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+    (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+    (uint8_t*)WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+    (uint8_t*)WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+    (uint8_t*)WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+    (uint8_t*)WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+    (uint8_t*)WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+    (uint8_t*)WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+    (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+    (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
 };
 
 static uint8_t* kOutlineRadius5x5[] = {
-    (uint8_t*)WHITE BLACK BLACK BLACK WHITE,
-    (uint8_t*)BLACK TRANS GREEN TRANS WHITE,
-    (uint8_t*)BLACK GREEN GREEN GREEN WHITE,
-    (uint8_t*)BLACK TRANS GREEN TRANS WHITE,
+    (uint8_t*)WHITE BLACK BLACK BLACK WHITE, (uint8_t*)BLACK TRANS GREEN TRANS WHITE,
+    (uint8_t*)BLACK GREEN GREEN GREEN WHITE, (uint8_t*)BLACK TRANS GREEN TRANS WHITE,
     (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
 };
 
@@ -195,14 +175,12 @@
 
 TEST(NinePatchTest, TransparentNeutralColor) {
   std::string err;
-  EXPECT_NE(nullptr,
-            NinePatch::Create(kTransparentNeutralColor3x3, 3, 3, &err));
+  EXPECT_NE(nullptr, NinePatch::Create(kTransparentNeutralColor3x3, 3, 3, &err));
 }
 
 TEST(NinePatchTest, SingleStretchRegion) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kSingleStretch7x6, 7, 6, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kSingleStretch7x6, 7, 6, &err);
   ASSERT_NE(nullptr, nine_patch);
 
   ASSERT_EQ(1u, nine_patch->horizontal_stretch_regions.size());
@@ -214,8 +192,7 @@
 
 TEST(NinePatchTest, MultipleStretchRegions) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kMultipleStretch10x7, 10, 7, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kMultipleStretch10x7, 10, 7, &err);
   ASSERT_NE(nullptr, nine_patch);
 
   ASSERT_EQ(3u, nine_patch->horizontal_stretch_regions.size());
@@ -231,16 +208,14 @@
 
 TEST(NinePatchTest, InferPaddingFromStretchRegions) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kMultipleStretch10x7, 10, 7, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kMultipleStretch10x7, 10, 7, &err);
   ASSERT_NE(nullptr, nine_patch);
   EXPECT_EQ(Bounds(1, 0, 1, 0), nine_patch->padding);
 }
 
 TEST(NinePatchTest, Padding) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kPadding6x5, 6, 5, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kPadding6x5, 6, 5, &err);
   ASSERT_NE(nullptr, nine_patch);
   EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->padding);
 }
@@ -253,15 +228,13 @@
 
 TEST(NinePatchTest, LayoutBoundsMustTouchEdges) {
   std::string err;
-  EXPECT_EQ(nullptr,
-            NinePatch::Create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
+  EXPECT_EQ(nullptr, NinePatch::Create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
   EXPECT_FALSE(err.empty());
 }
 
 TEST(NinePatchTest, LayoutBounds) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kLayoutBounds5x5, 5, 5, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kLayoutBounds5x5, 5, 5, &err);
   ASSERT_NE(nullptr, nine_patch);
   EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->layout_bounds);
 
@@ -272,8 +245,7 @@
 
 TEST(NinePatchTest, PaddingAndLayoutBounds) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kPaddingAndLayoutBounds5x5, 5, 5, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kPaddingAndLayoutBounds5x5, 5, 5, &err);
   ASSERT_NE(nullptr, nine_patch);
   EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->padding);
   EXPECT_EQ(Bounds(1, 1, 1, 1), nine_patch->layout_bounds);
@@ -281,25 +253,20 @@
 
 TEST(NinePatchTest, RegionColorsAreCorrect) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kColorfulImage5x5, 5, 5, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kColorfulImage5x5, 5, 5, &err);
   ASSERT_NE(nullptr, nine_patch);
 
   std::vector<uint32_t> expected_colors = {
-      NinePatch::PackRGBA((uint8_t*)RED),
-      (uint32_t)android::Res_png_9patch::NO_COLOR,
-      NinePatch::PackRGBA((uint8_t*)GREEN),
-      (uint32_t)android::Res_png_9patch::TRANSPARENT_COLOR,
-      NinePatch::PackRGBA((uint8_t*)BLUE),
-      NinePatch::PackRGBA((uint8_t*)GREEN),
+      NinePatch::PackRGBA((uint8_t*)RED),   (uint32_t)android::Res_png_9patch::NO_COLOR,
+      NinePatch::PackRGBA((uint8_t*)GREEN), (uint32_t)android::Res_png_9patch::TRANSPARENT_COLOR,
+      NinePatch::PackRGBA((uint8_t*)BLUE),  NinePatch::PackRGBA((uint8_t*)GREEN),
   };
   EXPECT_EQ(expected_colors, nine_patch->region_colors);
 }
 
 TEST(NinePatchTest, OutlineFromOpaqueImage) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kOutlineOpaque10x10, 10, 10, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kOutlineOpaque10x10, 10, 10, &err);
   ASSERT_NE(nullptr, nine_patch);
   EXPECT_EQ(Bounds(2, 2, 2, 2), nine_patch->outline);
   EXPECT_EQ(0x000000ffu, nine_patch->outline_alpha);
@@ -308,8 +275,7 @@
 
 TEST(NinePatchTest, OutlineFromTranslucentImage) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kOutlineTranslucent10x10, 10, 10, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kOutlineTranslucent10x10, 10, 10, &err);
   ASSERT_NE(nullptr, nine_patch);
   EXPECT_EQ(Bounds(3, 3, 3, 3), nine_patch->outline);
   EXPECT_EQ(0x000000b3u, nine_patch->outline_alpha);
@@ -337,8 +303,7 @@
 
 TEST(NinePatchTest, OutlineRadius) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kOutlineRadius5x5, 5, 5, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kOutlineRadius5x5, 5, 5, &err);
   ASSERT_NE(nullptr, nine_patch);
   EXPECT_EQ(Bounds(0, 0, 0, 0), nine_patch->outline);
   EXPECT_EQ(3.4142f, nine_patch->outline_radius);
@@ -353,8 +318,7 @@
 
 TEST(NinePatchTest, SerializePngEndianness) {
   std::string err;
-  std::unique_ptr<NinePatch> nine_patch =
-      NinePatch::Create(kStretchAndPadding5x5, 5, 5, &err);
+  std::unique_ptr<NinePatch> nine_patch = NinePatch::Create(kStretchAndPadding5x5, 5, 5, &err);
   ASSERT_NE(nullptr, nine_patch);
 
   size_t len;
@@ -374,4 +338,4 @@
   EXPECT_TRUE(BigEndianOne(cursor + 12));
 }
 
-}  // namespace aapt
+}  // namespace android
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index a1d6ab5..b1cf96d 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -113,13 +113,13 @@
   }
 
   public final class GnssMeasurementRequest implements android.os.Parcelable {
-    method @NonNull public android.os.WorkSource getWorkSource();
+    method @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @NonNull public android.os.WorkSource getWorkSource();
     method public boolean isCorrelationVectorOutputsEnabled();
   }
 
   public static final class GnssMeasurementRequest.Builder {
     method @NonNull public android.location.GnssMeasurementRequest.Builder setCorrelationVectorOutputsEnabled(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.GnssMeasurementRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
+    method @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.GnssMeasurementRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
   }
 
   public final class GnssReflectingPlane implements android.os.Parcelable {
diff --git a/location/java/android/location/GnssMeasurementRequest.java b/location/java/android/location/GnssMeasurementRequest.java
index 65af392..2f0835a 100644
--- a/location/java/android/location/GnssMeasurementRequest.java
+++ b/location/java/android/location/GnssMeasurementRequest.java
@@ -17,11 +17,13 @@
 package android.location;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.location.flags.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.WorkSource;
@@ -121,6 +123,7 @@
      *
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE)
     @SystemApi
     public @NonNull WorkSource getWorkSource() {
         return mWorkSource;
@@ -298,6 +301,7 @@
          *
          * @hide
          */
+        @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE)
         @SystemApi
         @RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
         public @NonNull Builder setWorkSource(@Nullable WorkSource workSource) {
diff --git a/location/java/android/location/flags/gnss.aconfig b/location/java/android/location/flags/gnss.aconfig
index c471a27..b6055e8 100644
--- a/location/java/android/location/flags/gnss.aconfig
+++ b/location/java/android/location/flags/gnss.aconfig
@@ -5,4 +5,18 @@
     namespace: "location"
     description: "Flag for GNSS API for NavIC L1"
     bug: "302199306"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "gnss_call_stop_before_set_position_mode"
+    namespace: "location"
+    description: "Flag for calling stop() before setPositionMode()"
+    bug: "306874828"
+}
+
+flag {
+    name: "gnss_api_measurement_request_work_source"
+    namespace: "location"
+    description: "Flag for GnssMeasurementRequest WorkSource API"
+    bug: "295235160"
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d14775f..5c8758a 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -739,7 +739,7 @@
 
     oneway void stopLoudnessCodecUpdates(int piid);
 
-    oneway void addLoudnessCodecInfo(int piid, in LoudnessCodecInfo codecInfo);
+    oneway void addLoudnessCodecInfo(int piid, int mediaCodecHash, in LoudnessCodecInfo codecInfo);
 
     oneway void removeLoudnessCodecInfo(int piid, in LoudnessCodecInfo codecInfo);
 
diff --git a/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java
index 92f3372..de9d87c0 100644
--- a/media/java/android/media/LoudnessCodecConfigurator.java
+++ b/media/java/android/media/LoudnessCodecConfigurator.java
@@ -30,10 +30,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -47,9 +47,6 @@
  * parameter updates are defined by the CTA-2075 standard.
  * <p>A new object should be instantiated for each {@link AudioTrack} with the help
  * of {@link #create()} or {@link #create(Executor, OnLoudnessCodecUpdateListener)}.
- *
- * TODO: remove hide once API is final
- * @hide
  */
 @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
 public class LoudnessCodecConfigurator {
@@ -57,9 +54,6 @@
 
     /**
      * Listener used for receiving asynchronous loudness metadata updates.
-     *
-     * TODO: remove hide once API is final
-     * @hide
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
     public interface OnLoudnessCodecUpdateListener {
@@ -76,9 +70,6 @@
          * @return a Bundle which contains the original computed codecValues
          * aggregated with user edits. The platform will configure the associated
          * MediaCodecs with the returned Bundle params.
-         *
-         * TODO: remove hide once API is final
-         * @hide
          */
         @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
         @NonNull
@@ -112,9 +103,6 @@
      * Otherwise, use {@link #create(Executor, OnLoudnessCodecUpdateListener)}.
      *
      * @return the {@link LoudnessCodecConfigurator} instance
-     *
-     * TODO: remove hide once API is final
-     * @hide
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
     public static @NonNull LoudnessCodecConfigurator create() {
@@ -133,9 +121,6 @@
      * @param listener used for receiving updates
      *
      * @return the {@link LoudnessCodecConfigurator} instance
-     *
-     * TODO: remove hide once API is final
-     * @hide
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
     public static @NonNull LoudnessCodecConfigurator create(
@@ -200,12 +185,9 @@
      *                   method will have the effect of clearing the existing set
      *                   {@link AudioTrack} and will stop receiving asynchronous
      *                   loudness updates
-     *
-     * TODO: remove hide once API is final
-     * @hide
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
-    public void setAudioTrack(AudioTrack audioTrack) {
+    public void setAudioTrack(@Nullable AudioTrack audioTrack) {
         List<LoudnessCodecInfo> codecInfos;
         int piid = PLAYER_PIID_INVALID;
         int oldPiid = PLAYER_PIID_INVALID;
@@ -250,10 +232,11 @@
      * previously added.
      *
      * @param mediaCodec the codec to start receiving asynchronous loudness
-     *                   updates
-     *
-     * TODO: remove hide once API is final
-     * @hide
+     *                   updates. The codec has to be in a configured or started
+     *                   state in order to add it for loudness updates.
+     * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
+     *                                  does not contain loudness metadata or if it
+     *                                  was already added before
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void addMediaCodec(@NonNull MediaCodec mediaCodec) {
@@ -262,30 +245,31 @@
         int piid = PLAYER_PIID_INVALID;
         final LoudnessCodecInfo mcInfo = getCodecInfo(mc);
 
-        if (mcInfo != null) {
-            synchronized (mConfiguratorLock) {
-                final AtomicBoolean containsCodec = new AtomicBoolean(false);
-                Set<MediaCodec> newSet = mMediaCodecs.computeIfPresent(mcInfo, (info, codecSet) -> {
-                    containsCodec.set(!codecSet.add(mc));
-                    return codecSet;
-                });
-                if (newSet == null) {
-                    newSet = new HashSet<>();
-                    newSet.add(mc);
-                    mMediaCodecs.put(mcInfo, newSet);
-                }
-                if (containsCodec.get()) {
-                    Log.v(TAG, "Loudness configurator already added media codec " + mediaCodec);
-                    return;
-                }
-                if (mAudioTrack != null) {
-                    piid = mAudioTrack.getPlayerIId();
-                }
+        if (mcInfo == null) {
+            throw new IllegalArgumentException("Could not extract codec loudness information");
+        }
+        synchronized (mConfiguratorLock) {
+            final AtomicBoolean containsCodec = new AtomicBoolean(false);
+            Set<MediaCodec> newSet = mMediaCodecs.computeIfPresent(mcInfo, (info, codecSet) -> {
+                containsCodec.set(!codecSet.add(mc));
+                return codecSet;
+            });
+            if (newSet == null) {
+                newSet = new HashSet<>();
+                newSet.add(mc);
+                mMediaCodecs.put(mcInfo, newSet);
             }
+            if (containsCodec.get()) {
+                throw new IllegalArgumentException(
+                        "Loudness configurator already added " + mediaCodec);
+            }
+            if (mAudioTrack != null) {
+                piid = mAudioTrack.getPlayerIId();
+            }
+        }
 
-            if (piid != PLAYER_PIID_INVALID) {
-                mLcDispatcher.addLoudnessCodecInfo(piid, mcInfo);
-            }
+        if (piid != PLAYER_PIID_INVALID) {
+            mLcDispatcher.addLoudnessCodecInfo(piid, mediaCodec.hashCode(), mcInfo);
         }
     }
 
@@ -297,37 +281,44 @@
      * <p>No elements will be removed if the passed mediaCodec was not added before.
      *
      * @param mediaCodec the element to remove for receiving asynchronous updates
-     *
-     * TODO: remove hide once API is final
-     * @hide
+     * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
+     *                                  does not contain loudness metadata or if it
+     *                                  was not added before
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void removeMediaCodec(@NonNull MediaCodec mediaCodec) {
         int piid = PLAYER_PIID_INVALID;
         LoudnessCodecInfo mcInfo;
-        AtomicBoolean removed = new AtomicBoolean(false);
+        AtomicBoolean removedMc = new AtomicBoolean(false);
+        AtomicBoolean removeInfo = new AtomicBoolean(false);
 
         mcInfo = getCodecInfo(Objects.requireNonNull(mediaCodec,
                 "MediaCodec for removeMediaCodec cannot be null"));
 
-        if (mcInfo != null) {
-            synchronized (mConfiguratorLock) {
-                if (mAudioTrack != null) {
-                    piid = mAudioTrack.getPlayerIId();
+        if (mcInfo == null) {
+            throw new IllegalArgumentException("Could not extract codec loudness information");
+        }
+        synchronized (mConfiguratorLock) {
+            if (mAudioTrack != null) {
+                piid = mAudioTrack.getPlayerIId();
+            }
+            mMediaCodecs.computeIfPresent(mcInfo, (format, mcs) -> {
+                removedMc.set(mcs.remove(mediaCodec));
+                if (mcs.isEmpty()) {
+                    // remove the entry
+                    removeInfo.set(true);
+                    return null;
                 }
-                mMediaCodecs.computeIfPresent(mcInfo, (format, mcs) -> {
-                    removed.set(mcs.remove(mediaCodec));
-                    if (mcs.isEmpty()) {
-                        // remove the entry
-                        return null;
-                    }
-                    return mcs;
-                });
+                return mcs;
+            });
+            if (!removedMc.get()) {
+                throw new IllegalArgumentException(
+                        "Loudness configurator does not contain " + mediaCodec);
             }
+        }
 
-            if (piid != PLAYER_PIID_INVALID && removed.get()) {
-                mLcDispatcher.removeLoudnessCodecInfo(piid, mcInfo);
-            }
+        if (piid != PLAYER_PIID_INVALID && removeInfo.get()) {
+            mLcDispatcher.removeLoudnessCodecInfo(piid, mcInfo);
         }
     }
 
@@ -342,9 +333,6 @@
      *
      * @return the {@link Bundle} containing the current loudness parameters. Caller is
      * responsible to update the {@link MediaCodec}
-     *
-     * TODO: remove hide once API is final
-     * @hide
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
     @NonNull
@@ -375,9 +363,9 @@
     }
 
     /** @hide */
-    /*package*/ List<MediaCodec> getRegisteredMediaCodecList() {
+    /*package*/ Map<LoudnessCodecInfo, Set<MediaCodec>> getRegisteredMediaCodecs() {
         synchronized (mConfiguratorLock) {
-            return mMediaCodecs.values().stream().flatMap(Collection::stream).toList();
+            return mMediaCodecs;
         }
     }
 
@@ -397,40 +385,43 @@
             return null;
         }
 
-        final MediaFormat inputFormat = mediaCodec.getInputFormat();
-        final String mimeType = inputFormat.getString(MediaFormat.KEY_MIME);
-        if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mimeType)) {
-            // check both KEY_AAC_PROFILE and KEY_PROFILE as some codecs may only recognize one of
-            // these two keys
-            int aacProfile = -1;
-            int profile = -1;
-            try {
-                aacProfile = inputFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);
-            } catch (NullPointerException e) {
-                // does not contain KEY_AAC_PROFILE. do nothing
-            }
-            try {
-                profile = inputFormat.getInteger(MediaFormat.KEY_PROFILE);
-            } catch (NullPointerException e) {
-                // does not contain KEY_PROFILE. do nothing
-            }
-            if (aacProfile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE
-                    || profile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE) {
-                lci.metadataType = CODEC_METADATA_TYPE_MPEG_D;
+        try {
+            final MediaFormat inputFormat = mediaCodec.getInputFormat();
+            final String mimeType = inputFormat.getString(MediaFormat.KEY_MIME);
+            if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mimeType)) {
+                // check both KEY_AAC_PROFILE and KEY_PROFILE as some codecs may only recognize
+                // one of these two keys
+                int aacProfile = -1;
+                int profile = -1;
+                try {
+                    aacProfile = inputFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);
+                } catch (NullPointerException e) {
+                    // does not contain KEY_AAC_PROFILE. do nothing
+                }
+                try {
+                    profile = inputFormat.getInteger(MediaFormat.KEY_PROFILE);
+                } catch (NullPointerException e) {
+                    // does not contain KEY_PROFILE. do nothing
+                }
+                if (aacProfile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE
+                        || profile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE) {
+                    lci.metadataType = CODEC_METADATA_TYPE_MPEG_D;
+                } else {
+                    lci.metadataType = CODEC_METADATA_TYPE_MPEG_4;
+                }
             } else {
-                lci.metadataType = CODEC_METADATA_TYPE_MPEG_4;
+                Log.w(TAG, "MediaCodec mime type not supported for loudness annotation");
+                return null;
             }
-        } else {
-            Log.w(TAG, "MediaCodec mime type not supported for loudness annotation");
+
+            final MediaFormat outputFormat = mediaCodec.getOutputFormat();
+            lci.isDownmixing = outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
+                    < inputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "MediaCodec is not configured", e);
             return null;
         }
 
-        final MediaFormat outputFormat = mediaCodec.getOutputFormat();
-        lci.isDownmixing = outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
-                < inputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
-
-        lci.mediaCodecHashCode = mediaCodec.hashCode();
-
         return lci;
     }
 }
diff --git a/media/java/android/media/LoudnessCodecDispatcher.java b/media/java/android/media/LoudnessCodecDispatcher.java
index be881b1..b546a81 100644
--- a/media/java/android/media/LoudnessCodecDispatcher.java
+++ b/media/java/android/media/LoudnessCodecDispatcher.java
@@ -27,12 +27,16 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -52,6 +56,9 @@
         private final CallbackUtil.LazyListenerManager<OnLoudnessCodecUpdateListener>
                 mLoudnessListenerMgr = new CallbackUtil.LazyListenerManager<>();
 
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
         private final HashMap<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>
                 mConfiguratorListener = new HashMap<>();
 
@@ -66,38 +73,56 @@
 
         @Override
         public void dispatchLoudnessCodecParameterChange(int piid, PersistableBundle params) {
-            mLoudnessListenerMgr.callListeners(listener ->
+            if (DEBUG) {
+                Log.d(TAG, "dispatchLoudnessCodecParameterChange for piid " + piid
+                        + " persistable bundle: " + params);
+            }
+            mLoudnessListenerMgr.callListeners(listener -> {
+                synchronized (mLock) {
                     mConfiguratorListener.computeIfPresent(listener, (l, lcConfig) -> {
                         // send the appropriate bundle for the user to update
                         if (lcConfig.getAssignedTrackPiid() == piid) {
-                            final List<MediaCodec> mediaCodecs =
-                                    lcConfig.getRegisteredMediaCodecList();
-                            for (MediaCodec mediaCodec : mediaCodecs) {
-                                final String infoKey = Integer.toString(mediaCodec.hashCode());
+                            final Map<LoudnessCodecInfo, Set<MediaCodec>> mediaCodecsMap =
+                                    lcConfig.getRegisteredMediaCodecs();
+                            for (LoudnessCodecInfo codecInfo : mediaCodecsMap.keySet()) {
+                                final String infoKey = Integer.toString(codecInfo.hashCode());
+                                Bundle bundle = null;
                                 if (params.containsKey(infoKey)) {
-                                    Bundle bundle = new Bundle(
-                                            params.getPersistableBundle(infoKey));
-                                    if (DEBUG) {
-                                        Log.d(TAG,
-                                                "Received for piid " + piid + " bundle: " + bundle);
+                                    bundle = new Bundle(params.getPersistableBundle(infoKey));
+                                }
+
+                                final Set<MediaCodec> mediaCodecs = mediaCodecsMap.get(codecInfo);
+                                for (MediaCodec mediaCodec : mediaCodecs) {
+                                    final String mediaCodecKey = Integer.toString(
+                                            mediaCodec.hashCode());
+                                    if (bundle == null && !params.containsKey(mediaCodecKey)) {
+                                        continue;
+                                    }
+                                    boolean canBreak = false;
+                                    if (bundle == null) {
+                                        // key was set by media codec hash to update single codec
+                                        bundle = new Bundle(
+                                                params.getPersistableBundle(mediaCodecKey));
+                                        canBreak = true;
                                     }
                                     bundle =
                                             LoudnessCodecUpdatesDispatcherStub.filterLoudnessParams(
-                                                    l.onLoudnessCodecUpdate(mediaCodec, bundle));
-                                    if (DEBUG) {
-                                        Log.d(TAG, "User changed for piid " + piid
-                                                + " to filtered bundle: " + bundle);
-                                    }
+                                                    l.onLoudnessCodecUpdate(mediaCodec,
+                                                            bundle));
 
                                     if (!bundle.isDefinitelyEmpty()) {
                                         mediaCodec.setParameters(bundle);
                                     }
+                                    if (canBreak) {
+                                        break;
+                                    }
                                 }
                             }
                         }
-
                         return lcConfig;
-                    }));
+                    });
+                }
+            });
         }
 
         private static Bundle filterLoudnessParams(Bundle bundle) {
@@ -130,21 +155,33 @@
             mLoudnessListenerMgr.addListener(
                     executor, listener, "addLoudnessCodecListener",
                     () -> dispatcher);
-            mConfiguratorListener.put(listener, configurator);
+            synchronized (mLock) {
+                mConfiguratorListener.put(listener, configurator);
+            }
         }
 
         void removeLoudnessCodecListener(@NonNull LoudnessCodecConfigurator configurator) {
             Objects.requireNonNull(configurator);
 
-            for (Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e :
-                    mConfiguratorListener.entrySet()) {
-                if (e.getValue() == configurator) {
-                    final OnLoudnessCodecUpdateListener listener = e.getKey();
-                    mConfiguratorListener.remove(listener);
-                    mLoudnessListenerMgr.removeListener(listener, "removeLoudnessCodecListener");
-                    break;
+            OnLoudnessCodecUpdateListener listenerToRemove = null;
+            synchronized (mLock) {
+                Iterator<Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>> iterator =
+                        mConfiguratorListener.entrySet().iterator();
+                while (iterator.hasNext()) {
+                    Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e =
+                            iterator.next();
+                    if (e.getValue() == configurator) {
+                        final OnLoudnessCodecUpdateListener listener = e.getKey();
+                        iterator.remove();
+                        listenerToRemove = listener;
+                        break;
+                    }
                 }
             }
+            if (listenerToRemove != null) {
+                mLoudnessListenerMgr.removeListener(listenerToRemove,
+                        "removeLoudnessCodecListener");
+            }
         }
     }
 
@@ -202,9 +239,10 @@
     }
 
     /** @hide */
-    public void addLoudnessCodecInfo(int piid, @NonNull LoudnessCodecInfo mcInfo) {
+    public void addLoudnessCodecInfo(int piid, int mediaCodecHash,
+            @NonNull LoudnessCodecInfo mcInfo) {
         try {
-            mAudioService.addLoudnessCodecInfo(piid, mcInfo);
+            mAudioService.addLoudnessCodecInfo(piid, mediaCodecHash, mcInfo);
         }  catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/LoudnessCodecInfo.aidl b/media/java/android/media/LoudnessCodecInfo.aidl
index fd69517..0ac5646 100644
--- a/media/java/android/media/LoudnessCodecInfo.aidl
+++ b/media/java/android/media/LoudnessCodecInfo.aidl
@@ -23,7 +23,7 @@
  *
  * {@hide}
  */
-@JavaDerive(equals = true)
+@JavaDerive(equals = true, toString = true)
 parcelable LoudnessCodecInfo {
     /** Supported codec metadata types for loudness updates. */
     @Backing(type="int")
@@ -37,7 +37,6 @@
         CODEC_METADATA_TYPE_DTS_UHD = 6
     }
 
-    int mediaCodecHashCode;
     CodecMetadataType metadataType;
     boolean isDownmixing;
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/ad/TvAdManager.java b/media/java/android/media/tv/ad/TvAdManager.java
index aa5a290..2b52c4b 100644
--- a/media/java/android/media/tv/ad/TvAdManager.java
+++ b/media/java/android/media/tv/ad/TvAdManager.java
@@ -16,6 +16,10 @@
 
 package android.media.tv.ad;
 
+import android.annotation.FlaggedApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.media.tv.flags.Flags;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -23,14 +27,17 @@
 /**
  * Central system API to the overall client-side TV AD architecture, which arbitrates interaction
  * between applications and AD services.
- * @hide
  */
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
+@SystemService(Context.TV_AD_SERVICE)
 public class TvAdManager {
+    // TODO: implement more methods and unhide APIs.
     private static final String TAG = "TvAdManager";
 
     private final ITvAdManager mService;
     private final int mUserId;
 
+    /** @hide */
     public TvAdManager(ITvAdManager service, int userId) {
         mService = service;
         mUserId = userId;
@@ -38,6 +45,7 @@
 
     /**
      * The Session provides the per-session functionality of AD service.
+     * @hide
      */
     public static final class Session {
         private final IBinder mToken;
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index a73d1ff..018eaf6 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -5,4 +5,11 @@
     namespace: "media_tv"
     description: "Constants for standardizing broadcast visibility types."
     bug: "222402395"
+}
+
+flag {
+    name: "enable_ad_service_fw"
+    namespace: "media_tv"
+    description: "Enable the TV client-side AD framework."
+    bug: "303506816"
 }
\ No newline at end of file
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index 65a9799..c9e36b7 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -208,7 +208,7 @@
         verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
 
         mLcc.addMediaCodec(createAndConfigureMediaCodec());
-        verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+        verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
     }
 
     @Test
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index fe26dc3..991fe41 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -16,10 +16,12 @@
 
     dex_preopt: {
         profile_guided: true,
+        //TODO: b/312357299 - Update baseline profile
         profile: "profile.txt.prof",
     },
 
     static_libs: [
+        "CredentialManagerShared",
         "PlatformComposeCore",
         "androidx.activity_activity-compose",
         "androidx.appcompat_appcompat",
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index 42f1207..325d3f8 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -16,6 +16,7 @@
 
 package com.android.credentialmanager
 
+import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.credentials.ui.RequestInfo
@@ -27,10 +28,10 @@
 import com.android.credentialmanager.model.Request
 
 fun Intent.parse(
-    packageManager: PackageManager,
+    context: Context,
 ): Request {
-    return parseCancelUiRequest(packageManager)
-        ?: parseRequestInfo()
+    return parseCancelUiRequest(context.packageManager)
+        ?: parseRequestInfo(context)
 }
 
 fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? =
@@ -51,11 +52,11 @@
         }
     }
 
-fun Intent.parseRequestInfo(): Request =
+fun Intent.parseRequestInfo(context: Context): Request =
     requestInfo.let{ info ->
         when (info?.type) {
             RequestInfo.TYPE_CREATE -> Request.Create(info.token)
-            RequestInfo.TYPE_GET -> toGet()
+            RequestInfo.TYPE_GET -> toGet(context)
             else -> {
                 throw IllegalStateException("Unrecognized request type: ${info?.type}")
             }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
index 83183b5..3ef65b0 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -16,8 +16,8 @@
 
 package com.android.credentialmanager.client.impl
 
+import android.content.Context
 import android.content.Intent
-import android.content.pm.PackageManager
 import android.credentials.ui.BaseDialogResult
 import android.credentials.ui.UserSelectionDialogResult
 import android.os.Bundle
@@ -26,12 +26,13 @@
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.parse
 import com.android.credentialmanager.client.CredentialManagerClient
+import dagger.hilt.android.qualifiers.ApplicationContext
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import javax.inject.Inject
 
 class CredentialManagerClientImpl @Inject constructor(
-        private val packageManager: PackageManager,
+    @ApplicationContext private val context: Context,
 ) : CredentialManagerClient {
 
     private val _requests = MutableStateFlow<Request?>(null)
@@ -40,7 +41,7 @@
 
     override fun updateRequest(intent: Intent) {
         val request = intent.parse(
-            packageManager = packageManager,
+            context = context,
         )
         Log.d(TAG, "Request parsed: $request, client instance: $this")
         if (request is Request.Cancel || request is Request.Close) {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
new file mode 100644
index 0000000..f063074
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.ktx
+
+import android.app.slice.Slice
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.credentials.Credential
+import android.credentials.flags.Flags
+import android.credentials.ui.AuthenticationEntry
+import android.credentials.ui.Entry
+import android.credentials.ui.GetCredentialProviderData
+import android.graphics.drawable.Drawable
+import android.text.TextUtils
+import android.util.Log
+import androidx.activity.result.IntentSenderRequest
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.provider.Action
+import androidx.credentials.provider.AuthenticationAction
+import androidx.credentials.provider.CredentialEntry
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
+import androidx.credentials.provider.RemoteEntry
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
+import com.android.credentialmanager.TAG
+
+fun CredentialEntryInfo.getIntentSenderRequest(
+    isAutoSelected: Boolean = false
+): IntentSenderRequest? {
+    val entryIntent = fillInIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
+
+    return pendingIntent?.let{
+        IntentSenderRequest
+            .Builder(pendingIntent = it)
+            .setFillInIntent(entryIntent)
+            .build()
+    }
+}
+
+// Returns the list (potentially empty) of enabled provider.
+fun List<GetCredentialProviderData>.toProviderList(
+    context: Context,
+): List<ProviderInfo> {
+    val providerList: MutableList<ProviderInfo> = mutableListOf()
+    this.forEach {
+        val providerLabelAndIcon = getServiceLabelAndIcon(
+            context.packageManager,
+            it.providerFlattenedComponentName
+        ) ?: return@forEach
+        val (providerLabel, providerIcon) = providerLabelAndIcon
+        providerList.add(
+            ProviderInfo(
+                id = it.providerFlattenedComponentName,
+                icon = providerIcon,
+                displayName = providerLabel,
+                credentialEntryList = getCredentialOptionInfoList(
+                    providerId = it.providerFlattenedComponentName,
+                    providerLabel = providerLabel,
+                    credentialEntries = it.credentialEntries,
+                    context = context
+                ),
+                authenticationEntryList = getAuthenticationEntryList(
+                    it.providerFlattenedComponentName,
+                    providerLabel,
+                    providerIcon,
+                    it.authenticationEntries),
+                remoteEntry = getRemoteEntry(
+                    it.providerFlattenedComponentName,
+                    it.remoteEntry
+                ),
+                actionEntryList = getActionEntryList(
+                    it.providerFlattenedComponentName, it.actionChips, providerIcon
+                ),
+            )
+        )
+    }
+    return providerList
+}
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getCredentialOptionInfoList(
+    providerId: String,
+    providerLabel: String,
+    credentialEntries: List<Entry>,
+    context: Context,
+): List<CredentialEntryInfo> {
+    val result: MutableList<CredentialEntryInfo> = mutableListOf()
+    credentialEntries.forEach {
+        val credentialEntry = it.slice.credentialEntry
+        when (credentialEntry) {
+            is PasswordCredentialEntry -> {
+                result.add(
+                    CredentialEntryInfo(
+                    providerId = providerId,
+                    providerDisplayName = providerLabel,
+                    entryKey = it.key,
+                    entrySubkey = it.subkey,
+                    pendingIntent = credentialEntry.pendingIntent,
+                    fillInIntent = it.frameworkExtrasIntent,
+                    credentialType = CredentialType.PASSWORD,
+                    credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+                    userName = credentialEntry.username.toString(),
+                    displayName = credentialEntry.displayName?.toString(),
+                    icon = credentialEntry.icon.loadDrawable(context),
+                    shouldTintIcon = credentialEntry.isDefaultIcon,
+                    lastUsedTimeMillis = credentialEntry.lastUsedTime,
+                    isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+                            credentialEntry.autoSelectAllowedFromOption,
+                )
+                )
+            }
+            is PublicKeyCredentialEntry -> {
+                result.add(
+                    CredentialEntryInfo(
+                    providerId = providerId,
+                    providerDisplayName = providerLabel,
+                    entryKey = it.key,
+                    entrySubkey = it.subkey,
+                    pendingIntent = credentialEntry.pendingIntent,
+                    fillInIntent = it.frameworkExtrasIntent,
+                    credentialType = CredentialType.PASSKEY,
+                    credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+                    userName = credentialEntry.username.toString(),
+                    displayName = credentialEntry.displayName?.toString(),
+                    icon = credentialEntry.icon.loadDrawable(context),
+                    shouldTintIcon = credentialEntry.isDefaultIcon,
+                    lastUsedTimeMillis = credentialEntry.lastUsedTime,
+                    isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+                            credentialEntry.autoSelectAllowedFromOption,
+                )
+                )
+            }
+            is CustomCredentialEntry -> {
+                result.add(
+                    CredentialEntryInfo(
+                    providerId = providerId,
+                    providerDisplayName = providerLabel,
+                    entryKey = it.key,
+                    entrySubkey = it.subkey,
+                    pendingIntent = credentialEntry.pendingIntent,
+                    fillInIntent = it.frameworkExtrasIntent,
+                    credentialType = CredentialType.UNKNOWN,
+                    credentialTypeDisplayName =
+                    credentialEntry.typeDisplayName?.toString().orEmpty(),
+                    userName = credentialEntry.title.toString(),
+                    displayName = credentialEntry.subtitle?.toString(),
+                    icon = credentialEntry.icon.loadDrawable(context),
+                    shouldTintIcon = credentialEntry.isDefaultIcon,
+                    lastUsedTimeMillis = credentialEntry.lastUsedTime,
+                    isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+                            credentialEntry.autoSelectAllowedFromOption,
+                )
+                )
+            }
+            else -> Log.d(
+                TAG,
+                "Encountered unrecognized credential entry ${it.slice.spec?.type}"
+            )
+        }
+    }
+    return result
+}
+val Slice.credentialEntry: CredentialEntry?
+    get() =
+        try {
+            when (spec?.type) {
+                Credential.TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(this)!!
+                PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
+                    PublicKeyCredentialEntry.fromSlice(this)!!
+
+                else -> CustomCredentialEntry.fromSlice(this)!!
+            }
+        } catch (e: Exception) {
+            // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+            // password / passkey parsing attempt.
+            CustomCredentialEntry.fromSlice(this)
+        }
+
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getAuthenticationEntryList(
+    providerId: String,
+    providerDisplayName: String,
+    providerIcon: Drawable,
+    authEntryList: List<AuthenticationEntry>,
+): List<AuthenticationEntryInfo> {
+    val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
+    authEntryList.forEach { entry ->
+        val structuredAuthEntry =
+            AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
+
+        val title: String =
+            structuredAuthEntry.title.toString().ifEmpty { providerDisplayName }
+
+        result.add(
+            AuthenticationEntryInfo(
+            providerId = providerId,
+            entryKey = entry.key,
+            entrySubkey = entry.subkey,
+            pendingIntent = structuredAuthEntry.pendingIntent,
+            fillInIntent = entry.frameworkExtrasIntent,
+            title = title,
+            providerDisplayName = providerDisplayName,
+            icon = providerIcon,
+            isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
+            isLastUnlocked =
+            entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
+        )
+        )
+    }
+    return result
+}
+
+private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
+    if (remoteEntry == null) {
+        return null
+    }
+    val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
+        ?: return null
+    return RemoteEntryInfo(
+        providerId = providerId,
+        entryKey = remoteEntry.key,
+        entrySubkey = remoteEntry.subkey,
+        pendingIntent = structuredRemoteEntry.pendingIntent,
+        fillInIntent = remoteEntry.frameworkExtrasIntent,
+    )
+}
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getActionEntryList(
+    providerId: String,
+    actionEntries: List<Entry>,
+    providerIcon: Drawable,
+): List<ActionEntryInfo> {
+    val result: MutableList<ActionEntryInfo> = mutableListOf()
+    actionEntries.forEach {
+        val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
+        result.add(
+            ActionEntryInfo(
+            providerId = providerId,
+            entryKey = it.key,
+            entrySubkey = it.subkey,
+            pendingIntent = actionEntryUi.pendingIntent,
+            fillInIntent = it.frameworkExtrasIntent,
+            title = actionEntryUi.title.toString(),
+            icon = providerIcon,
+            subTitle = actionEntryUi.subtitle?.toString(),
+        )
+        )
+    }
+    return result
+}
+
+
+
+private fun getServiceLabelAndIcon(
+    pm: PackageManager,
+    providerFlattenedComponentName: String
+): Pair<String, Drawable>? {
+    var providerLabel: String? = null
+    var providerIcon: Drawable? = null
+    val component = ComponentName.unflattenFromString(providerFlattenedComponentName)
+    if (component == null) {
+        // Test data has only package name not component name.
+        // For test data usage only.
+        try {
+            val pkgInfo = if (Flags.instantAppsEnabled()) {
+                getPackageInfo(pm, providerFlattenedComponentName)
+            } else {
+                pm.getPackageInfo(
+                    providerFlattenedComponentName,
+                    PackageManager.PackageInfoFlags.of(0)
+                )
+            }
+            val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+            providerLabel =
+                applicationInfo.loadSafeLabel(
+                    pm, 0f,
+                    TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+                ).toString()
+            providerIcon = applicationInfo.loadIcon(pm)
+        } catch (e: Exception) {
+            Log.e(TAG, "Provider package info not found", e)
+        }
+    } else {
+        try {
+            val si = pm.getServiceInfo(component, PackageManager.ComponentInfoFlags.of(0))
+            providerLabel = si.loadSafeLabel(
+                pm, 0f,
+                TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+            ).toString()
+            providerIcon = si.loadIcon(pm)
+        } catch (e: PackageManager.NameNotFoundException) {
+            Log.e(TAG, "Provider service info not found", e)
+            // Added for mdoc use case where the provider may not need to register a service and
+            // instead only relies on the registration api.
+            try {
+                val pkgInfo = if (Flags.instantAppsEnabled()) {
+                    getPackageInfo(pm, providerFlattenedComponentName)
+                } else {
+                    pm.getPackageInfo(
+                        component.packageName,
+                        PackageManager.PackageInfoFlags.of(0)
+                    )
+                }
+                val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+                providerLabel =
+                    applicationInfo.loadSafeLabel(
+                        pm, 0f,
+                        TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+                    ).toString()
+                providerIcon = applicationInfo.loadIcon(pm)
+            } catch (e: Exception) {
+                Log.e(TAG, "Provider package info not found", e)
+            }
+        }
+    }
+    return if (providerLabel == null || providerIcon == null) {
+        Log.d(
+            TAG,
+            "Failed to load provider label/icon for provider $providerFlattenedComponentName"
+        )
+        null
+    } else {
+        Pair(providerLabel, providerIcon)
+    }
+}
+
+private fun getPackageInfo(
+    pm: PackageManager,
+    packageName: String
+): PackageInfo {
+    val packageManagerFlags = PackageManager.MATCH_INSTANT
+
+    return pm.getPackageInfo(
+        packageName,
+        PackageManager.PackageInfoFlags.of(
+            (packageManagerFlags).toLong())
+    )
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
deleted file mode 100644
index 3471070..0000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 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.0N
- *
- * 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.credentialmanager.ktx
-
-import androidx.activity.result.IntentSenderRequest
-import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
-import com.android.credentialmanager.model.Password
-
-fun Password.getIntentSenderRequest(
-    isAutoSelected: Boolean = false
-): IntentSenderRequest {
-    val entryIntent = entry.frameworkExtrasIntent
-    entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
-
-    return IntentSenderRequest.Builder(
-        pendingIntent = passwordCredentialEntry.pendingIntent
-    ).setFillInIntent(entryIntent).build()
-}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index d4bca2a..f1f1f7c 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -16,48 +16,18 @@
 
 package com.android.credentialmanager.mapper
 
+import android.content.Context
 import android.content.Intent
-import android.credentials.ui.Entry
-import androidx.credentials.provider.PasswordCredentialEntry
-import com.android.credentialmanager.factory.fromSlice
 import com.android.credentialmanager.ktx.getCredentialProviderDataList
 import com.android.credentialmanager.ktx.requestInfo
 import com.android.credentialmanager.ktx.resultReceiver
-import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.ktx.toProviderList
 import com.android.credentialmanager.model.Request
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableMap
 
-fun Intent.toGet(): Request.Get {
-    val credentialEntries = mutableListOf<Pair<String, Entry>>()
-    for (providerData in getCredentialProviderDataList) {
-        for (credentialEntry in providerData.credentialEntries) {
-            credentialEntries.add(
-                Pair(providerData.providerFlattenedComponentName, credentialEntry)
-            )
-        }
-    }
-
-    val passwordEntries = mutableListOf<Password>()
-    for ((providerId, entry) in credentialEntries) {
-        val slice = fromSlice(entry.slice)
-        if (slice is PasswordCredentialEntry) {
-            passwordEntries.add(
-                Password(
-                    providerId = providerId,
-                    entry = entry,
-                    passwordCredentialEntry = slice
-                )
-            )
-        }
-    }
-
+fun Intent.toGet(context: Context): Request.Get {
     return Request.Get(
         token = requestInfo?.token,
-        resultReceiver = this.resultReceiver,
-        providers = ImmutableMap.copyOf(
-            getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
-        ),
-        passwordEntries = ImmutableList.copyOf(passwordEntries)
+        resultReceiver = resultReceiver,
+        providerInfos = getCredentialProviderDataList.toProviderList(context)
     )
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
similarity index 93%
rename from packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
rename to packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
index cc92f60..3f85192 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model
 
 enum class CredentialType {
     UNKNOWN, PASSKEY, PASSWORD,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
similarity index 92%
rename from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
rename to packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
index ee36989..6d59f11 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model
 
 import android.app.PendingIntent
 import android.content.Intent
 
-open class BaseEntry (
+open class EntryInfo (
     val providerId: String,
     val entryKey: String,
     val entrySubkey: String,
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
deleted file mode 100644
index 2fe4fd5..0000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2023 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.0N
- *
- * 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.credentialmanager.model
-
-import android.credentials.ui.Entry
-import androidx.credentials.provider.PasswordCredentialEntry
-
-data class Password(
-    val providerId: String,
-    val entry: Entry,
-    val passwordCredentialEntry: PasswordCredentialEntry,
-)
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index 2289ed7..7636462 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -16,11 +16,9 @@
 
 package com.android.credentialmanager.model
 
-import android.credentials.ui.ProviderData
 import android.os.IBinder
 import android.os.ResultReceiver
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableMap
+import com.android.credentialmanager.model.get.ProviderInfo
 
 /**
  * Represents the request made by the CredentialManager API.
@@ -51,8 +49,7 @@
     data class Get(
         override val token: IBinder?,
         val resultReceiver: ResultReceiver?,
-        val providers: ImmutableMap<String, ProviderData>,
-        val passwordEntries: ImmutableList<Password>,
+        val providerInfos: List<ProviderInfo>,
     ) : Request(token)
     /**
      * Request to start the create credentials flow.
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt
new file mode 100644
index 0000000..d6189eb
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.creation
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+import java.time.Instant
+
+class CreateOptionInfo(
+    providerId: String,
+    entryKey: String,
+    entrySubkey: String,
+    pendingIntent: PendingIntent?,
+    fillInIntent: Intent?,
+    val userProviderDisplayName: String,
+    val profileIcon: Drawable?,
+    val passwordCount: Int?,
+    val passkeyCount: Int?,
+    val totalCredentialCount: Int?,
+    val lastUsedTime: Instant,
+    val footerDescription: String?,
+    val allowAutoSelect: Boolean,
+) : EntryInfo(
+    providerId,
+    entryKey,
+    entrySubkey,
+    pendingIntent,
+    fillInIntent,
+    shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
copy to packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
index ee36989..7ee50d7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
@@ -14,16 +14,23 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model.creation
 
 import android.app.PendingIntent
 import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
 
-open class BaseEntry (
-    val providerId: String,
-    val entryKey: String,
-    val entrySubkey: String,
-    val pendingIntent: PendingIntent?,
-    val fillInIntent: Intent?,
-    val shouldTerminateUiUponSuccessfulProviderResult: Boolean,
+class RemoteInfo(
+    providerId: String,
+    entryKey: String,
+    entrySubkey: String,
+    pendingIntent: PendingIntent?,
+    fillInIntent: Intent?,
+) : EntryInfo(
+    providerId,
+    entryKey,
+    entrySubkey,
+    pendingIntent,
+    fillInIntent,
+    shouldTerminateUiUponSuccessfulProviderResult = true,
 )
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt
new file mode 100644
index 0000000..d9eee86
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+
+class ActionEntryInfo(
+    providerId: String,
+    entryKey: String,
+    entrySubkey: String,
+    pendingIntent: PendingIntent?,
+    fillInIntent: Intent?,
+    val title: String,
+    val icon: Drawable,
+    val subTitle: String?,
+) : EntryInfo(
+    providerId,
+    entryKey,
+    entrySubkey,
+    pendingIntent,
+    fillInIntent,
+    shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt
new file mode 100644
index 0000000..01c394f
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+
+class AuthenticationEntryInfo(
+    providerId: String,
+    entryKey: String,
+    entrySubkey: String,
+    pendingIntent: PendingIntent?,
+    fillInIntent: Intent?,
+    val title: String,
+    val providerDisplayName: String,
+    val icon: Drawable,
+    // The entry had been unlocked and turned out to be empty. Used to determine whether to
+    // show "Tap to unlock" or "No sign-in info" for this entry.
+    val isUnlockedAndEmpty: Boolean,
+    // True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
+    val isLastUnlocked: Boolean,
+) : EntryInfo(
+    providerId,
+    entryKey, entrySubkey,
+    pendingIntent,
+    fillInIntent,
+    shouldTerminateUiUponSuccessfulProviderResult = false,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
new file mode 100644
index 0000000..9725881
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import java.time.Instant
+
+class CredentialEntryInfo(
+    providerId: String,
+    entryKey: String,
+    entrySubkey: String,
+    pendingIntent: PendingIntent?,
+    fillInIntent: Intent?,
+    /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
+    val credentialType: CredentialType,
+    /** Localized type value of this credential used for display purpose. */
+    val credentialTypeDisplayName: String,
+    val providerDisplayName: String,
+    val userName: String,
+    val displayName: String?,
+    val icon: Drawable?,
+    val shouldTintIcon: Boolean,
+    val lastUsedTimeMillis: Instant?,
+    val isAutoSelectable: Boolean,
+) : EntryInfo(
+    providerId,
+    entryKey,
+    entrySubkey,
+    pendingIntent,
+    fillInIntent,
+    shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt
new file mode 100644
index 0000000..6da4146
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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.credentialmanager.model.get
+
+import android.graphics.drawable.Drawable
+
+data class ProviderInfo(
+    /**
+     * Unique id (component name) of this provider.
+     * Not for display purpose - [displayName] should be used for ui rendering.
+     */
+    val id: String,
+    val icon: Drawable,
+    val displayName: String,
+    val credentialEntryList: List<CredentialEntryInfo>,
+    val authenticationEntryList: List<AuthenticationEntryInfo>,
+    val remoteEntry: RemoteEntryInfo?,
+    val actionEntryList: List<ActionEntryInfo>,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
copy to packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
index ee36989..a68bf74 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
@@ -14,16 +14,23 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model.get
 
 import android.app.PendingIntent
 import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
 
-open class BaseEntry (
-    val providerId: String,
-    val entryKey: String,
-    val entrySubkey: String,
-    val pendingIntent: PendingIntent?,
-    val fillInIntent: Intent?,
-    val shouldTerminateUiUponSuccessfulProviderResult: Boolean,
+class RemoteEntryInfo(
+    providerId: String,
+    entryKey: String,
+    entrySubkey: String,
+    pendingIntent: PendingIntent?,
+    fillInIntent: Intent?,
+) : EntryInfo(
+    providerId,
+    entryKey,
+    entrySubkey,
+    pendingIntent,
+    fillInIntent,
+    shouldTerminateUiUponSuccessfulProviderResult = true,
 )
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index bce86c4..6c5a984 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -28,7 +28,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.lifecycle.ViewModel
-import com.android.credentialmanager.common.BaseEntry
+import com.android.credentialmanager.model.EntryInfo
 import com.android.credentialmanager.common.Constants
 import com.android.credentialmanager.common.DialogState
 import com.android.credentialmanager.common.ProviderActivityResult
@@ -47,7 +47,7 @@
 data class UiState(
     val createCredentialUiState: CreateCredentialUiState?,
     val getCredentialUiState: GetCredentialUiState?,
-    val selectedEntry: BaseEntry? = null,
+    val selectedEntry: EntryInfo? = null,
     val providerActivityState: ProviderActivityState = ProviderActivityState.NOT_APPLICABLE,
     val dialogState: DialogState = DialogState.ACTIVE,
     // True if the UI has one and only one auto selectable entry. Its provider activity will be
@@ -115,12 +115,13 @@
         launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
     ) {
         val entry = uiState.selectedEntry
-        if (entry != null && entry.pendingIntent != null) {
+        val pendingIntent = entry?.pendingIntent
+        if (pendingIntent != null) {
             Log.d(Constants.LOG_TAG, "Launching provider activity")
             uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING)
             val entryIntent = entry.fillInIntent
             entryIntent?.putExtra(Constants.IS_AUTO_SELECTED_KEY, uiState.isAutoSelectFlow)
-            val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
+            val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent)
                 .setFillInIntent(entryIntent).build()
             try {
                 launcher.launch(intentSenderRequest)
@@ -201,7 +202,7 @@
     /**************************************************************************/
     /*****                      Get Flow Callbacks                        *****/
     /**************************************************************************/
-    fun getFlowOnEntrySelected(entry: BaseEntry) {
+    fun getFlowOnEntrySelected(entry: EntryInfo) {
         Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" +
             ", key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
         uiState = if (entry.pendingIntent != null) {
@@ -363,7 +364,7 @@
         )
     }
 
-    fun createFlowOnEntrySelected(selectedEntry: BaseEntry) {
+    fun createFlowOnEntrySelected(selectedEntry: EntryInfo) {
         val providerId = selectedEntry.providerId
         val entryKey = selectedEntry.entryKey
         val entrySubkey = selectedEntry.entrySubkey
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index f0fa6c5..fc3970d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -16,13 +16,10 @@
 
 package com.android.credentialmanager
 
-import android.app.slice.Slice
 import android.content.ComponentName
 import android.content.Context
 import android.content.pm.PackageInfo
 import android.content.pm.PackageManager
-import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
-import android.credentials.ui.AuthenticationEntry
 import android.credentials.ui.CreateCredentialProviderData
 import android.credentials.ui.DisabledProviderData
 import android.credentials.ui.Entry
@@ -32,36 +29,26 @@
 import android.text.TextUtils
 import android.util.Log
 import com.android.credentialmanager.common.Constants
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.CredentialType
 import com.android.credentialmanager.createflow.ActiveEntry
 import com.android.credentialmanager.createflow.CreateCredentialUiState
-import com.android.credentialmanager.createflow.CreateOptionInfo
+import com.android.credentialmanager.model.creation.CreateOptionInfo
 import com.android.credentialmanager.createflow.CreateScreenState
 import com.android.credentialmanager.createflow.DisabledProviderInfo
 import com.android.credentialmanager.createflow.EnabledProviderInfo
-import com.android.credentialmanager.createflow.RemoteInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
 import com.android.credentialmanager.createflow.RequestDisplayInfo
-import com.android.credentialmanager.getflow.ActionEntryInfo
-import com.android.credentialmanager.getflow.AuthenticationEntryInfo
-import com.android.credentialmanager.getflow.CredentialEntryInfo
-import com.android.credentialmanager.getflow.ProviderInfo
-import com.android.credentialmanager.getflow.RemoteEntryInfo
-import com.android.credentialmanager.getflow.TopBrandingContent
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.ktx.toProviderList
 import androidx.credentials.CreateCredentialRequest
 import androidx.credentials.CreateCustomCredentialRequest
 import androidx.credentials.CreatePasswordRequest
 import androidx.credentials.CreatePublicKeyCredentialRequest
-import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-import androidx.credentials.provider.Action
-import androidx.credentials.provider.AuthenticationAction
 import androidx.credentials.provider.CreateEntry
-import androidx.credentials.provider.CredentialEntry
-import androidx.credentials.provider.CustomCredentialEntry
-import androidx.credentials.provider.PasswordCredentialEntry
-import androidx.credentials.provider.PublicKeyCredentialEntry
 import androidx.credentials.provider.RemoteEntry
 import org.json.JSONObject
 import android.credentials.flags.Flags
+import com.android.credentialmanager.getflow.TopBrandingContent
 import java.time.Instant
 
 
@@ -179,43 +166,7 @@
         fun toProviderList(
             providerDataList: List<GetCredentialProviderData>,
             context: Context,
-        ): List<ProviderInfo> {
-            val providerList: MutableList<ProviderInfo> = mutableListOf()
-            providerDataList.forEach {
-                val providerLabelAndIcon = getServiceLabelAndIcon(
-                    context.packageManager,
-                    it.providerFlattenedComponentName
-                ) ?: return@forEach
-                val (providerLabel, providerIcon) = providerLabelAndIcon
-                providerList.add(
-                    ProviderInfo(
-                        id = it.providerFlattenedComponentName,
-                        icon = providerIcon,
-                        displayName = providerLabel,
-                        credentialEntryList = getCredentialOptionInfoList(
-                            providerId = it.providerFlattenedComponentName,
-                            providerLabel = providerLabel,
-                            credentialEntries = it.credentialEntries,
-                            context = context
-                        ),
-                        authenticationEntryList = getAuthenticationEntryList(
-                            it.providerFlattenedComponentName,
-                            providerLabel,
-                            providerIcon,
-                            it.authenticationEntries),
-                        remoteEntry = getRemoteEntry(
-                            it.providerFlattenedComponentName,
-                            it.remoteEntry
-                        ),
-                        actionEntryList = getActionEntryList(
-                            it.providerFlattenedComponentName, it.actionChips, providerIcon
-                        ),
-                    )
-                )
-            }
-            return providerList
-        }
-
+        ): List<ProviderInfo> = providerDataList.toProviderList(context)
         fun toRequestDisplayInfo(
             requestInfo: RequestInfo?,
             context: Context,
@@ -254,178 +205,6 @@
                 preferTopBrandingContent = preferTopBrandingContent,
             )
         }
-
-
-        /**
-         * Note: caller required handle empty list due to parsing error.
-         */
-        private fun getCredentialOptionInfoList(
-            providerId: String,
-            providerLabel: String,
-            credentialEntries: List<Entry>,
-            context: Context,
-        ): List<CredentialEntryInfo> {
-            val result: MutableList<CredentialEntryInfo> = mutableListOf()
-            credentialEntries.forEach {
-                val credentialEntry = parseCredentialEntryFromSlice(it.slice)
-                when (credentialEntry) {
-                    is PasswordCredentialEntry -> {
-                        result.add(CredentialEntryInfo(
-                            providerId = providerId,
-                            providerDisplayName = providerLabel,
-                            entryKey = it.key,
-                            entrySubkey = it.subkey,
-                            pendingIntent = credentialEntry.pendingIntent,
-                            fillInIntent = it.frameworkExtrasIntent,
-                            credentialType = CredentialType.PASSWORD,
-                            credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
-                            userName = credentialEntry.username.toString(),
-                            displayName = credentialEntry.displayName?.toString(),
-                            icon = credentialEntry.icon.loadDrawable(context),
-                            shouldTintIcon = credentialEntry.isDefaultIcon,
-                            lastUsedTimeMillis = credentialEntry.lastUsedTime,
-                            isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
-                                credentialEntry.autoSelectAllowedFromOption,
-                        ))
-                    }
-                    is PublicKeyCredentialEntry -> {
-                        result.add(CredentialEntryInfo(
-                            providerId = providerId,
-                            providerDisplayName = providerLabel,
-                            entryKey = it.key,
-                            entrySubkey = it.subkey,
-                            pendingIntent = credentialEntry.pendingIntent,
-                            fillInIntent = it.frameworkExtrasIntent,
-                            credentialType = CredentialType.PASSKEY,
-                            credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
-                            userName = credentialEntry.username.toString(),
-                            displayName = credentialEntry.displayName?.toString(),
-                            icon = credentialEntry.icon.loadDrawable(context),
-                            shouldTintIcon = credentialEntry.isDefaultIcon,
-                            lastUsedTimeMillis = credentialEntry.lastUsedTime,
-                            isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
-                                credentialEntry.autoSelectAllowedFromOption,
-                        ))
-                    }
-                    is CustomCredentialEntry -> {
-                        result.add(CredentialEntryInfo(
-                            providerId = providerId,
-                            providerDisplayName = providerLabel,
-                            entryKey = it.key,
-                            entrySubkey = it.subkey,
-                            pendingIntent = credentialEntry.pendingIntent,
-                            fillInIntent = it.frameworkExtrasIntent,
-                            credentialType = CredentialType.UNKNOWN,
-                            credentialTypeDisplayName =
-                            credentialEntry.typeDisplayName?.toString().orEmpty(),
-                            userName = credentialEntry.title.toString(),
-                            displayName = credentialEntry.subtitle?.toString(),
-                            icon = credentialEntry.icon.loadDrawable(context),
-                            shouldTintIcon = credentialEntry.isDefaultIcon,
-                            lastUsedTimeMillis = credentialEntry.lastUsedTime,
-                            isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
-                                credentialEntry.autoSelectAllowedFromOption,
-                        ))
-                    }
-                    else -> Log.d(
-                        Constants.LOG_TAG,
-                        "Encountered unrecognized credential entry ${it.slice.spec?.type}"
-                    )
-                }
-            }
-            return result
-        }
-
-        /**
-         * @hide
-         */
-        fun parseCredentialEntryFromSlice(slice: Slice): CredentialEntry? {
-            try {
-                when (slice.spec?.type) {
-                    TYPE_PASSWORD_CREDENTIAL -> return PasswordCredentialEntry.fromSlice(slice)!!
-                    TYPE_PUBLIC_KEY_CREDENTIAL -> return PublicKeyCredentialEntry.fromSlice(slice)!!
-                    else -> return CustomCredentialEntry.fromSlice(slice)!!
-                }
-            } catch (e: Exception) {
-                // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
-                // password / passkey parsing attempt.
-                return CustomCredentialEntry.fromSlice(slice)
-            }
-        }
-
-        /**
-         * Note: caller required handle empty list due to parsing error.
-         */
-        private fun getAuthenticationEntryList(
-            providerId: String,
-            providerDisplayName: String,
-            providerIcon: Drawable,
-            authEntryList: List<AuthenticationEntry>,
-        ): List<AuthenticationEntryInfo> {
-            val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
-            authEntryList.forEach { entry ->
-                val structuredAuthEntry =
-                    AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
-
-                val title: String =
-                    structuredAuthEntry.title.toString().ifEmpty { providerDisplayName }
-
-                result.add(AuthenticationEntryInfo(
-                    providerId = providerId,
-                    entryKey = entry.key,
-                    entrySubkey = entry.subkey,
-                    pendingIntent = structuredAuthEntry.pendingIntent,
-                    fillInIntent = entry.frameworkExtrasIntent,
-                    title = title,
-                    providerDisplayName = providerDisplayName,
-                    icon = providerIcon,
-                    isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
-                    isLastUnlocked =
-                    entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
-                ))
-            }
-            return result
-        }
-
-        private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
-            if (remoteEntry == null) {
-                return null
-            }
-            val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
-                ?: return null
-            return RemoteEntryInfo(
-                providerId = providerId,
-                entryKey = remoteEntry.key,
-                entrySubkey = remoteEntry.subkey,
-                pendingIntent = structuredRemoteEntry.pendingIntent,
-                fillInIntent = remoteEntry.frameworkExtrasIntent,
-            )
-        }
-
-        /**
-         * Note: caller required handle empty list due to parsing error.
-         */
-        private fun getActionEntryList(
-            providerId: String,
-            actionEntries: List<Entry>,
-            providerIcon: Drawable,
-        ): List<ActionEntryInfo> {
-            val result: MutableList<ActionEntryInfo> = mutableListOf()
-            actionEntries.forEach {
-                val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
-                result.add(ActionEntryInfo(
-                    providerId = providerId,
-                    entryKey = it.key,
-                    entrySubkey = it.subkey,
-                    pendingIntent = actionEntryUi.pendingIntent,
-                    fillInIntent = it.frameworkExtrasIntent,
-                    title = actionEntryUi.title.toString(),
-                    icon = providerIcon,
-                    subTitle = actionEntryUi.subtitle?.toString(),
-                ))
-            }
-            return result
-        }
     }
 }
 
@@ -686,7 +465,8 @@
             val result: MutableList<CreateOptionInfo> = mutableListOf()
             creationEntries.forEach {
                 val createEntry = CreateEntry.fromSlice(it.slice) ?: return@forEach
-                result.add(CreateOptionInfo(
+                result.add(
+                    CreateOptionInfo(
                     providerId = providerId,
                     entryKey = it.key,
                     entrySubkey = it.subkey,
@@ -705,7 +485,8 @@
                         it.hasHint("androidx.credentials.provider.createEntry.SLICE_HINT_AUTO_" +
                             "SELECT_ALLOWED")
                     }?.text == "true",
-                ))
+                )
+                )
             }
             return result.sortedWith(
                 compareByDescending { it.lastUsedTime }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 281696d..20d2f09 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -48,10 +48,11 @@
 import androidx.credentials.provider.PasswordCredentialEntry
 import androidx.credentials.provider.PublicKeyCredentialEntry
 import com.android.credentialmanager.GetFlowUtils
-import com.android.credentialmanager.getflow.CredentialEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.getflow.ProviderDisplayInfo
-import com.android.credentialmanager.getflow.ProviderInfo
+import com.android.credentialmanager.model.get.ProviderInfo
 import com.android.credentialmanager.getflow.toProviderDisplayInfo
+import com.android.credentialmanager.ktx.credentialEntry
 import org.json.JSONObject
 import java.util.concurrent.Executors
 
@@ -122,8 +123,7 @@
         val entryIconMap: MutableMap<String, Icon> = mutableMapOf()
         candidateProviderDataList.forEach { provider ->
             provider.credentialEntries.forEach { entry ->
-                val credentialEntry = GetFlowUtils.parseCredentialEntryFromSlice(entry.slice)
-                when (credentialEntry) {
+                when (val credentialEntry = entry.slice.credentialEntry) {
                     is PasswordCredentialEntry -> {
                         entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
                     }
@@ -172,11 +172,11 @@
     }
 
     private fun processProvidersForAutofillId(
-            filLRequest: FillRequest,
-            autofillId: AutofillId,
-            providerList: List<ProviderInfo>,
-            entryIconMap: Map<String, Icon>,
-            fillResponseBuilder: FillResponse.Builder
+        filLRequest: FillRequest,
+        autofillId: AutofillId,
+        providerList: List<ProviderInfo>,
+        entryIconMap: Map<String, Icon>,
+        fillResponseBuilder: FillResponse.Builder
     ): Boolean {
         if (providerList.isEmpty()) {
             return false
@@ -200,7 +200,8 @@
         providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
             val primaryEntry = it.sortedCredentialEntryList.first()
             val pendingIntent = primaryEntry.pendingIntent
-            if (pendingIntent == null || primaryEntry.fillInIntent == null) {
+            val fillInIntent = primaryEntry.fillInIntent
+            if (pendingIntent == null || fillInIntent == null) {
                 // FillInIntent will not be null because autofillId was retrieved from it.
                 Log.e(TAG, "PendingIntent was missing from the entry.")
                 return@usernameLoop
@@ -245,7 +246,7 @@
                                             presentationBuilder.build())
                                             .build())
                             .setAuthentication(pendingIntent.intentSender)
-                            .setAuthenticationExtras(primaryEntry.fillInIntent.extras)
+                            .setAuthenticationExtras(fillInIntent.extras)
                             .build())
             datasetAdded = true
         }
@@ -322,8 +323,8 @@
     }
 
     private fun copyProviderInfo(
-            providerInfo: ProviderInfo,
-            credentialList: List<CredentialEntryInfo>
+        providerInfo: ProviderInfo,
+        credentialList: List<CredentialEntryInfo>
     ): ProviderInfo {
         return ProviderInfo(
                 providerInfo.id,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index d45b6f6..14a9165 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -48,8 +48,8 @@
 import androidx.core.graphics.drawable.toBitmap
 import com.android.credentialmanager.CredentialSelectorViewModel
 import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
 import com.android.credentialmanager.common.ProviderActivityState
 import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
 import com.android.credentialmanager.common.ui.ActionButton
@@ -68,6 +68,8 @@
 import com.android.credentialmanager.common.ui.PasskeyBenefitRow
 import com.android.credentialmanager.common.ui.HeadlineText
 import com.android.credentialmanager.logging.CreateCredentialEvent
+import com.android.credentialmanager.model.creation.CreateOptionInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
 import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
 import com.android.internal.logging.UiEventLogger.UiEventEnum
 
@@ -259,15 +261,15 @@
 
 @Composable
 fun MoreOptionsSelectionCard(
-        requestDisplayInfo: RequestDisplayInfo,
-        enabledProviderList: List<EnabledProviderInfo>,
-        disabledProviderList: List<DisabledProviderInfo>?,
-        sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
-        onBackCreationSelectionButtonSelected: () -> Unit,
-        onOptionSelected: (ActiveEntry) -> Unit,
-        onDisabledProvidersSelected: () -> Unit,
-        onRemoteEntrySelected: (BaseEntry) -> Unit,
-        onLog: @Composable (UiEventEnum) -> Unit,
+    requestDisplayInfo: RequestDisplayInfo,
+    enabledProviderList: List<EnabledProviderInfo>,
+    disabledProviderList: List<DisabledProviderInfo>?,
+    sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+    onBackCreationSelectionButtonSelected: () -> Unit,
+    onOptionSelected: (ActiveEntry) -> Unit,
+    onDisabledProvidersSelected: () -> Unit,
+    onRemoteEntrySelected: (EntryInfo) -> Unit,
+    onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard(topAppBar = {
         MoreOptionTopAppBar(
@@ -378,14 +380,14 @@
 
 @Composable
 fun CreationSelectionCard(
-        requestDisplayInfo: RequestDisplayInfo,
-        enabledProviderList: List<EnabledProviderInfo>,
-        providerInfo: EnabledProviderInfo,
-        createOptionInfo: CreateOptionInfo,
-        onOptionSelected: (BaseEntry) -> Unit,
-        onConfirm: () -> Unit,
-        onMoreOptionsSelected: () -> Unit,
-        onLog: @Composable (UiEventEnum) -> Unit,
+    requestDisplayInfo: RequestDisplayInfo,
+    enabledProviderList: List<EnabledProviderInfo>,
+    providerInfo: EnabledProviderInfo,
+    createOptionInfo: CreateOptionInfo,
+    onOptionSelected: (EntryInfo) -> Unit,
+    onConfirm: () -> Unit,
+    onMoreOptionsSelected: () -> Unit,
+    onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard {
         item {
@@ -474,11 +476,11 @@
 
 @Composable
 fun ExternalOnlySelectionCard(
-        requestDisplayInfo: RequestDisplayInfo,
-        activeRemoteEntry: BaseEntry,
-        onOptionSelected: (BaseEntry) -> Unit,
-        onConfirm: () -> Unit,
-        onLog: @Composable (UiEventEnum) -> Unit,
+    requestDisplayInfo: RequestDisplayInfo,
+    activeRemoteEntry: EntryInfo,
+    onOptionSelected: (EntryInfo) -> Unit,
+    onConfirm: () -> Unit,
+    onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     SheetContainerCard {
         item { HeadlineIcon(imageVector = Icons.Outlined.QrCodeScanner) }
@@ -575,17 +577,14 @@
 @Composable
 fun PrimaryCreateOptionRow(
     requestDisplayInfo: RequestDisplayInfo,
-    entryInfo: BaseEntry,
-    onOptionSelected: (BaseEntry) -> Unit
+    entryInfo: EntryInfo,
+    onOptionSelected: (EntryInfo) -> Unit
 ) {
     Entry(
         onClick = { onOptionSelected(entryInfo) },
-        iconImageBitmap =
-        if (entryInfo is CreateOptionInfo && entryInfo.profileIcon != null) {
-            entryInfo.profileIcon.toBitmap().asImageBitmap()
-        } else {
-            requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()
-        },
+        iconImageBitmap = ((entryInfo as? CreateOptionInfo)?.profileIcon
+            ?: requestDisplayInfo.typeIcon)
+            .toBitmap().asImageBitmap(),
         shouldApplyIconImageBitmapTint = !(entryInfo is CreateOptionInfo &&
             entryInfo.profileIcon != null),
         entryHeadlineText = requestDisplayInfo.title,
@@ -627,32 +626,33 @@
         entryThirdLineText =
         if (requestDisplayInfo.type == CredentialType.PASSKEY ||
             requestDisplayInfo.type == CredentialType.PASSWORD) {
-            if (createOptionInfo.passwordCount != null &&
-                createOptionInfo.passkeyCount != null
-            ) {
+            val passwordCount = createOptionInfo.passwordCount
+            val passkeyCount = createOptionInfo.passkeyCount
+            if (passwordCount != null && passkeyCount != null) {
                 stringResource(
                     R.string.more_options_usage_passwords_passkeys,
-                    createOptionInfo.passwordCount,
-                    createOptionInfo.passkeyCount
+                    passwordCount,
+                    passkeyCount
                 )
-            } else if (createOptionInfo.passwordCount != null) {
+            } else if (passwordCount != null) {
                 stringResource(
                     R.string.more_options_usage_passwords,
-                    createOptionInfo.passwordCount
+                    passwordCount
                 )
-            } else if (createOptionInfo.passkeyCount != null) {
+            } else if (passkeyCount != null) {
                 stringResource(
                     R.string.more_options_usage_passkeys,
-                    createOptionInfo.passkeyCount
+                    passkeyCount
                 )
             } else {
                 null
             }
         } else {
-            if (createOptionInfo.totalCredentialCount != null) {
+            val totalCredentialCount = createOptionInfo.totalCredentialCount
+            if (totalCredentialCount != null) {
                 stringResource(
                     R.string.more_options_usage_credentials,
-                    createOptionInfo.totalCredentialCount
+                    totalCredentialCount
                 )
             } else {
                 null
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index e9e8c2e..8b0ba87 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -16,22 +16,21 @@
 
 package com.android.credentialmanager.createflow
 
-import android.app.PendingIntent
-import android.content.Intent
 import android.graphics.drawable.Drawable
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
-import java.time.Instant
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.creation.CreateOptionInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
 
 data class CreateCredentialUiState(
-  val enabledProviders: List<EnabledProviderInfo>,
-  val disabledProviders: List<DisabledProviderInfo>? = null,
-  val currentScreenState: CreateScreenState,
-  val requestDisplayInfo: RequestDisplayInfo,
-  val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
-  val activeEntry: ActiveEntry? = null,
-  val remoteEntry: RemoteInfo? = null,
-  val foundCandidateFromUserDefaultProvider: Boolean,
+    val enabledProviders: List<EnabledProviderInfo>,
+    val disabledProviders: List<DisabledProviderInfo>? = null,
+    val currentScreenState: CreateScreenState,
+    val requestDisplayInfo: RequestDisplayInfo,
+    val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+    val activeEntry: ActiveEntry? = null,
+    val remoteEntry: RemoteInfo? = null,
+    val foundCandidateFromUserDefaultProvider: Boolean,
 )
 
 internal fun isFlowAutoSelectable(
@@ -75,44 +74,6 @@
   displayName: String,
 ) : ProviderInfo(icon, id, displayName)
 
-class CreateOptionInfo(
-    providerId: String,
-    entryKey: String,
-    entrySubkey: String,
-    pendingIntent: PendingIntent?,
-    fillInIntent: Intent?,
-    val userProviderDisplayName: String,
-    val profileIcon: Drawable?,
-    val passwordCount: Int?,
-    val passkeyCount: Int?,
-    val totalCredentialCount: Int?,
-    val lastUsedTime: Instant,
-    val footerDescription: String?,
-    val allowAutoSelect: Boolean,
-) : BaseEntry(
-    providerId,
-    entryKey,
-    entrySubkey,
-    pendingIntent,
-    fillInIntent,
-    shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class RemoteInfo(
-  providerId: String,
-  entryKey: String,
-  entrySubkey: String,
-  pendingIntent: PendingIntent?,
-  fillInIntent: Intent?,
-) : BaseEntry(
-    providerId,
-    entryKey,
-    entrySubkey,
-    pendingIntent,
-    fillInIntent,
-    shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
 data class RequestDisplayInfo(
   val title: String,
   val subtitle: String?,
@@ -131,8 +92,8 @@
  * user selects a different entry on the more option page.
  */
 data class ActiveEntry (
-  val activeProvider: EnabledProviderInfo,
-  val activeEntryInfo: BaseEntry,
+    val activeProvider: EnabledProviderInfo,
+    val activeEntryInfo: EntryInfo,
 )
 
 /** The name of the current screen. */
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 72d030b..4ed84b9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -48,8 +48,9 @@
 import androidx.core.graphics.drawable.toBitmap
 import com.android.credentialmanager.CredentialSelectorViewModel
 import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
 import com.android.credentialmanager.common.ProviderActivityState
 import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
 import com.android.credentialmanager.common.ui.ActionButton
@@ -68,6 +69,10 @@
 import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
 import com.android.credentialmanager.common.ui.Snackbar
 import com.android.credentialmanager.logging.GetCredentialEvent
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
 import com.android.credentialmanager.userAndDisplayNameForPasskey
 import com.android.internal.logging.UiEventLogger.UiEventEnum
 
@@ -175,8 +180,8 @@
     requestDisplayInfo: RequestDisplayInfo,
     providerDisplayInfo: ProviderDisplayInfo,
     providerInfoList: List<ProviderInfo>,
-    activeEntry: BaseEntry?,
-    onEntrySelected: (BaseEntry) -> Unit,
+    activeEntry: EntryInfo?,
+    onEntrySelected: (EntryInfo) -> Unit,
     onConfirm: () -> Unit,
     onMoreOptionSelected: () -> Unit,
     onLog: @Composable (UiEventEnum) -> Unit,
@@ -358,7 +363,7 @@
 fun AllSignInOptionCard(
     providerInfoList: List<ProviderInfo>,
     providerDisplayInfo: ProviderDisplayInfo,
-    onEntrySelected: (BaseEntry) -> Unit,
+    onEntrySelected: (EntryInfo) -> Unit,
     onBackButtonClicked: () -> Unit,
     onCancel: () -> Unit,
     onLog: @Composable (UiEventEnum) -> Unit,
@@ -436,7 +441,7 @@
 @Composable
 fun ActionChips(
     providerInfoList: List<ProviderInfo>,
-    onEntrySelected: (BaseEntry) -> Unit,
+    onEntrySelected: (EntryInfo) -> Unit,
     isFirstSection: Boolean,
 ) {
     val actionChips = providerInfoList.flatMap { it.actionEntryList }
@@ -460,7 +465,7 @@
 @Composable
 fun RemoteEntryCard(
     remoteEntry: RemoteEntryInfo,
-    onEntrySelected: (BaseEntry) -> Unit,
+    onEntrySelected: (EntryInfo) -> Unit,
     isFirstSection: Boolean,
 ) {
     CredentialListSectionHeader(
@@ -486,7 +491,7 @@
 @Composable
 fun LockedCredentials(
     authenticationEntryList: List<AuthenticationEntryInfo>,
-    onEntrySelected: (BaseEntry) -> Unit,
+    onEntrySelected: (EntryInfo) -> Unit,
     isFirstSection: Boolean,
 ) {
     CredentialListSectionHeader(
@@ -508,7 +513,7 @@
 @Composable
 fun PerUserNameCredentials(
     perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
-    onEntrySelected: (BaseEntry) -> Unit,
+    onEntrySelected: (EntryInfo) -> Unit,
     isFirstSection: Boolean,
 ) {
     CredentialListSectionHeader(
@@ -532,7 +537,7 @@
 @Composable
 fun CredentialEntryRow(
     credentialEntryInfo: CredentialEntryInfo,
-    onEntrySelected: (BaseEntry) -> Unit,
+    onEntrySelected: (EntryInfo) -> Unit,
     enforceOneLine: Boolean = false,
     onTextLayout: (TextLayoutResult) -> Unit = {},
 ) {
@@ -571,7 +576,7 @@
 @Composable
 fun AuthenticationEntryRow(
     authenticationEntryInfo: AuthenticationEntryInfo,
-    onEntrySelected: (BaseEntry) -> Unit,
+    onEntrySelected: (EntryInfo) -> Unit,
     enforceOneLine: Boolean = false,
 ) {
     Entry(
@@ -596,7 +601,7 @@
 @Composable
 fun ActionEntryRow(
     actionEntryInfo: ActionEntryInfo,
-    onEntrySelected: (BaseEntry) -> Unit,
+    onEntrySelected: (EntryInfo) -> Unit,
 ) {
     ActionEntry(
         iconImageBitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 447a9d2..46bebc4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -16,21 +16,21 @@
 
 package com.android.credentialmanager.getflow
 
-import android.app.PendingIntent
-import android.content.Intent
 import android.graphics.drawable.Drawable
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
 import com.android.internal.util.Preconditions
 
-import java.time.Instant
-
 data class GetCredentialUiState(
     val providerInfoList: List<ProviderInfo>,
     val requestDisplayInfo: RequestDisplayInfo,
     val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
     val currentScreenState: GetScreenState = toGetScreenState(providerDisplayInfo),
-    val activeEntry: BaseEntry? = toActiveEntry(providerDisplayInfo),
+    val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
     val isNoAccount: Boolean = false,
 )
 
@@ -58,20 +58,6 @@
     return null
 }
 
-data class ProviderInfo(
-    /**
-     * Unique id (component name) of this provider.
-     * Not for display purpose - [displayName] should be used for ui rendering.
-     */
-    val id: String,
-    val icon: Drawable,
-    val displayName: String,
-    val credentialEntryList: List<CredentialEntryInfo>,
-    val authenticationEntryList: List<AuthenticationEntryInfo>,
-    val remoteEntry: RemoteEntryInfo?,
-    val actionEntryList: List<ActionEntryInfo>,
-)
-
 /** Display-centric data structure derived from the [ProviderInfo]. This abstraction is not grouping
  *  by the provider id but instead focuses on structures convenient for display purposes. */
 data class ProviderDisplayInfo(
@@ -84,87 +70,6 @@
     val remoteEntry: RemoteEntryInfo?
 )
 
-class CredentialEntryInfo(
-    providerId: String,
-    entryKey: String,
-    entrySubkey: String,
-    pendingIntent: PendingIntent?,
-    fillInIntent: Intent?,
-    /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
-    val credentialType: CredentialType,
-    /** Localized type value of this credential used for display purpose. */
-    val credentialTypeDisplayName: String,
-    val providerDisplayName: String,
-    val userName: String,
-    val displayName: String?,
-    val icon: Drawable?,
-    val shouldTintIcon: Boolean,
-    val lastUsedTimeMillis: Instant?,
-    val isAutoSelectable: Boolean,
-) : BaseEntry(
-    providerId,
-    entryKey,
-    entrySubkey,
-    pendingIntent,
-    fillInIntent,
-    shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class AuthenticationEntryInfo(
-    providerId: String,
-    entryKey: String,
-    entrySubkey: String,
-    pendingIntent: PendingIntent?,
-    fillInIntent: Intent?,
-    val title: String,
-    val providerDisplayName: String,
-    val icon: Drawable,
-    // The entry had been unlocked and turned out to be empty. Used to determine whether to
-    // show "Tap to unlock" or "No sign-in info" for this entry.
-    val isUnlockedAndEmpty: Boolean,
-    // True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
-    val isLastUnlocked: Boolean,
-) : BaseEntry(
-    providerId,
-    entryKey, entrySubkey,
-    pendingIntent,
-    fillInIntent,
-    shouldTerminateUiUponSuccessfulProviderResult = false,
-)
-
-class RemoteEntryInfo(
-    providerId: String,
-    entryKey: String,
-    entrySubkey: String,
-    pendingIntent: PendingIntent?,
-    fillInIntent: Intent?,
-) : BaseEntry(
-    providerId,
-    entryKey,
-    entrySubkey,
-    pendingIntent,
-    fillInIntent,
-    shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class ActionEntryInfo(
-    providerId: String,
-    entryKey: String,
-    entrySubkey: String,
-    pendingIntent: PendingIntent?,
-    fillInIntent: Intent?,
-    val title: String,
-    val icon: Drawable,
-    val subTitle: String?,
-) : BaseEntry(
-    providerId,
-    entryKey,
-    entrySubkey,
-    pendingIntent,
-    fillInIntent,
-    shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
 data class RequestDisplayInfo(
     val appName: String,
     val preferImmediatelyAvailableCredentials: Boolean,
@@ -218,8 +123,8 @@
     val remoteEntryList = mutableListOf<RemoteEntryInfo>()
     providerInfoList.forEach { providerInfo ->
         authenticationEntryList.addAll(providerInfo.authenticationEntryList)
-        if (providerInfo.remoteEntry != null) {
-            remoteEntryList.add(providerInfo.remoteEntry)
+        providerInfo.remoteEntry?.let {
+            remoteEntryList.add(it)
         }
         // There can only be at most one remote entry
         Preconditions.checkState(remoteEntryList.size <= 1)
@@ -260,11 +165,11 @@
 
 private fun toActiveEntry(
     providerDisplayInfo: ProviderDisplayInfo,
-): BaseEntry? {
+): EntryInfo? {
     val sortedUserNameToCredentialEntryList =
         providerDisplayInfo.sortedUserNameToCredentialEntryList
     val authenticationEntryList = providerDisplayInfo.authenticationEntryList
-    var activeEntry: BaseEntry? = null
+    var activeEntry: EntryInfo? = null
     if (sortedUserNameToCredentialEntryList
             .size == 1 && authenticationEntryList.isEmpty()
     ) {
@@ -302,17 +207,18 @@
                 return 1
             }
         }
-
+        val p0LastUsedTimeMillis = p0.lastUsedTimeMillis
+        val p1LastUsedTimeMillis = p1.lastUsedTimeMillis
         // Then order by last used timestamp
-        if (p0.lastUsedTimeMillis != null && p1.lastUsedTimeMillis != null) {
-            if (p0.lastUsedTimeMillis < p1.lastUsedTimeMillis) {
+        if (p0LastUsedTimeMillis != null && p1LastUsedTimeMillis != null) {
+            if (p0LastUsedTimeMillis < p1LastUsedTimeMillis) {
                 return 1
-            } else if (p0.lastUsedTimeMillis > p1.lastUsedTimeMillis) {
+            } else if (p0LastUsedTimeMillis > p1LastUsedTimeMillis) {
                 return -1
             }
-        } else if (p0.lastUsedTimeMillis != null) {
+        } else if (p0LastUsedTimeMillis != null) {
             return -1
-        } else if (p1.lastUsedTimeMillis != null) {
+        } else if (p1LastUsedTimeMillis != null) {
             return 1
         }
         return 0
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
index 6ededf3..d8a6019 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
@@ -1,29 +1,20 @@
 package com.android.credentialmanager.di
 
-import android.content.Context
-import android.content.pm.PackageManager
 import com.android.credentialmanager.client.CredentialManagerClient
 import com.android.credentialmanager.client.impl.CredentialManagerClientImpl
+import dagger.Binds
 import dagger.Module
-import dagger.Provides
 import dagger.hilt.InstallIn
-import dagger.hilt.android.qualifiers.ApplicationContext
 import dagger.hilt.components.SingletonComponent
 import javax.inject.Singleton
 
 @Module
 @InstallIn(SingletonComponent::class)
-internal object AppModule {
-    @Provides
+abstract class AppModule {
+    @Binds
     @Singleton
-    @JvmStatic
-    fun providePackageManager(@ApplicationContext context: Context): PackageManager =
-        context.packageManager
-
-    @Provides
-    @Singleton
-    @JvmStatic
-    fun provideCredentialManagerClient(packageManager: PackageManager): CredentialManagerClient =
-        CredentialManagerClientImpl(packageManager)
+    abstract fun provideCredentialManagerClient(
+        client: CredentialManagerClientImpl
+    ): CredentialManagerClient
 }
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index f2f878e..14b992a 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -23,8 +23,8 @@
     // TODO: b/301206470 returning a hard coded state for MVP
     if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
 
-    return if (providers.size == 1) {
-        if (passwordEntries.size == 1) {
+    return if (providerInfos.size == 1) {
+        if (providerInfos.first().credentialEntryList.size == 1) {
             CredentialSelectorUiState.Get.SingleProviderSinglePassword
         } else {
             TODO() // b/301206470 - Implement other get flows
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index c28df3e8..b64f581 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -76,7 +76,9 @@
             }
 
             SideEffect {
-                launcher.launch(state.intentSenderRequest)
+                state.intentSenderRequest?.let {
+                    launcher.launch(it)
+                }
             }
         }
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
index fb72c54..26bee1f 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -26,9 +26,9 @@
 import androidx.lifecycle.viewModelScope
 import com.android.credentialmanager.TAG
 import com.android.credentialmanager.ktx.getIntentSenderRequest
-import com.android.credentialmanager.model.Password
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.ui.model.PasswordUiModel
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -44,7 +44,7 @@
     private var initializeCalled = false
 
     private lateinit var requestGet: Request.Get
-    private lateinit var password: Password
+    private lateinit var entryInfo: CredentialEntryInfo
 
     private val _uiState =
         MutableStateFlow<SinglePasswordScreenUiState>(SinglePasswordScreenUiState.Idle)
@@ -63,14 +63,15 @@
                 _uiState.value = SinglePasswordScreenUiState.Error
             } else {
                 requestGet = request
-                if (requestGet.passwordEntries.isEmpty()) {
+
+                if (requestGet.providerInfos.all { it.credentialEntryList.isEmpty() }) {
                     Log.d(TAG, "Empty passwordEntries")
                     _uiState.value = SinglePasswordScreenUiState.Error
                 } else {
-                    password = requestGet.passwordEntries.first()
+                    entryInfo = requestGet.providerInfos.first().credentialEntryList.first()
                     _uiState.value = SinglePasswordScreenUiState.Loaded(
                         PasswordUiModel(
-                            email = password.passwordCredentialEntry.username.toString(),
+                            email = entryInfo.userName,
                         )
                     )
                 }
@@ -84,7 +85,7 @@
 
     fun onOKClick() {
         _uiState.value = SinglePasswordScreenUiState.PasswordSelected(
-            intentSenderRequest = password.getIntentSenderRequest()
+            intentSenderRequest = entryInfo.getIntentSenderRequest()
         )
     }
 
@@ -94,9 +95,9 @@
     ) {
         val userSelectionDialogResult = UserSelectionDialogResult(
             requestGet.token,
-            password.providerId,
-            password.entry.key,
-            password.entry.subkey,
+            entryInfo.providerId,
+            entryInfo.entryKey,
+            entryInfo.entrySubkey,
             if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
         )
         credentialManagerClient.sendResult(userSelectionDialogResult)
@@ -108,7 +109,7 @@
     data object Idle : SinglePasswordScreenUiState()
     data class Loaded(val passwordUiModel: PasswordUiModel) : SinglePasswordScreenUiState()
     data class PasswordSelected(
-        val intentSenderRequest: IntentSenderRequest
+        val intentSenderRequest: IntentSenderRequest?
     ) : SinglePasswordScreenUiState()
 
     data object Cancel : SinglePasswordScreenUiState()
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 38bd7d5..6213b34 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -49,6 +49,7 @@
         "androidx.fragment_fragment",
         "androidx.lifecycle_lifecycle-livedata",
         "androidx.lifecycle_lifecycle-extensions",
+        "android.content.pm.flags-aconfig-java",
     ],
 
     lint: {
@@ -75,6 +76,7 @@
         "androidx.fragment_fragment",
         "androidx.lifecycle_lifecycle-livedata",
         "androidx.lifecycle_lifecycle-extensions",
+        "android.content.pm.flags-aconfig-java",
     ],
     aaptflags: ["--product tablet"],
 
@@ -103,6 +105,7 @@
         "androidx.fragment_fragment",
         "androidx.lifecycle_lifecycle-livedata",
         "androidx.lifecycle_lifecycle-extensions",
+        "android.content.pm.flags-aconfig-java",
     ],
     aaptflags: ["--product tv"],
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index daedb1a..8d8254a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -298,7 +298,14 @@
                         broadcastIntent,
                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
 
-                session.commit(pendingIntent.getIntentSender());
+                try {
+                    session.commit(pendingIntent.getIntentSender());
+                } catch (Exception e) {
+                    Log.e(LOG_TAG, "Cannot install package: ", e);
+                    launchFailure(PackageInstaller.STATUS_FAILURE,
+                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+                    return;
+                }
                 mCancelButton.setEnabled(false);
                 setFinishOnTouchOutside(false);
             } else {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index e2107eb..4187058 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -16,12 +16,14 @@
 
 package com.android.packageinstaller;
 
+import static android.content.pm.Flags.usePiaV2;
 import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
 
 import android.Manifest;
 import android.app.Activity;
 import android.app.DialogFragment;
 import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -57,14 +59,21 @@
 
     private final boolean mLocalLOGV = false;
 
-    // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
-    private static final boolean USE_PIA_V2 = false;
-
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        if (USE_PIA_V2) {
+        mPackageManager = getPackageManager();
+        if (usePiaV2()) {
+            Log.i(TAG, "Using Pia V2");
+
+            mPackageManager.setComponentEnabledSetting(new ComponentName(this,
+                    com.android.packageinstaller.InstallEventReceiver.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+            mPackageManager.setComponentEnabledSetting(new ComponentName(this,
+                    com.android.packageinstaller.v2.model.InstallEventReceiver.class),
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
             Intent piaV2 = new Intent(getIntent());
             piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_NAME, getCallingPackage());
             piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
@@ -74,7 +83,6 @@
             finish();
             return;
         }
-        mPackageManager = getPackageManager();
         mUserManager = getSystemService(UserManager.class);
 
         Intent intent = getIntent();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 34062a4..ba627e9 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -17,8 +17,8 @@
 package com.android.packageinstaller;
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.content.pm.Flags.usePiaV2;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
 import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
 
 import android.Manifest;
@@ -81,9 +81,6 @@
     private String mPackageName;
     private DialogInfo mDialogInfo;
 
-    // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
-    private static final boolean USE_PIA_V2 = false;
-
     @Override
     public void onCreate(Bundle icicle) {
         getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
@@ -92,7 +89,18 @@
         // be stale, if e.g. the app was uninstalled while the activity was destroyed.
         super.onCreate(null);
 
-        if (USE_PIA_V2 && !isTv()) {
+        if (usePiaV2() && !isTv()) {
+            Log.i(TAG, "Using Pia V2");
+
+            PackageManager pm = getPackageManager();
+            pm.setComponentEnabledSetting(
+                new ComponentName(this, com.android.packageinstaller.UninstallEventReceiver.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+            pm.setComponentEnabledSetting(
+                new ComponentName(this,
+                    com.android.packageinstaller.v2.model.UninstallEventReceiver.class),
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
             boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
             Intent piaV2 = new Intent(getIntent());
             piaV2.putExtra(UninstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 80b944f..60bf48c 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -25,11 +25,11 @@
     alias(libs.plugins.kotlin.android) apply false
 }
 
-val androidTop : String = File(rootDir, "../../../../..").canonicalPath
+val androidTop: String = File(rootDir, "../../../../..").canonicalPath
 
 allprojects {
     extra["androidTop"] = androidTop
-    extra["jetpackComposeVersion"] = "1.6.0-alpha08"
+    extra["jetpackComposeVersion"] = "1.6.0-beta01"
 }
 
 subprojects {
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 4335173..acd90f3 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@
     api("androidx.slice:slice-builders:1.1.0-alpha02")
     api("androidx.slice:slice-core:1.1.0-alpha02")
     api("androidx.slice:slice-view:1.1.0-alpha02")
-    api("androidx.compose.material3:material3:1.2.0-alpha10")
+    api("androidx.compose.material3:material3:1.2.0-alpha11")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.7.5")
+    api("androidx.navigation:navigation-compose:2.7.4")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
     api("com.google.android.material:material:1.7.0-alpha03")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
index 26372b6..c395558 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
@@ -17,9 +17,7 @@
 package com.android.settingslib.spa.framework.theme
 
 import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material.ripple.LocalRippleTheme
-import androidx.compose.material.ripple.RippleAlpha
-import androidx.compose.material.ripple.RippleTheme
+import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
@@ -39,7 +37,7 @@
     MaterialTheme(colorScheme = colorScheme, typography = rememberSettingsTypography()) {
         CompositionLocalProvider(
             LocalColorScheme provides settingsColorScheme(isDarkTheme),
-            LocalRippleTheme provides SettingsRippleTheme,
+            LocalContentColor provides MaterialTheme.colorScheme.onSurface,
         ) {
             content()
         }
@@ -52,19 +50,3 @@
         @ReadOnlyComposable
         get() = LocalColorScheme.current
 }
-
-private object SettingsRippleTheme : RippleTheme {
-    @Composable
-    override fun defaultColor() = MaterialTheme.colorScheme.onSurface
-
-    @Composable
-    override fun rippleAlpha() = RippleAlpha
-}
-
-/** Alpha levels for all content. */
-private val RippleAlpha = RippleAlpha(
-    pressedAlpha = 0.48f,
-    focusedAlpha = 0.48f,
-    draggedAlpha = 0.32f,
-    hoveredAlpha = 0.16f,
-)
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index 0a2d9fc..e41126f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -140,12 +140,6 @@
         }
     }
 
-    @Override
-    public void setEnabled(boolean enabled) {
-        super.setEnabled(enabled);
-        setSwitchEnabled(enabled);
-    }
-
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
     public boolean isSwitchEnabled() {
         return mEnableSwitch;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 8b0f19d..29ea25e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -22,7 +22,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -237,15 +236,13 @@
     }
 
     private void updateDisabledState() {
-        boolean isEnabled = !(mDisabledByAdmin || mDisabledByAppOps);
         if (!(mPreference instanceof RestrictedTopLevelPreference)) {
-            mPreference.setEnabled(isEnabled);
+            mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
         }
 
-        Drawable icon = mPreference.getIcon();
-        if (!isEnabled && icon != null) {
-            Utils.convertToGrayscale(icon);
-            mPreference.setIcon(icon);
+        if (mPreference instanceof PrimarySwitchPreference) {
+            ((PrimarySwitchPreference) mPreference)
+                    .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
         }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f03263b..107d5f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -728,14 +728,4 @@
         return false;
     }
 
-    /**
-     *  Convert a drawable to grayscale drawable
-     */
-    public static void convertToGrayscale(@NonNull Drawable drawable) {
-        ColorMatrix matrix = new ColorMatrix();
-        matrix.setSaturation(0.0f);
-
-        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
-        drawable.setColorFilter(filter);
-    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8412cba..5c09b16 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -251,6 +251,8 @@
         Settings.Secure.STYLUS_HANDWRITING_ENABLED,
         Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
         Settings.Secure.CREDENTIAL_SERVICE,
-        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY
+        Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
+        Settings.Secure.EVEN_DIMMER_ACTIVATED,
+        Settings.Secure.EVEN_DIMMER_MIN_NITS
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 9197554..b0169a1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -110,6 +110,9 @@
         VALIDATORS.put(Secure.FONT_WEIGHT_ADJUSTMENT, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.REDUCE_BRIGHT_COLORS_LEVEL, PERCENTAGE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.EVEN_DIMMER_ACTIVATED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.EVEN_DIMMER_MIN_NITS,
+                new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE));
         VALIDATORS.put(Secure.TTS_DEFAULT_RATE, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TTS_DEFAULT_PITCH, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.TTS_DEFAULT_SYNTH, PACKAGE_NAME_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a509ba3..a978889 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2135,6 +2135,15 @@
                 Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED,
                 SecureSettingsProto.ENHANCED_VOICE_PRIVACY_ENABLED);
 
+        final long evenDimmerToken = p.start(SecureSettingsProto.EVEN_DIMMER);
+        dumpSetting(s, p,
+                Settings.Secure.EVEN_DIMMER_ACTIVATED,
+                SecureSettingsProto.EvenDimmer.EVEN_DIMMER_ACTIVATED);
+        dumpSetting(s, p,
+                Settings.Secure.EVEN_DIMMER_MIN_NITS,
+                SecureSettingsProto.EvenDimmer.EVEN_DIMMER_MIN_NITS);
+        p.end(evenDimmerToken);
+
         final long gestureToken = p.start(SecureSettingsProto.GESTURE);
         dumpSetting(s, p,
                 Settings.Secure.AWARE_ENABLED,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index efed8c3..85e8769 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -235,6 +235,7 @@
                     Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
                     Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH,
                     Settings.Global.DEVICE_DEMO_MODE,
+                    Settings.Global.DEVICE_IDLE_CONSTANTS,
                     Settings.Global.DISABLE_WINDOW_BLURS,
                     Settings.Global.BATTERY_SAVER_CONSTANTS,
                     Settings.Global.BATTERY_TIP_CONSTANTS,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d78038e..7cf562f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -242,184 +242,10 @@
 }
 
 filegroup {
-    name: "SystemUI-test-fakes",
-    srcs: [
-        /* Status bar fakes */
-        "tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt",
-
-        /* QS fakes */
-        "tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt",
-    ],
-    path: "tests/src",
-}
-
-filegroup {
-    name: "SystemUI-tests-robolectric-pilots",
-    srcs: [
-        /* Keyguard converted tests */
-        // data
-        "tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt",
-        "tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt",
-        // domain
-        "tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt",
-        "tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt",
-        "tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt",
-        "tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt",
-        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
-        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt",
-        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt",
-        "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt",
-        "tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt",
-        // ui
-        "tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt",
-        // Keyguard helper
-        "tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt",
-        "tests/src/com/android/systemui/dock/DockManagerFake.java",
-        "tests/src/com/android/systemui/dump/LogBufferHelper.kt",
-        "tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java",
-
-        /* Biometric converted tests */
-        "tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt",
-        "tests/src/com/android/systemui/biometrics/AuthControllerTest.java",
-        "tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java",
-        "tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt",
-        "tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt",
-        "tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt",
-        "tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java",
-        "tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java",
-        "tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java",
-        "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java",
-        "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java",
-        "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt",
-        "tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt",
-        "tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt",
-
-        /* Status bar wifi converted tests */
-        "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt",
-        "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt",
-
-        /* Bouncer UI tests */
-        "tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt",
-        "tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt",
-        "tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java",
-        "tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt",
-        "tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java",
-        "tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt",
-        "tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java",
-        "tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt",
-        "tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt",
-
-        /* Communal tests */
-        "tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt",
-        "tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt",
-        "tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt",
-
-        /* Dream tests */
-        "tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java",
-        "tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java",
-        "tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java",
-        "tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java",
-        "tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java",
-        "tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java",
-        "tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java",
-        "tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt",
-        "tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt",
-        "tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java",
-        "tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java",
-        "tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java",
-        "tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java",
-        "tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java",
-        "tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java",
-
-        /* Smartspace tests */
-        "tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt",
-        "tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt",
-        "tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt",
-        "tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt",
-
-        /* Quick Settings new pipeline converted tests */
-        "tests/src/com/android/systemui/qs/pipeline/data/**/*Test.kt",
-        "tests/src/com/android/systemui/qs/pipeline/domain/**/*Test.kt",
-        "tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt",
-        "tests/src/com/android/systemui/qs/tiles/base/**/*.kt",
-        "tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt",
-        "tests/src/com/android/systemui/qs/tiles/impl/**/*.kt",
-
-        /* Authentication */
-        "tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt",
-        "tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt",
-
-        /* Device entry */
-        "tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt",
-        "tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt",
-
-        /* Bouncer scene */
-        "tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt",
-        "tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt",
-        "tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt",
-        "tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt",
-        "tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt",
-        "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt",
-        "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt",
-
-        /* Lockscreen scene */
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt",
-
-        /* Shade scene */
-        "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt",
-        "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt",
-
-        /* Quick Settings scene */
-        "tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt",
-
-        /* Flexiglass / Scene framework tests */
-        "tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt",
-        "tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt",
-        "tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt",
-        "tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt",
-        "tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt",
-        "tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt",
-        "tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt",
-
-    ],
-    path: "tests/src",
-}
-
-filegroup {
     name: "SystemUI-tests-multivalent",
     srcs: [
         "multivalentTests/src/**/*.kt",
+        "multivalentTests/src/**/*.java",
     ],
     path: "multivalentTests/src",
 }
@@ -597,8 +423,6 @@
         "tests/robolectric/src/**/*.kt",
         "tests/robolectric/src/**/*.java",
         ":SystemUI-tests-utils",
-        ":SystemUI-test-fakes",
-        ":SystemUI-tests-robolectric-pilots",
         ":SystemUI-tests-multivalent",
     ],
     static_libs: [
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0480b9d..0c89a5d 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -126,24 +126,6 @@
           "exclude-annotation": "android.platform.test.annotations.Postsubmit"
         }
       ]
-    },
-    {
-      // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
-      "name": "SystemUIGoogleBiometricsScreenshotTests",
-      "options": [
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "android.platform.test.annotations.FlakyTest"
-        },
-        {
-          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
-        }
-      ]
     }
   ],
   
@@ -170,18 +152,6 @@
           "include-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
-    },
-    {
-      // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
-      "name": "SystemUIGoogleBiometricsScreenshotTests",
-      "options": [
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        },
-        {
-          "include-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
     }
   ]
 }
diff --git a/packages/SystemUI/aconfig/OWNERS b/packages/SystemUI/aconfig/OWNERS
index e1a7a0f..902ba90 100644
--- a/packages/SystemUI/aconfig/OWNERS
+++ b/packages/SystemUI/aconfig/OWNERS
@@ -1 +1,2 @@
 per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS
+per-file biometrics_framework.aconfig = file:/services/core/java/com/android/server/biometrics/OWNERS
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
new file mode 100644
index 0000000..5fd3b48
--- /dev/null
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.systemui"
+
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
+flag {
+    name: "bp_talkback"
+    namespace: "biometrics_framework"
+    description: "Adds talkback directional guidance when using UDFPS with biometric prompt"
+    bug: "310044658"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1762065..3e84597 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -164,3 +164,9 @@
    bug: "296122467"
 }
 
+flag {
+   name: "record_issue_qs_tile"
+   namespace: "systemui"
+   description: "Replace Record Trace QS Tile with expanded Record Issue QS Tile"
+   bug: "305049544"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 0b13383..eb06889 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -16,12 +16,10 @@
 
 package com.android.systemui.bouncer.ui.composable
 
+import android.view.ViewTreeObserver
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.imeAnimationTarget
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material3.LocalTextStyle
@@ -30,46 +28,56 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PasswordVisualTransformation
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
+import androidx.core.view.WindowInsetsCompat
 import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
 
 /** UI for the input part of a password-requiring version of the bouncer. */
-@OptIn(ExperimentalLayoutApi::class)
 @Composable
 internal fun PasswordBouncer(
     viewModel: PasswordBouncerViewModel,
     modifier: Modifier = Modifier,
 ) {
     val focusRequester = remember { FocusRequester() }
+    val isTextFieldFocusRequested by viewModel.isTextFieldFocusRequested.collectAsState()
+    LaunchedEffect(isTextFieldFocusRequested) {
+        if (isTextFieldFocusRequested) {
+            focusRequester.requestFocus()
+        }
+    }
+    val (isTextFieldFocused, onTextFieldFocusChanged) = remember { mutableStateOf(false) }
+    LaunchedEffect(isTextFieldFocused) {
+        viewModel.onTextFieldFocusChanged(isFocused = isTextFieldFocused)
+    }
+
     val password: String by viewModel.password.collectAsState()
     val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
     val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
 
-    val density = LocalDensity.current
-    val isImeVisible by rememberUpdatedState(WindowInsets.imeAnimationTarget.getBottom(density) > 0)
+    val isImeVisible by isSoftwareKeyboardVisible()
     LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) }
 
     DisposableEffect(Unit) {
         viewModel.onShown()
-
-        // When the UI comes up, request focus on the TextField to bring up the software keyboard.
-        focusRequester.requestFocus()
-
         onDispose { viewModel.onHidden() }
     }
 
@@ -104,16 +112,39 @@
                     onDone = { viewModel.onAuthenticateKeyPressed() },
                 ),
             modifier =
-                Modifier.focusRequester(focusRequester).drawBehind {
-                    drawLine(
-                        color = color,
-                        start = Offset(x = 0f, y = size.height - lineWidthPx),
-                        end = Offset(size.width, y = size.height - lineWidthPx),
-                        strokeWidth = lineWidthPx,
-                    )
-                },
+                Modifier.focusRequester(focusRequester)
+                    .onFocusChanged { onTextFieldFocusChanged(it.isFocused) }
+                    .drawBehind {
+                        drawLine(
+                            color = color,
+                            start = Offset(x = 0f, y = size.height - lineWidthPx),
+                            end = Offset(size.width, y = size.height - lineWidthPx),
+                            strokeWidth = lineWidthPx,
+                        )
+                    },
         )
 
         Spacer(Modifier.height(100.dp))
     }
 }
+
+/** Returns a [State] with `true` when the IME/keyboard is visible and `false` when it's not. */
+@Composable
+fun isSoftwareKeyboardVisible(): State<Boolean> {
+    val view = LocalView.current
+    val viewTreeObserver = view.viewTreeObserver
+
+    return produceState(
+        initialValue = false,
+        key1 = viewTreeObserver,
+    ) {
+        val listener =
+            ViewTreeObserver.OnGlobalLayoutListener {
+                value = view.rootWindowInsets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false
+            }
+
+        viewTreeObserver.addOnGlobalLayoutListener(listener)
+
+        awaitDispose { viewTreeObserver.removeOnGlobalLayoutListener(listener) }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 1e80732..83fb17f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -319,10 +319,10 @@
     @Test
     fun imeHiddenEvent_isTriggered() =
         testScope.runTest {
-            val imeHiddenEvent by collectLastValue(underTest.onImeHidden)
+            val imeHiddenEvent by collectLastValue(underTest.onImeHiddenByUser)
             runCurrent()
 
-            underTest.onImeHidden()
+            underTest.onImeHiddenByUser()
             runCurrent()
 
             assertThat(imeHiddenEvent).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
similarity index 82%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 63c992b..45c186d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -23,8 +23,6 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runTest
@@ -76,18 +74,4 @@
             underTest.onAuthenticateButtonClicked()
             assertThat(animateFailure).isFalse()
         }
-
-    @Test
-    fun onImeVisibilityChanged() =
-        testScope.runTest {
-            sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
-            val onImeHidden by collectLastValue(bouncerInteractor.onImeHidden)
-
-            underTest.onImeVisibilityChanged(true)
-            assertThat(onImeHidden).isNull()
-
-            underTest.onImeVisibilityChanged(false)
-            assertThat(onImeHidden).isNotNull()
-        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
similarity index 65%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 9b1e958..937c703 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -20,7 +20,9 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.res.R
 import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.scene.shared.model.SceneKey
@@ -43,7 +45,11 @@
 
     private val utils = SceneTestUtils(this)
     private val testScope = utils.testScope
-    private val authenticationInteractor = utils.authenticationInteractor()
+    private val authenticationRepository = utils.authenticationRepository
+    private val authenticationInteractor =
+        utils.authenticationInteractor(
+            repository = authenticationRepository,
+        )
     private val sceneInteractor = utils.sceneInteractor()
     private val bouncerInteractor =
         utils.bouncerInteractor(
@@ -207,6 +213,101 @@
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
         }
 
+    @Test
+    fun onImeVisibilityChanged_false_doesNothing() =
+        testScope.runTest {
+            val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+            assertThat(events).isEmpty()
+
+            underTest.onImeVisibilityChanged(isVisible = false)
+            assertThat(events).isEmpty()
+        }
+
+    @Test
+    fun onImeVisibilityChanged_falseAfterTrue_emitsOnImeHiddenByUserEvent() =
+        testScope.runTest {
+            val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+            assertThat(events).isEmpty()
+
+            underTest.onImeVisibilityChanged(isVisible = true)
+            assertThat(events).isEmpty()
+
+            underTest.onImeVisibilityChanged(isVisible = false)
+            assertThat(events).hasSize(1)
+
+            underTest.onImeVisibilityChanged(isVisible = true)
+            assertThat(events).hasSize(1)
+
+            underTest.onImeVisibilityChanged(isVisible = false)
+            assertThat(events).hasSize(2)
+        }
+
+    @Test
+    fun onImeVisibilityChanged_falseAfterTrue_whileThrottling_doesNothing() =
+        testScope.runTest {
+            val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+            assertThat(events).isEmpty()
+            underTest.onImeVisibilityChanged(isVisible = true)
+            setThrottling(true)
+
+            underTest.onImeVisibilityChanged(isVisible = false)
+
+            assertThat(events).isEmpty()
+        }
+
+    @Test
+    fun isTextFieldFocusRequested_initiallyTrue() =
+        testScope.runTest {
+            val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+            assertThat(isTextFieldFocusRequested).isTrue()
+        }
+
+    @Test
+    fun isTextFieldFocusRequested_focusGained_becomesFalse() =
+        testScope.runTest {
+            val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+
+            underTest.onTextFieldFocusChanged(isFocused = true)
+
+            assertThat(isTextFieldFocusRequested).isFalse()
+        }
+
+    @Test
+    fun isTextFieldFocusRequested_focusLost_becomesTrue() =
+        testScope.runTest {
+            val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+            underTest.onTextFieldFocusChanged(isFocused = true)
+
+            underTest.onTextFieldFocusChanged(isFocused = false)
+
+            assertThat(isTextFieldFocusRequested).isTrue()
+        }
+
+    @Test
+    fun isTextFieldFocusRequested_focusLostWhileThrottling_staysFalse() =
+        testScope.runTest {
+            val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+            underTest.onTextFieldFocusChanged(isFocused = true)
+            setThrottling(true)
+
+            underTest.onTextFieldFocusChanged(isFocused = false)
+
+            assertThat(isTextFieldFocusRequested).isFalse()
+        }
+
+    @Test
+    fun isTextFieldFocusRequested_throttlingCountdownEnds_becomesTrue() =
+        testScope.runTest {
+            val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+            underTest.onTextFieldFocusChanged(isFocused = true)
+            setThrottling(true)
+            underTest.onTextFieldFocusChanged(isFocused = false)
+
+            setThrottling(false)
+
+            assertThat(isTextFieldFocusRequested).isTrue()
+        }
+
     private fun TestScope.switchToScene(toScene: SceneKey) {
         val currentScene by collectLastValue(sceneInteractor.desiredScene)
         val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
@@ -226,6 +327,35 @@
         switchToScene(SceneKey.Bouncer)
     }
 
+    private suspend fun TestScope.setThrottling(
+        isThrottling: Boolean,
+        failedAttemptCount: Int = 5,
+    ) {
+        if (isThrottling) {
+            repeat(failedAttemptCount) {
+                authenticationRepository.reportAuthenticationAttempt(false)
+            }
+            val remainingTimeMs = 30_000
+            authenticationRepository.setThrottleDuration(remainingTimeMs)
+            authenticationRepository.setThrottling(
+                AuthenticationThrottlingModel(
+                    failedAttemptCount = failedAttemptCount,
+                    remainingMs = remainingTimeMs,
+                )
+            )
+        } else {
+            authenticationRepository.reportAuthenticationAttempt(true)
+            authenticationRepository.setThrottling(
+                AuthenticationThrottlingModel(
+                    failedAttemptCount = failedAttemptCount,
+                    remainingMs = 0,
+                )
+            )
+        }
+
+        runCurrent()
+    }
+
     companion object {
         private const val ENTER_YOUR_PASSWORD = "Enter your password"
         private const val WRONG_PASSWORD = "Wrong password"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
similarity index 75%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 02db0d7..a613ad8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -29,17 +29,16 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.wallet.controller.QuickAccessWalletController
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -48,7 +47,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@@ -56,26 +54,32 @@
     @Mock private lateinit var walletController: QuickAccessWalletController
     @Mock private lateinit var activityStarter: ActivityStarter
 
+    private lateinit var testDispatcher: TestDispatcher
+    private lateinit var testScope: TestScope
+
     private lateinit var underTest: QuickAccessWalletKeyguardQuickAffordanceConfig
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+
         underTest =
             QuickAccessWalletKeyguardQuickAffordanceConfig(
                 context,
+                testDispatcher,
                 walletController,
                 activityStarter,
             )
     }
 
     @Test
-    fun affordance_keyguardShowing_hasWalletCard_visibleModel() = runBlockingTest {
+    fun affordance_keyguardShowing_hasWalletCard_visibleModel() = testScope.runTest {
         setUpState()
-        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
         val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
         assertThat(visibleModel.icon)
@@ -88,77 +92,61 @@
                         ),
                 )
             )
-        job.cancel()
     }
 
     @Test
-    fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
-        runTest(UnconfinedTestDispatcher()) {
+    fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() = testScope.runTest {
             setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
-            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.lockScreenState)
 
             assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-            job.cancel()
         }
 
     @Test
-    fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
-        runTest(UnconfinedTestDispatcher()) {
-            setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
-            var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+    fun affordance_keyguardShowing_hasPaymentCard_visibleModel() = testScope.runTest {
+        setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
 
-            val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
-            val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
-            assertThat(visibleModel.icon)
-                .isEqualTo(
-                    Icon.Loaded(
-                        drawable = ICON,
-                        contentDescription =
-                            ContentDescription.Resource(
-                                res = R.string.accessibility_wallet_button,
-                            ),
-                    )
+        val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+        assertThat(visibleModel.icon)
+            .isEqualTo(
+                Icon.Loaded(
+                    drawable = ICON,
+                    contentDescription =
+                        ContentDescription.Resource(
+                            res = R.string.accessibility_wallet_button,
+                        ),
                 )
-            job.cancel()
-        }
+            )
+    }
 
     @Test
-    fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest {
+    fun affordance_walletFeatureNotEnabled_modelIsNone() = testScope.runTest {
         setUpState(isWalletFeatureAvailable = false)
-        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
         assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-
-        job.cancel()
     }
 
     @Test
-    fun affordance_queryNotSuccessful_modelIsNone() = runBlockingTest {
+    fun affordance_queryNotSuccessful_modelIsNone() = testScope.runTest {
         setUpState(isWalletQuerySuccessful = false)
-        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
         assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-
-        job.cancel()
     }
 
     @Test
-    fun affordance_noSelectedCard_modelIsNone() = runBlockingTest {
+    fun affordance_noSelectedCard_modelIsNone() = testScope.runTest {
         setUpState(hasSelectedCard = false)
-        var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
 
-        val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+        val latest by collectLastValue(underTest.lockScreenState)
 
         assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-
-        job.cancel()
     }
 
     @Test
@@ -179,7 +167,7 @@
     }
 
     @Test
-    fun getPickerScreenState_default() = runTest {
+    fun getPickerScreenState_default() = testScope.runTest {
         setUpState()
 
         assertThat(underTest.getPickerScreenState())
@@ -187,7 +175,7 @@
     }
 
     @Test
-    fun getPickerScreenState_unavailable() = runTest {
+    fun getPickerScreenState_unavailable() = testScope.runTest {
         setUpState(
             isWalletServiceAvailable = false,
         )
@@ -197,7 +185,7 @@
     }
 
     @Test
-    fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = runTest {
+    fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = testScope.runTest {
         setUpState(
             isWalletFeatureAvailable = false,
         )
@@ -207,7 +195,7 @@
     }
 
     @Test
-    fun getPickerScreenState_disabledWhenThereIsNoCard() = runTest {
+    fun getPickerScreenState_disabledWhenThereIsNoCard() = testScope.runTest {
         setUpState(
             hasSelectedCard = false,
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index fab290d..7b2ac90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs.tiles.impl.flashlight.domain
 
-import android.graphics.drawable.Drawable
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -26,7 +25,6 @@
 import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import org.junit.Test
@@ -37,7 +35,7 @@
 class FlashlightMapperTest : SysuiTestCase() {
     private val kosmos = Kosmos()
     private val qsTileConfig = kosmos.qsFlashlightTileConfig
-    private val mapper by lazy { FlashlightMapper(context) }
+    private val mapper by lazy { FlashlightMapper(context.orCreateTestableResources.resources) }
 
     @Test
     fun mapsDisabledDataToInactiveState() {
@@ -58,12 +56,7 @@
 
     @Test
     fun mapsEnabledDataToOnIconState() {
-        val fakeDrawable = mock<Drawable>()
-        context.orCreateTestableResources.addOverride(
-            R.drawable.qs_flashlight_icon_on,
-            fakeDrawable
-        )
-        val expectedIcon = Icon.Loaded(fakeDrawable, null)
+        val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_on, null)
 
         val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
 
@@ -73,12 +66,7 @@
 
     @Test
     fun mapsDisabledDataToOffIconState() {
-        val fakeDrawable = mock<Drawable>()
-        context.orCreateTestableResources.addOverride(
-            R.drawable.qs_flashlight_icon_off,
-            fakeDrawable
-        )
-        val expectedIcon = Icon.Loaded(fakeDrawable, null)
+        val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_off, null)
 
         val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 820c056..8791877 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs.tiles.impl.location.domain
 
-import android.graphics.drawable.Drawable
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -26,7 +25,6 @@
 import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth
 import junit.framework.Assert
 import org.junit.Test
@@ -37,7 +35,8 @@
 class LocationTileMapperTest : SysuiTestCase() {
     private val kosmos = Kosmos()
     private val qsTileConfig = kosmos.qsLocationTileConfig
-    private val mapper by lazy { LocationTileMapper(context) }
+
+    private val mapper by lazy { LocationTileMapper(context.orCreateTestableResources.resources) }
 
     @Test
     fun mapsDisabledDataToInactiveState() {
@@ -57,9 +56,7 @@
 
     @Test
     fun mapsEnabledDataToOnIconState() {
-        val fakeDrawable = mock<Drawable>()
-        context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_on, fakeDrawable)
-        val expectedIcon = Icon.Loaded(fakeDrawable, null)
+        val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_on, null)
 
         val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
 
@@ -69,9 +66,7 @@
 
     @Test
     fun mapsDisabledDataToOffIconState() {
-        val fakeDrawable = mock<Drawable>()
-        context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_off, fakeDrawable)
-        val expectedIcon = Icon.Loaded(fakeDrawable, null)
+        val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_off, null)
 
         val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index c953743..18b7168 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
 import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags
@@ -777,11 +778,11 @@
     private suspend fun TestScope.dismissIme(
         showImeBeforeDismissing: Boolean = true,
     ) {
-        bouncerViewModel.authMethodViewModel.value?.apply {
+        (bouncerViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
             if (showImeBeforeDismissing) {
-                onImeVisibilityChanged(true)
+                it.onImeVisibilityChanged(true)
             }
-            onImeVisibilityChanged(false)
+            it.onImeVisibilityChanged(false)
             runCurrent()
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index adc1c61..3cb97e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -479,7 +479,7 @@
             underTest.start()
             runCurrent()
 
-            bouncerInteractor.onImeHidden()
+            bouncerInteractor.onImeHiddenByUser()
             runCurrent()
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index d9385c7..bcc3c83 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -104,4 +104,7 @@
     <!-- Internet Dialog -->
     <color name="connected_network_primary_color">@color/material_dynamic_primary80</color>
     <color name="connected_network_secondary_color">@color/material_dynamic_secondary80</color>
+
+    <!-- Keyboard shortcut helper dialog -->
+    <color name="ksh_key_item_color">@*android:color/system_on_surface_variant_dark</color>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e124aa0..5f6a39a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -119,7 +119,7 @@
 
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_application_group_color">#fff44336</color>
-    <color name="ksh_key_item_color">?androidprv:attr/materialColorOnSurfaceVariant</color>
+    <color name="ksh_key_item_color">@*android:color/system_on_surface_variant_light</color>
     <color name="ksh_key_item_background">?androidprv:attr/materialColorSurfaceContainerHighest</color>
 
     <color name="instant_apps_color">#ff4d5a64</color>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index b704f3c..1edb551 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -71,7 +71,7 @@
     private final DisplayTracker mDisplayTracker;
     private final AccessibilityLogger mA11yLogger;
 
-    private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
+    private MagnificationConnectionImpl mMagnificationConnectionImpl;
     private SysUiState mSysUiState;
 
     @VisibleForTesting
@@ -220,7 +220,7 @@
     }
 
     @MainThread
-    void setScale(int displayId, float scale) {
+    void setScaleForWindowMagnification(int displayId, float scale) {
         final WindowMagnificationController windowMagnificationController =
                 mMagnificationControllerSupplier.get(displayId);
         if (windowMagnificationController != null) {
@@ -321,37 +321,37 @@
     final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
         @Override
         public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
-            if (mWindowMagnificationConnectionImpl != null) {
-                mWindowMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
+            if (mMagnificationConnectionImpl != null) {
+                mMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
             }
         }
 
         @Override
         public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
-            if (mWindowMagnificationConnectionImpl != null) {
-                mWindowMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
+            if (mMagnificationConnectionImpl != null) {
+                mMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
             }
         }
 
         @Override
         public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
-            if (mWindowMagnificationConnectionImpl != null) {
-                mWindowMagnificationConnectionImpl.onPerformScaleAction(
+            if (mMagnificationConnectionImpl != null) {
+                mMagnificationConnectionImpl.onPerformScaleAction(
                         displayId, scale, updatePersistence);
             }
         }
 
         @Override
         public void onAccessibilityActionPerformed(int displayId) {
-            if (mWindowMagnificationConnectionImpl != null) {
-                mWindowMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
+            if (mMagnificationConnectionImpl != null) {
+                mMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
             }
         }
 
         @Override
         public void onMove(int displayId) {
-            if (mWindowMagnificationConnectionImpl != null) {
-                mWindowMagnificationConnectionImpl.onMove(displayId);
+            if (mMagnificationConnectionImpl != null) {
+                mMagnificationConnectionImpl.onMove(displayId);
             }
         }
 
@@ -394,8 +394,8 @@
                 @Override
                 public void onMagnifierScale(int displayId, float scale,
                         boolean updatePersistence) {
-                    if (mWindowMagnificationConnectionImpl != null) {
-                        mWindowMagnificationConnectionImpl.onPerformScaleAction(
+                    if (mMagnificationConnectionImpl != null) {
+                        mMagnificationConnectionImpl.onPerformScaleAction(
                                 displayId, scale, updatePersistence);
                     }
                     mA11yLogger.logThrottled(
@@ -454,8 +454,8 @@
             if (magnificationSettingsController != null) {
                 magnificationSettingsController.closeMagnificationSettings();
             }
-            if (mWindowMagnificationConnectionImpl != null) {
-                mWindowMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
+            if (mMagnificationConnectionImpl != null) {
+                mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
             }
         }
     }
@@ -500,12 +500,12 @@
     }
 
     private void setWindowMagnificationConnection() {
-        if (mWindowMagnificationConnectionImpl == null) {
-            mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
+        if (mMagnificationConnectionImpl == null) {
+            mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
                     mHandler);
         }
         mAccessibilityManager.setWindowMagnificationConnection(
-                mWindowMagnificationConnectionImpl);
+                mMagnificationConnectionImpl);
     }
 
     private void clearWindowMagnificationConnection() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
rename to packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
index 5666851..5f0d496 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
@@ -32,7 +32,7 @@
  *
  * @see IWindowMagnificationConnection
  */
-class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
+class MagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
 
     private static final String TAG = "WindowMagnificationConnectionImpl";
 
@@ -40,7 +40,7 @@
     private final Magnification mMagnification;
     private final Handler mHandler;
 
-    WindowMagnificationConnectionImpl(@NonNull Magnification magnification,
+    MagnificationConnectionImpl(@NonNull Magnification magnification,
             @Main Handler mainHandler) {
         mMagnification = magnification;
         mHandler = mainHandler;
@@ -57,8 +57,8 @@
     }
 
     @Override
-    public void setScale(int displayId, float scale) {
-        mHandler.post(() -> mMagnification.setScale(displayId, scale));
+    public void setScaleForWindowMagnification(int displayId, float scale) {
+        mHandler.post(() -> mMagnification.setScaleForWindowMagnification(displayId, scale));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index b598631..7c46339 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -105,9 +105,9 @@
     val isUserSwitcherVisible: Boolean
         get() = repository.isUserSwitcherVisible
 
-    private val _onImeHidden = MutableSharedFlow<Unit>()
-    /** Provide the onImeHidden events from the bouncer */
-    val onImeHidden: SharedFlow<Unit> = _onImeHidden
+    private val _onImeHiddenByUser = MutableSharedFlow<Unit>()
+    /** Emits a [Unit] each time the IME (keyboard) is hidden by the user. */
+    val onImeHiddenByUser: SharedFlow<Unit> = _onImeHiddenByUser
 
     init {
         if (flags.isEnabled()) {
@@ -230,9 +230,9 @@
         repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
     }
 
-    /** Notifies the interactor that the input method editor has been hidden. */
-    suspend fun onImeHidden() {
-        _onImeHidden.emit(Unit)
+    /** Notifies that the input method editor (software keyboard) has been hidden by the user. */
+    suspend fun onImeHiddenByUser() {
+        _onImeHiddenByUser.emit(Unit)
     }
 
     private fun promptMessage(authMethod: AuthenticationMethodModel): String {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 8024874..e379dab 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -46,9 +46,6 @@
      */
     val animateFailure: StateFlow<Boolean> = _animateFailure.asStateFlow()
 
-    /** Whether the input method editor (for example, the software keyboard) is visible. */
-    private var isImeVisible: Boolean = false
-
     /** The authentication method that corresponds to this view model. */
     abstract val authenticationMethod: AuthenticationMethodModel
 
@@ -68,7 +65,7 @@
     /**
      * Notifies that the UI has been hidden from the user (after any transitions have completed).
      */
-    fun onHidden() {
+    open fun onHidden() {
         clearInput()
         interactor.resetMessage()
     }
@@ -79,18 +76,6 @@
     }
 
     /**
-     * Notifies that the input method editor (for example, the software keyboard) has been shown or
-     * hidden.
-     */
-    suspend fun onImeVisibilityChanged(isVisible: Boolean) {
-        if (isImeVisible && !isVisible) {
-            interactor.onImeHidden()
-        }
-
-        isImeVisible = isVisible
-    }
-
-    /**
      * Notifies that the failure animation has been shown. This should be called to consume a `true`
      * value in [animateFailure].
      */
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index a15698e..45d18128 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -21,8 +21,11 @@
 import com.android.systemui.res.R
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
 
 /** Holds UI state and handles user input for the password bouncer UI. */
 class PasswordBouncerViewModel(
@@ -45,6 +48,32 @@
 
     override val throttlingMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
 
+    /** Whether the input method editor (for example, the software keyboard) is visible. */
+    private var isImeVisible: Boolean = false
+
+    /** Whether the text field element currently has focus. */
+    private val isTextFieldFocused = MutableStateFlow(false)
+
+    /** Whether the UI should request focus on the text field element. */
+    val isTextFieldFocusRequested =
+        combine(
+                interactor.isThrottled,
+                isTextFieldFocused,
+            ) { isThrottled, hasFocus ->
+                !isThrottled && !hasFocus
+            }
+            .stateIn(
+                scope = viewModelScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = !interactor.isThrottled.value && !isTextFieldFocused.value,
+            )
+
+    override fun onHidden() {
+        super.onHidden()
+        isImeVisible = false
+        isTextFieldFocused.value = false
+    }
+
     override fun clearInput() {
         _password.value = ""
     }
@@ -72,4 +101,21 @@
             tryAuthenticate()
         }
     }
+
+    /**
+     * Notifies that the input method editor (for example, the software keyboard) has been shown or
+     * hidden.
+     */
+    suspend fun onImeVisibilityChanged(isVisible: Boolean) {
+        if (isImeVisible && !isVisible && !interactor.isThrottled.value) {
+            interactor.onImeHiddenByUser()
+        }
+
+        isImeVisible = isVisible
+    }
+
+    /** Notifies that the password text field has gained or lost focus. */
+    fun onTextFieldFocusChanged(isFocused: Boolean) {
+        isTextFieldFocused.value = isFocused
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2a0d6a8..0c24752 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -545,28 +545,12 @@
             unreleasedFlag("clipboard_shared_transitions", teamfood = true)
 
     /**
-     * Whether the scene container (Flexiglass) is enabled. Note that [SCENE_CONTAINER] should be
-     * checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can remove
-     * unused code from our APK at compile time.
+     * Whether the scene container (Flexiglass) is enabled. Note that SceneContainerFlags#isEnabled
+     * should be checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can
+     * remove unused code from our APK at compile time.
      */
     // TODO(b/283300105): Tracking Bug
     @JvmField val SCENE_CONTAINER_ENABLED = false
-    @Deprecated(
-        message = """
-            Do not use this flag directly. Please use
-            [com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled].
-
-            (Not really deprecated but using this as a simple way to bring attention to the above).
-        """,
-        replaceWith = ReplaceWith(
-            "com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled",
-        ),
-        level = DeprecationLevel.WARNING,
-    )
-    @JvmField val SCENE_CONTAINER = resourceBooleanFlag(
-        R.bool.config_sceneContainerFrameworkEnabled,
-        "scene_container",
-    )
 
     // 1900
     @JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 7337292..a988a5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -32,13 +32,19 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.wallet.controller.QuickAccessWalletController
 import com.android.systemui.wallet.util.getPaymentCards
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
 
 /** Quick access wallet quick affordance data source. */
 @SysUISingleton
@@ -46,6 +52,7 @@
 @Inject
 constructor(
     @Application private val context: Context,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val walletController: QuickAccessWalletController,
     private val activityStarter: ActivityStarter,
 ) : KeyguardQuickAffordanceConfig {
@@ -56,6 +63,7 @@
 
     override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen
 
+    @OptIn(ExperimentalCoroutinesApi::class)
     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
         conflatedCallbackFlow {
             val callback =
@@ -63,11 +71,7 @@
                     override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
                         val hasCards = getPaymentCards(response.walletCards)?.isNotEmpty() == true
                         trySendWithFailureLogging(
-                            state(
-                                isFeatureEnabled = isWalletAvailable(),
-                                hasCard = hasCards,
-                                tileIcon = walletController.walletClient.tileIcon,
-                            ),
+                            hasCards,
                             TAG,
                         )
                     }
@@ -75,7 +79,7 @@
                     override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
                         Log.e(TAG, "Wallet card retrieval error, message: \"${error?.message}\"")
                         trySendWithFailureLogging(
-                            KeyguardQuickAffordanceConfig.LockScreenState.Hidden,
+                            null,
                             TAG,
                         )
                     }
@@ -86,8 +90,12 @@
                 QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
                 QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
             )
-            walletController.updateWalletPreference()
-            walletController.queryWalletCards(callback)
+
+            withContext(backgroundDispatcher) {
+                // Both must be called on background thread
+                walletController.updateWalletPreference()
+                walletController.queryWalletCards(callback)
+            }
 
             awaitClose {
                 walletController.unregisterWalletChangeObservers(
@@ -95,6 +103,19 @@
                     QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
                 )
             }
+        }.flatMapLatest { hasCards ->
+            // If hasCards is null, this indicates an error occurred upon card retrieval
+            val state =
+                if (hasCards == null) {
+                    KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+                } else {
+                    state(
+                        isWalletAvailable(),
+                        hasCards,
+                        walletController.walletClient.tileIcon,
+                    )
+                }
+            flowOf(state)
         }
 
     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
@@ -131,25 +152,33 @@
     }
 
     private suspend fun queryCards(): List<WalletCard> {
-        return suspendCancellableCoroutine { continuation ->
-            val callback =
-                object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
-                    override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
-                        continuation.resumeWith(
-                            Result.success(getPaymentCards(response.walletCards) ?: emptyList())
-                        )
-                    }
+        return withContext(backgroundDispatcher) {
+            suspendCancellableCoroutine { continuation ->
+                val callback =
+                    object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
+                        override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
+                            continuation.resumeWith(
+                                Result.success(getPaymentCards(response.walletCards))
+                            )
+                        }
 
-                    override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
-                        continuation.resumeWith(Result.success(emptyList()))
+                        override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
+                            continuation.resumeWith(Result.success(emptyList()))
+                        }
                     }
-                }
-            walletController.queryWalletCards(callback)
+                // Must be called on background thread
+                walletController.queryWalletCards(callback)
+            }
         }
     }
 
-    private fun isWalletAvailable() =
-        with(walletController.walletClient) { isWalletServiceAvailable && isWalletFeatureAvailable }
+    private suspend fun isWalletAvailable() =
+        withContext(backgroundDispatcher) {
+            with(walletController.walletClient) {
+                // Must be called on background thread
+                isWalletServiceAvailable && isWalletFeatureAvailable
+            }
+        }
 
     private fun state(
         isFeatureEnabled: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index bd73d60..62a0b0e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -131,13 +131,16 @@
                 when (toState) {
                     KeyguardState.DREAMING -> TO_DREAMING_DURATION
                     KeyguardState.AOD -> TO_AOD_DURATION
+                    KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
                     else -> DEFAULT_DURATION
                 }.inWholeMilliseconds
         }
     }
+
     companion object {
         private val DEFAULT_DURATION = 500.milliseconds
         val TO_DREAMING_DURATION = 933.milliseconds
         val TO_AOD_DURATION = 1300.milliseconds
+        val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 152d217..cbfd17ff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -380,6 +380,8 @@
                     KeyguardState.DREAMING -> TO_DREAMING_DURATION
                     KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
                     KeyguardState.AOD -> TO_AOD_DURATION
+                    KeyguardState.DOZING -> TO_DOZING_DURATION
+                    KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION
                     else -> DEFAULT_DURATION
                 }.inWholeMilliseconds
         }
@@ -388,7 +390,9 @@
     companion object {
         const val TAG = "FromLockscreenTransitionInteractor"
         private val DEFAULT_DURATION = 400.milliseconds
+        val TO_DOZING_DURATION = 500.milliseconds
         val TO_DREAMING_DURATION = 933.milliseconds
+        val TO_DREAMING_HOSTED_DURATION = 933.milliseconds
         val TO_OCCLUDED_DURATION = 450.milliseconds
         val TO_AOD_DURATION = 500.milliseconds
         val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 706aba3c..f7d1543 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -72,6 +72,10 @@
     val fromDreamingTransition: Flow<TransitionStep> =
         repository.transitions.filter { step -> step.from == DREAMING }
 
+    /** LOCKSCREEN->(any) transition information. */
+    val fromLockscreenTransition: Flow<TransitionStep> =
+        repository.transitions.filter { step -> step.from == LOCKSCREEN }
+
     /** (any)->Lockscreen transition information */
     val anyStateToLockscreenTransition: Flow<TransitionStep> =
         repository.transitions.filter { step -> step.to == LOCKSCREEN }
@@ -113,9 +117,16 @@
     val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
         repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED)
 
+    /** GONE->LOCKSCREEN transition information. */
+    val goneToLockscreenTransition: Flow<TransitionStep> = repository.transition(GONE, LOCKSCREEN)
+
     /** LOCKSCREEN->AOD transition information. */
     val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
 
+    /** LOCKSCREEN->DOZING transition information. */
+    val lockscreenToDozingTransition: Flow<TransitionStep> =
+        repository.transition(LOCKSCREEN, DOZING)
+
     /** LOCKSCREEN->DREAMING transition information. */
     val lockscreenToDreamingTransition: Flow<TransitionStep> =
         repository.transition(LOCKSCREEN, DREAMING)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 99025ace..abd79ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,9 +30,7 @@
 import androidx.core.view.updateLayoutParams
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
 import com.android.settingslib.Utils
-import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.common.shared.model.Icon
@@ -40,6 +38,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.util.doOnEnd
 import kotlinx.coroutines.flow.Flow
@@ -48,9 +47,7 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
-/**
- * This is only for a SINGLE Quick affordance
- */
+/** This is only for a SINGLE Quick affordance */
 object KeyguardQuickAffordanceViewBinder {
 
     private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
@@ -135,28 +132,12 @@
         vibratorHelper: VibratorHelper?,
     ) {
         if (!viewModel.isVisible) {
-            view.alpha = 1f
-            view
-                    .animate()
-                    .alpha(0f)
-                    .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
-                    .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS)
-                    .withEndAction { view.isInvisible = true }
-                    .start()
+            view.isInvisible = true
             return
         }
 
         if (!view.isVisible) {
             view.isVisible = true
-            if (viewModel.animateReveal) {
-                view.alpha = 0f
-                view
-                    .animate()
-                    .alpha(1f)
-                    .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
-                    .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS)
-                    .start()
-            }
         }
 
         IconViewBinder.bind(viewModel.icon, view)
@@ -216,13 +197,14 @@
         view.isClickable = viewModel.isClickable
         if (viewModel.isClickable) {
             if (viewModel.useLongPress) {
-                val onTouchListener = KeyguardQuickAffordanceOnTouchListener(
-                    view,
-                    viewModel,
-                    messageDisplayer,
-                    vibratorHelper,
-                    falsingManager,
-                )
+                val onTouchListener =
+                    KeyguardQuickAffordanceOnTouchListener(
+                        view,
+                        viewModel,
+                        messageDisplayer,
+                        vibratorHelper,
+                        falsingManager,
+                    )
                 view.setOnTouchListener(onTouchListener)
                 view.setOnClickListener {
                     messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
@@ -241,9 +223,7 @@
                         KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
                     shakeAnimator.interpolator =
                         CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
-                    shakeAnimator.doOnEnd {
-                        view.translationX = 0f
-                    }
+                    shakeAnimator.doOnEnd { view.translationX = 0f }
                     shakeAnimator.start()
 
                     vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
@@ -268,18 +248,18 @@
         alphaFlow: Flow<Float>,
     ) {
         combine(viewModel.map { it.isDimmed }, alphaFlow) { isDimmed, alpha ->
-            if (isDimmed) DIM_ALPHA else alpha
-        }
+                if (isDimmed) DIM_ALPHA else alpha
+            }
             .collect { view.alpha = it }
     }
 
     private fun loadFromResources(view: View): ConfigurationBasedDimensions {
         return ConfigurationBasedDimensions(
             buttonSizePx =
-            Size(
-                view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
-                view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
-            ),
+                Size(
+                    view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
+                    view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
+                ),
         )
     }
 
@@ -337,11 +317,9 @@
         }
 
         override fun onLongClickUseDefaultHapticFeedback(view: View) = false
-
     }
 
     private data class ConfigurationBasedDimensions(
         val buttonSizePx: Size,
     )
-
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a2e930c..59c798b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -84,6 +84,7 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 
 /** Renders the preview of the lock screen. */
@@ -158,7 +159,6 @@
 
     init {
         if (keyguardBottomAreaRefactor()) {
-            keyguardRootViewModel.enablePreviewMode()
             quickAffordancesCombinedViewModel.enablePreviewMode(
                 initiallySelectedSlotId =
                     bundle.getString(
@@ -287,6 +287,10 @@
             return
         }
 
+        if (smartSpaceView != null) {
+            parentView.removeView(smartSpaceView)
+        }
+
         smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
 
         val topPadding: Int =
@@ -334,26 +338,27 @@
             ),
         )
     }
-
     @OptIn(ExperimentalCoroutinesApi::class)
     private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
         val keyguardRootView = KeyguardRootView(previewContext, null)
-        disposables.add(
-            KeyguardRootViewBinder.bind(
-                keyguardRootView,
-                keyguardRootViewModel,
-                configuration,
-                featureFlags,
-                occludingAppDeviceEntryMessageViewModel,
-                chipbarCoordinator,
-                screenOffAnimationController,
-                shadeInteractor,
-                null, // clock provider only needed for burn in
-                null, // jank monitor not required for preview mode
-                null, // device entry haptics not required for preview mode
-                null, // device entry haptics not required for preview mode
+        if (!keyguardBottomAreaRefactor()) {
+            disposables.add(
+                KeyguardRootViewBinder.bind(
+                    keyguardRootView,
+                    keyguardRootViewModel,
+                    configuration,
+                    featureFlags,
+                    occludingAppDeviceEntryMessageViewModel,
+                    chipbarCoordinator,
+                    screenOffAnimationController,
+                    shadeInteractor,
+                    null, // clock provider only needed for burn in
+                    null, // jank monitor not required for preview mode
+                    null, // device entry haptics not required preview mode
+                    null, // device entry haptics not required for preview mode
+                )
             )
-        )
+        }
         rootView.addView(
             keyguardRootView,
             FrameLayout.LayoutParams(
@@ -362,12 +367,13 @@
             ),
         )
 
+        setUpUdfps(previewContext, rootView)
+
         disposables.add(
             PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) {
                 if (keyguardBottomAreaRefactor()) {
                     setupShortcuts(keyguardRootView)
                 }
-                setUpUdfps(previewContext, rootView)
 
                 if (!shouldHideClock) {
                     setUpClock(previewContext, rootView)
@@ -387,30 +393,30 @@
     }
 
     private fun setupShortcuts(keyguardRootView: ConstraintLayout) {
-        keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let {
+        keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let { imageView ->
             shortcutsBindings.add(
                 KeyguardQuickAffordanceViewBinder.bind(
-                    it,
-                    quickAffordancesCombinedViewModel.startButton,
-                    keyguardRootViewModel.alpha,
-                    falsingManager,
-                    vibratorHelper,
-                ) {
-                    indicationController.showTransientIndication(it)
+                    view = imageView,
+                    viewModel = quickAffordancesCombinedViewModel.startButton,
+                    alpha = flowOf(1f),
+                    falsingManager = falsingManager,
+                    vibratorHelper = vibratorHelper,
+                ) { message ->
+                    indicationController.showTransientIndication(message)
                 }
             )
         }
 
-        keyguardRootView.findViewById<LaunchableImageView?>(R.id.end_button)?.let {
+        keyguardRootView.findViewById<LaunchableImageView?>(R.id.end_button)?.let { imageView ->
             shortcutsBindings.add(
                 KeyguardQuickAffordanceViewBinder.bind(
-                    it,
-                    quickAffordancesCombinedViewModel.endButton,
-                    keyguardRootViewModel.alpha,
-                    falsingManager,
-                    vibratorHelper,
-                ) {
-                    indicationController.showTransientIndication(it)
+                    view = imageView,
+                    viewModel = quickAffordancesCombinedViewModel.endButton,
+                    alpha = flowOf(1f),
+                    falsingManager = falsingManager,
+                    vibratorHelper = vibratorHelper,
+                ) { message ->
+                    indicationController.showTransientIndication(message)
                 }
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index 55df466..cd46d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -61,7 +61,7 @@
                 KeyguardQuickAffordanceViewBinder.bind(
                     constraintLayout.requireViewById(R.id.start_button),
                     keyguardQuickAffordancesCombinedViewModel.startButton,
-                    keyguardRootViewModel.alpha,
+                    keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
                     falsingManager,
                     vibratorHelper,
                 ) {
@@ -71,7 +71,7 @@
                 KeyguardQuickAffordanceViewBinder.bind(
                     constraintLayout.requireViewById(R.id.end_button),
                     keyguardQuickAffordancesCombinedViewModel.endButton,
-                    keyguardRootViewModel.alpha,
+                    keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
                     falsingManager,
                     vibratorHelper,
                 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index 0f6a966..2a68f26 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -25,14 +25,12 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.RIGHT
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.res.R
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
 import com.android.systemui.statusbar.VibratorHelper
 import javax.inject.Inject
@@ -61,7 +59,7 @@
                 KeyguardQuickAffordanceViewBinder.bind(
                     constraintLayout.requireViewById(R.id.start_button),
                     keyguardQuickAffordancesCombinedViewModel.startButton,
-                    keyguardRootViewModel.alpha,
+                    keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
                     falsingManager,
                     vibratorHelper,
                 ) {
@@ -71,7 +69,7 @@
                 KeyguardQuickAffordanceViewBinder.bind(
                     constraintLayout.requireViewById(R.id.end_button),
                     keyguardQuickAffordancesCombinedViewModel.endButton,
-                    keyguardRootViewModel.alpha,
+                    keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
                     falsingManager,
                     vibratorHelper,
                 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index 14de01b..1864437 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -55,6 +55,14 @@
             onStep = { 1f },
         )
 
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 167.milliseconds,
+            startTime = 67.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+        )
+
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
             if (isUdfps) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index 27fb8a3..a728a28 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
@@ -41,6 +42,13 @@
             transitionFlow = interactor.dozingToLockscreenTransition,
         )
 
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 150.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+        )
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..58235ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class DreamingHostedToLockscreenTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_LOCKSCREEN_DURATION,
+            transitionFlow = interactor.dreamingLockscreenHostedToLockscreenTransition
+        )
+
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index a3b8b85..f943bdf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -96,6 +96,14 @@
             onStep = { it },
         )
 
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            startTime = 233.milliseconds,
+            duration = 250.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+        )
+
     val deviceEntryBackgroundViewAlpha =
         deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
             if (isUdfps) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..5804a20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class GoneToLockscreenTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_LOCKSCREEN_DURATION,
+            transitionFlow = interactor.goneToLockscreenTransition
+        )
+
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 02ea550..188be24 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -32,6 +33,7 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
 
 @OptIn(ExperimentalCoroutinesApi::class)
 class KeyguardQuickAffordancesCombinedViewModel
@@ -39,6 +41,22 @@
 constructor(
     private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
     private val keyguardInteractor: KeyguardInteractor,
+    shadeInteractor: ShadeInteractor,
+    aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+    dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
+    dreamingHostedToLockscreenTransitionViewModel: DreamingHostedToLockscreenTransitionViewModel,
+    dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+    goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
+    occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+    offToLockscreenTransitionViewModel: OffToLockscreenTransitionViewModel,
+    primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
+    lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+    lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
+    lockscreenToDreamingHostedTransitionViewModel: LockscreenToDreamingHostedTransitionViewModel,
+    lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
+    lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel,
+    lockscreenToOccludedTransitionViewModel: LockscreenToOccludedTransitionViewModel,
+    lockscreenToPrimaryBouncerTransitionViewModel: LockscreenToPrimaryBouncerTransitionViewModel,
 ) {
 
     data class PreviewMode(
@@ -60,6 +78,39 @@
     private val selectedPreviewSlotId =
         MutableStateFlow(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
 
+    /** alpha while fading the quick affordances out */
+    private val fadeInAlpha: Flow<Float> =
+        merge(
+            aodToLockscreenTransitionViewModel.shortcutsAlpha,
+            dozingToLockscreenTransitionViewModel.shortcutsAlpha,
+            dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha,
+            dreamingToLockscreenTransitionViewModel.shortcutsAlpha,
+            goneToLockscreenTransitionViewModel.shortcutsAlpha,
+            occludedToLockscreenTransitionViewModel.shortcutsAlpha,
+            offToLockscreenTransitionViewModel.shortcutsAlpha,
+            primaryBouncerToLockscreenTransitionViewModel.shortcutsAlpha,
+        )
+
+    /** alpha while fading the quick affordances in */
+    private val fadeOutAlpha: Flow<Float> =
+        merge(
+            lockscreenToAodTransitionViewModel.shortcutsAlpha,
+            lockscreenToDozingTransitionViewModel.shortcutsAlpha,
+            lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha,
+            lockscreenToDreamingTransitionViewModel.shortcutsAlpha,
+            lockscreenToGoneTransitionViewModel.shortcutsAlpha,
+            lockscreenToOccludedTransitionViewModel.shortcutsAlpha,
+            lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha,
+            shadeInteractor.qsExpansion.map { 1 - it },
+        )
+
+    /** The source of truth of alpha for all of the quick affordances on lockscreen */
+    val transitionAlpha: Flow<Float> =
+        merge(
+            fadeInAlpha,
+            fadeOutAlpha,
+        )
+
     /**
      * Whether quick affordances are "opaque enough" to be considered visible to and interactive by
      * the user. If they are not interactive, user input should not be allowed on them.
@@ -73,7 +124,7 @@
      * interactive/clickable unless "fully opaque" to avoid issues like in b/241830987.
      */
     private val areQuickAffordancesFullyOpaque: Flow<Boolean> =
-        keyguardInteractor.keyguardAlpha
+        transitionAlpha
             .map { alpha -> alpha >= AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD }
             .distinctUntilChanged()
 
@@ -89,7 +140,7 @@
      * Notifies that a slot with the given ID has been selected in the preview experience that is
      * rendering in the wallpaper picker. This is ignored for the real lock screen experience.
      *
-     * @see [KeyguardRootViewModel.enablePreviewMode]
+     * @see [enablePreviewMode]
      */
     fun onPreviewSlotSelected(slotId: String) {
         selectedPreviewSlotId.value = slotId
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 524fa1e..f63afeb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -47,13 +47,11 @@
 import javax.inject.Provider
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onStart
@@ -74,16 +72,6 @@
     private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     screenOffAnimationController: ScreenOffAnimationController,
 ) {
-
-    data class PreviewMode(val isInPreviewMode: Boolean = false)
-
-    /**
-     * Whether this view-model instance is powering the preview experience that renders exclusively
-     * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
-     * experience.
-     */
-    private val previewMode = MutableStateFlow(PreviewMode())
-
     var clockControllerProvider: Provider<ClockController>? = null
 
     /** System insets that keyguard needs to stay out of */
@@ -103,14 +91,7 @@
         keyguardInteractor.notificationContainerBounds
 
     /** An observable for the alpha level for the entire keyguard root view. */
-    val alpha: Flow<Float> =
-        previewMode.flatMapLatest {
-            if (it.isInPreviewMode) {
-                flowOf(1f)
-            } else {
-                keyguardInteractor.keyguardAlpha.distinctUntilChanged()
-            }
-        }
+    val alpha: Flow<Float> = keyguardInteractor.keyguardAlpha.distinctUntilChanged()
 
     private fun burnIn(): Flow<BurnInModel> {
         val dozingAmount: Flow<Float> =
@@ -147,55 +128,29 @@
     val lockscreenStateAlpha: Flow<Float> = aodToLockscreenTransitionViewModel.lockscreenAlpha
 
     /** For elements that appear and move during the animation -> AOD */
-    val burnInLayerAlpha: Flow<Float> =
-        previewMode.flatMapLatest {
-            if (it.isInPreviewMode) {
-                flowOf(1f)
-            } else {
-                goneToAodTransitionViewModel.enterFromTopAnimationAlpha
-            }
-        }
+    val burnInLayerAlpha: Flow<Float> = goneToAodTransitionViewModel.enterFromTopAnimationAlpha
 
     val translationY: Flow<Float> =
-        previewMode.flatMapLatest {
-            if (it.isInPreviewMode) {
-                flowOf(0f)
-            } else {
-                keyguardInteractor.configurationChange.flatMapLatest { _ ->
-                    val enterFromTopAmount =
-                        context.resources.getDimensionPixelSize(
-                            R.dimen.keyguard_enter_from_top_translation_y
-                        )
-                    combine(
-                        keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
-                        burnIn().map { it.translationY.toFloat() }.onStart { emit(0f) },
-                        goneToAodTransitionViewModel
-                            .enterFromTopTranslationY(enterFromTopAmount)
-                            .onStart { emit(0f) },
-                    ) { keyguardTransitionY, burnInTranslationY, goneToAodTransitionTranslationY ->
-                        // All 3 values need to be combined for a smooth translation
-                        keyguardTransitionY + burnInTranslationY + goneToAodTransitionTranslationY
-                    }
-                }
+        keyguardInteractor.configurationChange.flatMapLatest { _ ->
+            val enterFromTopAmount =
+                context.resources.getDimensionPixelSize(
+                    R.dimen.keyguard_enter_from_top_translation_y
+                )
+            combine(
+                keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
+                burnIn().map { it.translationY.toFloat() }.onStart { emit(0f) },
+                goneToAodTransitionViewModel.enterFromTopTranslationY(enterFromTopAmount).onStart {
+                    emit(0f)
+                },
+            ) { keyguardTransitionY, burnInTranslationY, goneToAodTransitionTranslationY ->
+                // All 3 values need to be combined for a smooth translation
+                keyguardTransitionY + burnInTranslationY + goneToAodTransitionTranslationY
             }
         }
 
-    val translationX: Flow<Float> =
-        previewMode.flatMapLatest {
-            if (it.isInPreviewMode) {
-                flowOf(0f)
-            } else {
-                burnIn().map { it.translationX.toFloat() }
-            }
-        }
+    val translationX: Flow<Float> = burnIn().map { it.translationX.toFloat() }
 
-    val scale: Flow<Pair<Float, Boolean>> =
-        previewMode.flatMapLatest { previewMode ->
-            burnIn().map {
-                val scale = if (previewMode.isInPreviewMode) 1f else it.scale
-                Pair(scale, it.scaleClockOnly)
-            }
-        }
+    val scale: Flow<Pair<Float, Boolean>> = burnIn().map { Pair(it.scale, it.scaleClockOnly) }
 
     /** Is the notification icon container visible? */
     val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> =
@@ -238,20 +193,7 @@
             }
             .distinctUntilChanged()
 
-    /**
-     * Puts this view-model in "preview mode", which means it's being used for UI that is rendering
-     * the lock screen preview in wallpaper picker / settings and not the real experience on the
-     * lock screen.
-     */
-    fun enablePreviewMode() {
-        previewMode.value = PreviewMode(true)
-    }
-
     fun onNotificationContainerBoundsChanged(top: Float, bottom: Float) {
-        // Notifications should not be visible in preview mode
-        if (previewMode.value.isInPreviewMode) {
-            return
-        }
 
         keyguardInteractor.setNotificationContainerBounds(NotificationContainerBounds(top, bottom))
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 2bf12e8..8e8fd75c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -57,6 +57,15 @@
                     onFinish = { 0f },
                 ),
         )
+
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1 - it },
+            onFinish = { 0f },
+            onCancel = { 1f },
+        )
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
             isUdfpsEnrolledAndEnabled ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
new file mode 100644
index 0000000..263ed11
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class LockscreenToDozingTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_DOZING_DURATION,
+            transitionFlow = interactor.lockscreenToDozingTransition
+        )
+
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1 - it },
+            onFinish = { 0f },
+            onCancel = { 1f },
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
new file mode 100644
index 0000000..1701505
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class LockscreenToDreamingHostedTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_DREAMING_HOSTED_DURATION,
+            transitionFlow = interactor.lockscreenToDreamingLockscreenHostedTransition
+        )
+
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1 - it },
+            onFinish = { 0f },
+            onCancel = { 1f },
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index 5229613..401c0ff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -62,6 +62,14 @@
             onStep = { 1f - it },
         )
 
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1 - it },
+            onFinish = { 0f },
+            onCancel = { 1f },
+        )
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsNotExpanded = lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index 59e5aa8..cfb4bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
@@ -43,6 +44,14 @@
             transitionFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
         )
 
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1 - it },
+            onFinish = { 0f },
+            onCancel = { 1f },
+        )
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index d49bc49..a6136f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -50,6 +50,14 @@
             onStep = { 1f - it },
         )
 
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { 1 - it },
+            onFinish = { 0f },
+            onCancel = { 1f },
+        )
+
     /** Lockscreen views y-translation */
     fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
         return transitionAnimation.createFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index f04b67a..07dd4ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -26,6 +26,7 @@
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
 
 /**
  * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -46,6 +47,11 @@
                 interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER),
         )
 
+    val shortcutsAlpha: Flow<Float> =
+        interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER).map {
+            1 - it.value
+        }
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsNotExpanded =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 0bdc85d..58be093 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -58,6 +58,13 @@
         )
     }
 
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+        )
+
     /** Lockscreen views alpha */
     val lockscreenAlpha: Flow<Float> =
         transitionAnimation.createFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..c3bc799
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class OffToLockscreenTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = 250.milliseconds,
+            transitionFlow = interactor.offToLockscreenTransition
+        )
+
+    val shortcutsAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 250.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index 3cf793a..7ef8374 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -28,6 +28,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
 
 /**
  * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -57,6 +58,11 @@
             }
         }
 
+    val shortcutsAlpha: Flow<Float> =
+        interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN).map {
+            it.value
+        }
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index eba1c25..3884184 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -36,11 +36,11 @@
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.tileimpl.HeightOverrideable;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -64,6 +64,7 @@
 
     private static final String TAG = "QSAnimator";
 
+    private static final int ANIMATORS_UPDATE_DELAY_MS = 100;
     private static final float EXPANDED_TILE_DELAY = .86f;
     //Non first page delays
     private static final float QS_TILE_LABEL_FADE_OUT_START = 0.15f;
@@ -133,7 +134,7 @@
     private int mLastQQSTileHeight;
     private float mLastPosition;
     private final QSHost mHost;
-    private final Executor mExecutor;
+    private final DelayableExecutor mExecutor;
     private boolean mShowCollapsedOnKeyguard;
     private int mQQSTop;
 
@@ -144,7 +145,7 @@
     public QSAnimator(@RootView View rootView, QuickQSPanel quickPanel,
             QSPanelController qsPanelController,
             QuickQSPanelController quickQSPanelController, QSHost qsTileHost,
-            @Main Executor executor, TunerService tunerService,
+            @Main DelayableExecutor executor, TunerService tunerService,
             QSExpansionPathInterpolator qsExpansionPathInterpolator) {
         mQsRootView = rootView;
         mQuickQsPanel = quickPanel;
@@ -753,7 +754,10 @@
     public void onTilesChanged() {
         // Give the QS panels a moment to generate their new tiles, then create all new animators
         // hooked up to the new views.
-        mExecutor.execute(mUpdateAnimators);
+        mExecutor.executeDelayed(mUpdateAnimators, ANIMATORS_UPDATE_DELAY_MS);
+
+        // Also requests a lazy animators update in case the animation starts before the executor.
+        requestAnimatorUpdate();
     }
 
     private final TouchAnimator.Listener mNonFirstPageListener =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 11db69b..6c930b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -255,6 +255,10 @@
             for (QSTile tile : tiles) {
                 addTile(tile, collapsedView);
             }
+        } else {
+            for (QSPanelControllerBase.TileRecord record : mRecords) {
+                record.tile.addCallback(record.callback);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 69fe46a..529d684 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -109,7 +109,7 @@
     // Only read and modified in main thread (where click events come through).
     private int mClickEventId = 0;
 
-    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+    private final ArraySet<Callback> mCallbacks = new ArraySet<>();
     private final Object mStaleListener = new Object();
     protected TState mState;
     private TState mTmpState;
@@ -444,9 +444,9 @@
     }
 
     private void handleStateChanged() {
-        if (mCallbacks.size() != 0) {
+        if (!mCallbacks.isEmpty()) {
             for (int i = 0; i < mCallbacks.size(); i++) {
-                mCallbacks.get(i).onStateChanged(mState);
+                mCallbacks.valueAt(i).onStateChanged(mState);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index c390695b..cfb5442 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -16,8 +16,9 @@
 
 package com.android.systemui.qs.tiles.impl.airplane.domain
 
-import android.content.Context
+import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,29 +27,27 @@
 import javax.inject.Inject
 
 /** Maps [AirplaneModeTileModel] to [QSTileState]. */
-class AirplaneModeMapper @Inject constructor(private val context: Context) :
+class AirplaneModeMapper @Inject constructor(@Main private val resources: Resources) :
     QSTileDataToStateMapper<AirplaneModeTileModel> {
 
     override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
-        QSTileState.build(context, config.uiConfig) {
+        QSTileState.build(resources, config.uiConfig) {
             val icon =
-                Icon.Loaded(
-                    context.getDrawable(
-                        if (data.isEnabled) {
-                            R.drawable.qs_airplane_icon_on
-                        } else {
-                            R.drawable.qs_airplane_icon_off
-                        }
-                    )!!,
+                Icon.Resource(
+                    if (data.isEnabled) {
+                        R.drawable.qs_airplane_icon_on
+                    } else {
+                        R.drawable.qs_airplane_icon_off
+                    },
                     contentDescription = null
                 )
             this.icon = { icon }
             if (data.isEnabled) {
                 activationState = QSTileState.ActivationState.ACTIVE
-                secondaryLabel = context.resources.getStringArray(R.array.tile_states_airplane)[2]
+                secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
             } else {
                 activationState = QSTileState.ActivationState.INACTIVE
-                secondaryLabel = context.resources.getStringArray(R.array.tile_states_airplane)[1]
+                secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[1]
             }
             contentDescription = label
             supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index b2b22646..881a6bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -16,8 +16,9 @@
 
 package com.android.systemui.qs.tiles.impl.flashlight.domain
 
-import android.content.Context
+import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,30 +27,28 @@
 import javax.inject.Inject
 
 /** Maps [FlashlightTileModel] to [QSTileState]. */
-class FlashlightMapper @Inject constructor(private val context: Context) :
+class FlashlightMapper @Inject constructor(@Main private val resources: Resources) :
     QSTileDataToStateMapper<FlashlightTileModel> {
 
     override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
-        QSTileState.build(context, config.uiConfig) {
+        QSTileState.build(resources, config.uiConfig) {
             val icon =
-                Icon.Loaded(
-                    context.resources.getDrawable(
-                        if (data.isEnabled) {
-                            R.drawable.qs_flashlight_icon_on
-                        } else {
-                            R.drawable.qs_flashlight_icon_off
-                        }
-                    ),
+                Icon.Resource(
+                    if (data.isEnabled) {
+                        R.drawable.qs_flashlight_icon_on
+                    } else {
+                        R.drawable.qs_flashlight_icon_off
+                    },
                     contentDescription = null
                 )
             this.icon = { icon }
 
             if (data.isEnabled) {
                 activationState = QSTileState.ActivationState.ACTIVE
-                secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[2]
+                secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[2]
             } else {
                 activationState = QSTileState.ActivationState.INACTIVE
-                secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[1]
+                secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[1]
             }
             contentDescription = label
             supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index 8e53723..7e7034d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -16,8 +16,9 @@
 
 package com.android.systemui.qs.tiles.impl.location.domain
 
-import android.content.Context
+import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,32 +27,30 @@
 import javax.inject.Inject
 
 /** Maps [LocationTileModel] to [QSTileState]. */
-class LocationTileMapper @Inject constructor(private val context: Context) :
+class LocationTileMapper @Inject constructor(@Main private val resources: Resources) :
     QSTileDataToStateMapper<LocationTileModel> {
 
     override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
-        QSTileState.build(context, config.uiConfig) {
+        QSTileState.build(resources, config.uiConfig) {
             val icon =
-                Icon.Loaded(
-                    context.resources.getDrawable(
-                        if (data.isEnabled) {
-                            R.drawable.qs_location_icon_on
-                        } else {
-                            R.drawable.qs_location_icon_off
-                        }
-                    ),
+                Icon.Resource(
+                    if (data.isEnabled) {
+                        R.drawable.qs_location_icon_on
+                    } else {
+                        R.drawable.qs_location_icon_off
+                    },
                     contentDescription = null
                 )
             this.icon = { icon }
 
-            this.label = context.resources.getString(R.string.quick_settings_location_label)
+            this.label = resources.getString(R.string.quick_settings_location_label)
 
             if (data.isEnabled) {
                 activationState = QSTileState.ActivationState.ACTIVE
-                secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[2]
+                secondaryLabel = resources.getStringArray(R.array.tile_states_location)[2]
             } else {
                 activationState = QSTileState.ActivationState.INACTIVE
-                secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[1]
+                secondaryLabel = resources.getStringArray(R.array.tile_states_location)[1]
             }
             contentDescription = label
             supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index f9e0b16..23e0cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.qs.tiles.viewmodel
 
-import android.content.Context
+import android.content.res.Resources
 import android.service.quicksettings.Tile
 import android.view.View
 import android.widget.Switch
@@ -46,13 +46,13 @@
     companion object {
 
         fun build(
-            context: Context,
+            resources: Resources,
             config: QSTileUIConfig,
             build: Builder.() -> Unit
         ): QSTileState =
             build(
                 { Icon.Resource(config.iconRes, null) },
-                context.getString(config.labelRes),
+                resources.getString(config.labelRes),
                 build,
             )
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 1c5330e..d42fde6 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -128,7 +128,7 @@
     private fun automaticallySwitchScenes() {
         applicationScope.launch {
             // TODO (b/308001302): Move this to a bouncer specific interactor.
-            bouncerInteractor.onImeHidden.collectLatest {
+            bouncerInteractor.onImeHiddenByUser.collectLatest {
                 if (sceneInteractor.desiredScene.value.key == SceneKey.Bouncer) {
                     sceneInteractor.changeScene(
                         scene = SceneModel(SceneKey.Lockscreen),
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index d14ef35..dbb58a3 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -16,12 +16,14 @@
 
 package com.android.systemui.scene.shared.flag
 
+import android.content.Context
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
 import com.android.systemui.Flags.sceneContainer
 import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flag
 import com.android.systemui.flags.Flags
@@ -29,6 +31,7 @@
 import com.android.systemui.flags.ResourceBooleanFlag
 import com.android.systemui.flags.UnreleasedFlag
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.res.R
 import dagger.Module
 import dagger.Provides
 import dagger.assisted.Assisted
@@ -51,6 +54,7 @@
 class SceneContainerFlagsImpl
 @AssistedInject
 constructor(
+    @Application private val context: Context,
     private val featureFlagsClassic: FeatureFlagsClassic,
     @Assisted private val isComposeAvailable: Boolean,
 ) : SceneContainerFlags {
@@ -80,7 +84,11 @@
             ),
         ) +
             classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
-            listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled())
+            listOf(
+                ComposeMustBeAvailable(),
+                CompileTimeFlagMustBeEnabled(),
+                ResourceConfigMustBeEnabled()
+            )
 
     override fun isEnabled(): Boolean {
         // SCENE_CONTAINER_ENABLED is an explicit static flag check that helps with downstream
@@ -146,6 +154,14 @@
         }
     }
 
+    private inner class ResourceConfigMustBeEnabled : Requirement {
+        override val name: String = "R.bool.config_sceneContainerFrameworkEnabled must be true"
+
+        override fun isMet(): Boolean {
+            return context.resources.getBoolean(R.bool.config_sceneContainerFrameworkEnabled)
+        }
+    }
+
     @AssistedFactory
     interface Factory {
         fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index e7481cc..b98093e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -32,13 +32,16 @@
 import android.os.Looper;
 import android.os.ResultReceiver;
 import android.view.Gravity;
+import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ArrayAdapter;
 import android.widget.Spinner;
 import android.widget.Switch;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
@@ -115,6 +118,17 @@
         mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
             mAudioSwitch.setChecked(true);
         });
+
+        // disable redundant Touch & Hold accessibility action for Switch Access
+        mOptions.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+                    @NonNull AccessibilityNodeInfo info) {
+                info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+                super.onInitializeAccessibilityNodeInfo(host, info);
+            }
+        });
+        mOptions.setLongClickable(false);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 9f4ea27..d13edf0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -39,6 +39,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -58,18 +59,21 @@
     private final DelayableExecutor mMainExecutor;
     private final AccessibilityManagerWrapper mAccessibilityMgr;
     private Runnable mCancelTimeoutRunnable;
+    private final ShadeInteractor mShadeInteractor;
 
     @Inject
     public BrightnessDialog(
             BrightnessSliderController.Factory brightnessSliderfactory,
             BrightnessController.Factory brightnessControllerFactory,
             @Main DelayableExecutor mainExecutor,
-            AccessibilityManagerWrapper accessibilityMgr
+            AccessibilityManagerWrapper accessibilityMgr,
+            ShadeInteractor shadeInteractor
     ) {
         mToggleSliderFactory = brightnessSliderfactory;
         mBrightnessControllerFactory = brightnessControllerFactory;
         mMainExecutor = mainExecutor;
         mAccessibilityMgr = accessibilityMgr;
+        mShadeInteractor = shadeInteractor;
     }
 
 
@@ -79,6 +83,10 @@
         setWindowAttributes();
         setContentView(R.layout.brightness_mirror_container);
         setBrightnessDialogViewAttributes();
+
+        if (mShadeInteractor.isQsExpanded().getValue()) {
+            finish();
+        }
     }
 
     private void setWindowAttributes() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 1b096b5..909cff37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -493,11 +493,11 @@
                         Arrays.asList(
                                 Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))),
                 /* Back: go back to previous state (back button) */
-                /* Meta + Grave, Meta + backspace, Meta + left arrow */
+                /* Meta + Escape, Meta + Grave, Meta + backspace, Meta + left arrow */
                 new ShortcutKeyGroupMultiMappingInfo(
                         context.getString(R.string.group_system_go_back),
                         Arrays.asList(
-                                Pair.create(KeyEvent.KEYCODE_GRAVE, KeyEvent.META_META_ON),
+                                Pair.create(KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_META_ON),
                                 Pair.create(KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON),
                                 Pair.create(KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_META_ON))),
                 /* Access home screen: Meta + H, Meta + Enter */
@@ -612,7 +612,7 @@
     private static KeyboardShortcutMultiMappingGroup getMultiMappingInputShortcuts(
             Context context) {
         List<ShortcutMultiMappingInfo> shortcutMultiMappingInfoList = Arrays.asList(
-                /* Switch input language (next language): Ctrl + Space or Meta + Space */
+                /* Switch input language (next language): Ctrl + Space */
                 new ShortcutMultiMappingInfo(
                         context.getString(R.string.input_switch_input_language_next),
                         null,
@@ -621,14 +621,9 @@
                                         context.getString(
                                                 R.string.input_switch_input_language_next),
                                         KeyEvent.KEYCODE_SPACE, KeyEvent.META_CTRL_ON),
-                                        null),
-                                new ShortcutKeyGroup(new KeyboardShortcutInfo(
-                                        context.getString(
-                                                R.string.input_switch_input_language_next),
-                                        KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON),
                                         null))),
                 /* Switch input language (previous language): */
-                /* Ctrl + Shift + Space or Meta + Shift + Space */
+                /* Ctrl + Shift + Space */
                 new ShortcutMultiMappingInfo(
                         context.getString(R.string.input_switch_input_language_previous),
                         null,
@@ -638,12 +633,6 @@
                                                 R.string.input_switch_input_language_previous),
                                         KeyEvent.KEYCODE_SPACE,
                                         KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON),
-                                        null),
-                                new ShortcutKeyGroup(new KeyboardShortcutInfo(
-                                        context.getString(
-                                                R.string.input_switch_input_language_previous),
-                                        KeyEvent.KEYCODE_SPACE,
-                                        KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON),
                                         null)))
         );
         return new KeyboardShortcutMultiMappingGroup(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 31893b4..e90ddf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -15,37 +15,48 @@
 
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
 class ActiveNotificationsInteractor
 @Inject
 constructor(
     private val repository: ActiveNotificationListRepository,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
     /** Notifications actively presented to the user in the notification stack, in order. */
     val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> =
-        repository.activeNotifications.map { store ->
-            store.renderList.map { key ->
-                val entry =
-                    store[key]
-                        ?: error("Could not find notification with key $key in active notif store.")
-                when (entry) {
-                    is ActiveNotificationGroupModel -> entry.summary
-                    is ActiveNotificationModel -> entry
+        repository.activeNotifications
+            .map { store ->
+                store.renderList.map { key ->
+                    val entry =
+                        store[key]
+                            ?: error(
+                                "Could not find notification with key $key in active notif store."
+                            )
+                    when (entry) {
+                        is ActiveNotificationGroupModel -> entry.summary
+                        is ActiveNotificationModel -> entry
+                    }
                 }
             }
-        }
+            .flowOn(backgroundDispatcher)
 
     /** Are any notifications being actively presented in the notification stack? */
     val areAnyNotificationsPresent: Flow<Boolean> =
-        repository.activeNotifications.map { it.renderList.isNotEmpty() }.distinctUntilChanged()
+        repository.activeNotifications
+            .map { it.renderList.isNotEmpty() }
+            .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
 
     /**
      * The same as [areAnyNotificationsPresent], but without flows, for easy access in synchronous
@@ -59,6 +70,7 @@
         repository.notifStats
             .map { it.hasClearableAlertingNotifs || it.hasClearableSilentNotifs }
             .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
 
     fun setNotifStats(notifStats: NotifStats) {
         repository.notifStats.value = notifStats
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
index 87b8e55..73341db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
@@ -15,19 +15,24 @@
  */
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
 
 /** Domain logic pertaining to notifications on the keyguard. */
 class NotificationsKeyguardInteractor
 @Inject
 constructor(
     repository: NotificationsKeyguardViewStateRepository,
+    @Background backgroundDispatcher: CoroutineDispatcher,
 ) {
     /** Is a pulse expansion occurring? */
-    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding
+    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding.flowOn(backgroundDispatcher)
 
     /** Are notifications fully hidden from view? */
-    val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden
+    val areNotificationsFullyHidden: Flow<Boolean> =
+        repository.areNotificationsFullyHidden.flowOn(backgroundDispatcher)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 6d8ec44..c615887 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -67,6 +67,7 @@
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
     private boolean mIsStatusBarExpanded = false;
+    private boolean mIsSceneContainerVisible = false;
     private boolean mShouldAdjustInsets = false;
     private View mNotificationShadeWindowView;
     private View mNotificationPanelView;
@@ -128,11 +129,14 @@
         });
 
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
-        javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
 
         if (sceneContainerFlags.isEnabled()) {
             javaAdapter.alwaysCollectFlow(
                     sceneInteractor.get().isVisible(),
+                    this::onSceneContainerVisibilityChanged);
+        } else {
+            javaAdapter.alwaysCollectFlow(
+                    shadeInteractor.isAnyExpanded(),
                     this::onShadeOrQsExpanded);
         }
 
@@ -164,6 +168,17 @@
         }
     }
 
+    private void onSceneContainerVisibilityChanged(Boolean isVisible) {
+        if (isVisible != mIsSceneContainerVisible) {
+            mIsSceneContainerVisible = isVisible;
+            if (isVisible) {
+                // make sure our state is sensible
+                mForceCollapsedUntilLayout = false;
+            }
+            updateTouchableRegion();
+        }
+    }
+
     /**
      * Calculates the touch region needed for heads up notifications, taking into consideration
      * any existing display cutouts (notch)
@@ -267,6 +282,7 @@
         // since we don't want stray touches to go through the light reveal scrim to whatever is
         // underneath.
         return mIsStatusBarExpanded
+                || mIsSceneContainerVisible
                 || mPrimaryBouncerInteractor.isShowing().getValue()
                 || mAlternateBouncerInteractor.isVisibleState()
                 || mUnlockedScreenOffAnimationController.isAnimationPlaying();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
index bbba19d..87df180 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
@@ -73,7 +73,11 @@
 
     /** Callback to be notified about device posture changes. */
     interface Callback {
-        /** Called when the posture changes. */
+        /**
+         * Called when the posture changes. If there are multiple active displays ("concurrent"),
+         * this will report the physical posture of the device (also known as the base device
+         * state).
+         */
         void onPostureChanged(@DevicePostureInt int posture);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index 8f1ac81..422aa4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -39,8 +39,11 @@
 /** Implementation of {@link DevicePostureController} using the DeviceStateManager. */
 @SysUISingleton
 public class DevicePostureControllerImpl implements DevicePostureController {
+    /** From androidx.window.common.COMMON_STATE_USE_BASE_STATE */
+    private static final int COMMON_STATE_USE_BASE_STATE = 1000;
     private final List<Callback> mListeners = new ArrayList<>();
     private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
+    private int mCurrentBasePosture = DEVICE_POSTURE_UNKNOWN;
 
     private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
 
@@ -73,16 +76,32 @@
             mDeviceStateToPostureMap.put(deviceState, posture);
         }
 
-        deviceStateManager.registerCallback(executor, state -> {
-            Assert.isMainThread();
-            mCurrentDevicePosture =
-                    mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+        deviceStateManager.registerCallback(executor, new DeviceStateManager.DeviceStateCallback() {
+            @Override
+            public void onStateChanged(int state) {
+                Assert.isMainThread();
+                mCurrentDevicePosture =
+                        mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+                sendUpdatePosture();
+            }
 
-            ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl",
+            @Override
+            public void onBaseStateChanged(int state) {
+                Assert.isMainThread();
+                mCurrentBasePosture = mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+
+                if (useBaseState()) {
+                    sendUpdatePosture();
+                }
+            }
+
+            private void sendUpdatePosture() {
+                ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl",
                     l -> {
-                        l.onPostureChanged(mCurrentDevicePosture);
+                        l.onPostureChanged(getDevicePosture());
                         return Unit.INSTANCE;
                     });
+            }
         });
     }
 
@@ -100,6 +119,14 @@
 
     @Override
     public int getDevicePosture() {
-        return mCurrentDevicePosture;
+        if (useBaseState()) {
+            return mCurrentBasePosture;
+        } else {
+            return mCurrentDevicePosture;
+        }
+    }
+
+    private boolean useBaseState() {
+        return mCurrentDevicePosture == COMMON_STATE_USE_BASE_STATE;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index e576f36..279e5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -16,6 +16,7 @@
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
 
 public interface FlashlightController extends CallbackController<FlashlightListener>, Dumpable {
 
@@ -24,6 +25,7 @@
     boolean isAvailable();
     boolean isEnabled();
 
+    @WeaklyReferencedCallback
     public interface FlashlightListener {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 0531487..9689811 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -20,10 +20,12 @@
 import android.hardware.devicestate.DeviceStateManager
 import android.os.SystemProperties
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.LifecycleScreenStatusProvider
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
@@ -64,6 +66,10 @@
 
     @Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
 
+    @Provides
+    @UnfoldBgProgressFlag
+    fun unfoldBgProgressFlag() = Flags.unfoldAnimationBackgroundProgress()
+
     /** A globally available FoldStateListener that allows one to query the fold state. */
     @Provides
     @Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 0e6df6b..e031be2 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -19,6 +19,7 @@
 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
 
+import android.annotation.WorkerThread;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -146,7 +147,9 @@
 
     /**
      * Update the "show wallet" preference.
+     * This should not be called on the main thread.
      */
+    @WorkerThread
     public void updateWalletPreference() {
         mWalletEnabled = mQuickAccessWalletClient.isWalletServiceAvailable()
                 && mQuickAccessWalletClient.isWalletFeatureAvailable()
@@ -155,10 +158,12 @@
 
     /**
      * Query the wallet cards from {@link QuickAccessWalletClient}.
+     * This should not be called on the main thread.
      *
      * @param cardsRetriever a callback to retrieve wallet cards.
      * @param maxCards the maximum number of cards requested from the QuickAccessWallet
      */
+    @WorkerThread
     public void queryWalletCards(
             QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever, int maxCards) {
         if (mClock.elapsedRealtime() - mQawClientCreatedTimeMillis
@@ -182,9 +187,11 @@
 
     /**
      * Query the wallet cards from {@link QuickAccessWalletClient}.
+     * This should not be called on the main thread.
      *
      * @param cardsRetriever a callback to retrieve wallet cards.
      */
+    @WorkerThread
     public void queryWalletCards(
             QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
         queryWalletCards(cardsRetriever, /* maxCards= */ 1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index d8799e1..4395282 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -133,8 +133,8 @@
     }
 
     @Test
-    public void setScale() throws RemoteException {
-        mIWindowMagnificationConnection.setScale(TEST_DISPLAY, 3.0f);
+    public void setScaleForWindowMagnification() throws RemoteException {
+        mIWindowMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
         waitForIdleSync();
 
         verify(mWindowMagnificationController).setScale(3.0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 67c4e26..0c30d10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -51,6 +51,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.FakeSharedPreferences
@@ -58,8 +59,10 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth
-import kotlin.math.max
 import kotlin.math.min
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
@@ -73,6 +76,7 @@
 import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(JUnit4::class)
 class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
@@ -85,6 +89,47 @@
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+    @Mock private lateinit var shadeInteractor: ShadeInteractor
+    @Mock
+    private lateinit var aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel
+    @Mock
+    private lateinit var dozingToLockscreenTransitionViewModel:
+        DozingToLockscreenTransitionViewModel
+    @Mock
+    private lateinit var dreamingHostedToLockscreenTransitionViewModel:
+        DreamingHostedToLockscreenTransitionViewModel
+    @Mock
+    private lateinit var dreamingToLockscreenTransitionViewModel:
+        DreamingToLockscreenTransitionViewModel
+    @Mock
+    private lateinit var goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel
+    @Mock
+    private lateinit var occludedToLockscreenTransitionViewModel:
+        OccludedToLockscreenTransitionViewModel
+    @Mock
+    private lateinit var offToLockscreenTransitionViewModel: OffToLockscreenTransitionViewModel
+    @Mock
+    private lateinit var primaryBouncerToLockscreenTransitionViewModel:
+        PrimaryBouncerToLockscreenTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToDozingTransitionViewModel:
+        LockscreenToDozingTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToDreamingHostedTransitionViewModel:
+        LockscreenToDreamingHostedTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToDreamingTransitionViewModel:
+        LockscreenToDreamingTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToOccludedTransitionViewModel:
+        LockscreenToOccludedTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToPrimaryBouncerTransitionViewModel:
+        LockscreenToPrimaryBouncerTransitionViewModel
 
     private lateinit var underTest: KeyguardQuickAffordancesCombinedViewModel
 
@@ -97,6 +142,10 @@
     private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
     private lateinit var keyguardInteractor: KeyguardInteractor
 
+    private val intendedAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(1f)
+    // the viewModel does a `map { 1 - it }` on this value, which is why it's different
+    private val intendedShadeAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(0f)
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -191,6 +240,31 @@
                 userHandle = UserHandle.SYSTEM,
             )
 
+        intendedAlphaMutableStateFlow.value = 1f
+        intendedShadeAlphaMutableStateFlow.value = 0f
+        whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha)
+            .thenReturn(intendedAlphaMutableStateFlow)
+        whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha)
+            .thenReturn(emptyFlow())
+        whenever(dreamingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(goneToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(occludedToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(offToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(primaryBouncerToLockscreenTransitionViewModel.shortcutsAlpha)
+            .thenReturn(emptyFlow())
+        whenever(lockscreenToAodTransitionViewModel.shortcutsAlpha)
+            .thenReturn(intendedAlphaMutableStateFlow)
+        whenever(lockscreenToDozingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha)
+            .thenReturn(emptyFlow())
+        whenever(lockscreenToDreamingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(lockscreenToGoneTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(lockscreenToOccludedTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+        whenever(lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha)
+            .thenReturn(emptyFlow())
+        whenever(shadeInteractor.qsExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
+
         underTest =
             KeyguardQuickAffordancesCombinedViewModel(
                 quickAffordanceInteractor =
@@ -210,7 +284,27 @@
                         backgroundDispatcher = testDispatcher,
                         appContext = mContext,
                     ),
-                keyguardInteractor = keyguardInteractor
+                keyguardInteractor = keyguardInteractor,
+                shadeInteractor = shadeInteractor,
+                aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
+                dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
+                dreamingHostedToLockscreenTransitionViewModel =
+                    dreamingHostedToLockscreenTransitionViewModel,
+                dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
+                goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel,
+                occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
+                offToLockscreenTransitionViewModel = offToLockscreenTransitionViewModel,
+                primaryBouncerToLockscreenTransitionViewModel =
+                    primaryBouncerToLockscreenTransitionViewModel,
+                lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
+                lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel,
+                lockscreenToDreamingHostedTransitionViewModel =
+                    lockscreenToDreamingHostedTransitionViewModel,
+                lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel,
+                lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel,
+                lockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel,
+                lockscreenToPrimaryBouncerTransitionViewModel =
+                    lockscreenToPrimaryBouncerTransitionViewModel
             )
     }
 
@@ -526,15 +620,15 @@
     @Test
     fun isClickable_falseWhenAlphaBelowThreshold() =
         testScope.runTest {
+            intendedAlphaMutableStateFlow.value =
+                KeyguardQuickAffordancesCombinedViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD -
+                    .1f
+            // the viewModel does a `map { 1 - it }` on this value, which is why it's different
+            intendedShadeAlphaMutableStateFlow.value =
+                KeyguardQuickAffordancesCombinedViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD +
+                    .1f
             repository.setKeyguardShowing(true)
             val latest = collectLastValue(underTest.startButton)
-            repository.setKeyguardAlpha(
-                max(
-                    0f,
-                    KeyguardQuickAffordancesCombinedViewModel
-                        .AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD - 0.1f
-                ),
-            )
 
             val testConfig =
                 TestConfig(
@@ -561,9 +655,10 @@
     @Test
     fun isClickable_falseWhenAlphaAtZero() =
         testScope.runTest {
+            intendedAlphaMutableStateFlow.value = 0f
+            intendedShadeAlphaMutableStateFlow.value = 1f
             repository.setKeyguardShowing(true)
             val latest = collectLastValue(underTest.startButton)
-            repository.setKeyguardAlpha(0f)
 
             val testConfig =
                 TestConfig(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index e6d6cf2..a57feda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -163,23 +163,6 @@
         }
 
     @Test
-    fun alpha_inPreviewMode_doesNotChange() =
-        testScope.runTest {
-            val value = collectLastValue(underTest.alpha)
-            underTest.enablePreviewMode()
-
-            assertThat(value()).isEqualTo(1f)
-            repository.setKeyguardAlpha(0.1f)
-            assertThat(value()).isEqualTo(1f)
-            repository.setKeyguardAlpha(0.5f)
-            assertThat(value()).isEqualTo(1f)
-            repository.setKeyguardAlpha(0.2f)
-            assertThat(value()).isEqualTo(1f)
-            repository.setKeyguardAlpha(0f)
-            assertThat(value()).isEqualTo(1f)
-        }
-
-    @Test
     fun translationAndScaleFromBurnInNotDozing() =
         testScope.runTest {
             val translationX by collectLastValue(underTest.translationX)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 5969bd8..0173c32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.flags.ResourceBooleanFlag
 import com.android.systemui.flags.UnreleasedFlag
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.res.R
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Rule
@@ -74,10 +75,15 @@
             .forEach { flagToken ->
                 setFlagsRule.enableFlags(flagToken)
                 aconfigFlags.setFlag(flagToken, testCase.areAllFlagsSet)
+                overrideResource(
+                    R.bool.config_sceneContainerFrameworkEnabled,
+                    testCase.isResourceConfigEnabled
+                )
             }
 
         underTest =
             SceneContainerFlagsImpl(
+                context = context,
                 featureFlagsClassic = featureFlags,
                 isComposeAvailable = testCase.isComposeAvailable,
             )
@@ -91,13 +97,12 @@
     internal data class TestCase(
         val isComposeAvailable: Boolean,
         val areAllFlagsSet: Boolean,
+        val isResourceConfigEnabled: Boolean,
         val expectedEnabled: Boolean,
     ) {
         override fun toString(): String {
-            return """
-                (compose=$isComposeAvailable + flags=$areAllFlagsSet) -> expected=$expectedEnabled
-            """
-                .trimIndent()
+            return "(compose=$isComposeAvailable + flags=$areAllFlagsSet) + XML" +
+                " config=$isResourceConfigEnabled -> expected=$expectedEnabled"
         }
     }
 
@@ -105,17 +110,20 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun testCases() = buildList {
-            repeat(4) { combination ->
-                val isComposeAvailable = combination and 0b10 != 0
-                val areAllFlagsSet = combination and 0b01 != 0
+            repeat(8) { combination ->
+                val isComposeAvailable = combination and 0b100 != 0
+                val areAllFlagsSet = combination and 0b010 != 0
+                val isResourceConfigEnabled = combination and 0b001 != 0
 
-                val expectedEnabled = isComposeAvailable && areAllFlagsSet
+                val expectedEnabled =
+                    isComposeAvailable && areAllFlagsSet && isResourceConfigEnabled
 
                 add(
                     TestCase(
                         isComposeAvailable = isComposeAvailable,
                         areAllFlagsSet = areAllFlagsSet,
                         expectedEnabled = expectedEnabled,
+                        isResourceConfigEnabled = isResourceConfigEnabled,
                     )
                 )
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 6e487cd..88c728f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -28,12 +28,16 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.activity.SingleActivityFactory
 import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -55,6 +59,8 @@
     @Mock private lateinit var brightnessControllerFactory: BrightnessController.Factory
     @Mock private lateinit var brightnessController: BrightnessController
     @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
+    @Mock private lateinit var shadeInteractorLazy: Lazy<ShadeInteractor>
+    @Mock private lateinit var shadeInteractor: ShadeInteractor
 
     private val clock = FakeSystemClock()
     private val mainExecutor = FakeExecutor(clock)
@@ -68,7 +74,8 @@
                     brightnessSliderControllerFactory,
                     brightnessControllerFactory,
                     mainExecutor,
-                    accessibilityMgr
+                    accessibilityMgr,
+                    shadeInteractor
                 )
             },
             /* initialTouchMode= */ false,
@@ -82,6 +89,8 @@
             .thenReturn(brightnessSliderController)
         `when`(brightnessSliderController.rootView).thenReturn(View(context))
         `when`(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
+        whenever(shadeInteractorLazy.get()).thenReturn(shadeInteractor)
+        whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
     }
 
     @After
@@ -175,13 +184,15 @@
         brightnessSliderControllerFactory: BrightnessSliderController.Factory,
         brightnessControllerFactory: BrightnessController.Factory,
         mainExecutor: DelayableExecutor,
-        accessibilityMgr: AccessibilityManagerWrapper
+        accessibilityMgr: AccessibilityManagerWrapper,
+        shadeInteractor: ShadeInteractor
     ) :
         BrightnessDialog(
             brightnessSliderControllerFactory,
             brightnessControllerFactory,
             mainExecutor,
-            accessibilityMgr
+            accessibilityMgr,
+            shadeInteractor
         ) {
         private var finishing = false
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 1dbb297..62c0ebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -22,6 +22,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Looper;
@@ -293,8 +295,10 @@
                 )
         );
 
-        mActiveNotificationsInteractor =
-                new ActiveNotificationsInteractor(new ActiveNotificationListRepository());
+        mActiveNotificationsInteractor = new ActiveNotificationsInteractor(
+                        new ActiveNotificationListRepository(),
+                        StandardTestDispatcher(/* scheduler = */ null, /* name = */ null)
+                );
 
         KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
         keyguardStatusView.setId(R.id.keyguard_status_view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index b86f841..6374d5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -25,14 +25,19 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 
 @SmallTest
 class RenderNotificationsListInteractorTest : SysuiTestCase() {
+    private val backgroundDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(backgroundDispatcher)
 
     private val notifsRepository = ActiveNotificationListRepository()
-    private val notifsInteractor = ActiveNotificationsInteractor(notifsRepository)
+    private val notifsInteractor =
+        ActiveNotificationsInteractor(notifsRepository, backgroundDispatcher)
     private val underTest =
         RenderNotificationListInteractor(
             notifsRepository,
@@ -40,21 +45,26 @@
         )
 
     @Test
-    fun setRenderedList_preservesOrdering() = runTest {
-        val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
-        val keys = (1..50).shuffled().map { "$it" }
-        val entries =
-            keys.map {
-                mock<ListEntry> {
-                    val mockRep = mock<NotificationEntry> {
-                        whenever(key).thenReturn(it)
-                        whenever(sbn).thenReturn(mock())
-                        whenever(icons).thenReturn(mock())
+    fun setRenderedList_preservesOrdering() =
+        testScope.runTest {
+            val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+            val keys = (1..50).shuffled().map { "$it" }
+            val entries =
+                keys.map {
+                    mock<ListEntry> {
+                        val mockRep =
+                            mock<NotificationEntry> {
+                                whenever(key).thenReturn(it)
+                                whenever(sbn).thenReturn(mock())
+                                whenever(icons).thenReturn(mock())
+                            }
+                        whenever(representativeEntry).thenReturn(mockRep)
                     }
-                    whenever(representativeEntry).thenReturn(mockRep)
                 }
-            }
-        underTest.setRenderedList(entries)
-        assertThat(notifs).comparingElementsUsing(byKey).containsExactlyElementsIn(keys).inOrder()
-    }
+            underTest.setRenderedList(entries)
+            assertThat(notifs)
+                .comparingElementsUsing(byKey)
+                .containsExactlyElementsIn(keys)
+                .inOrder()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index ff5c026..7558974 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
 
 import android.metrics.LogMaker;
 import android.testing.AndroidTestingRunner;
@@ -169,7 +170,8 @@
             new ActiveNotificationListRepository();
 
     private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
-            new ActiveNotificationsInteractor(mActiveNotificationsRepository);
+            new ActiveNotificationsInteractor(mActiveNotificationsRepository,
+                    StandardTestDispatcher(/* scheduler = */ null, /* name = */ null));
 
     private final SeenNotificationsInteractor mSeenNotificationsInteractor =
             new SeenNotificationsInteractor(mActiveNotificationsRepository);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
new file mode 100644
index 0000000..ce47170
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.hardware.devicestate.DeviceStateManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableResources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_FLIPPED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
+import com.android.systemui.statusbar.policy.DevicePostureController.SUPPORTED_POSTURES_SIZE
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DevicePostureControllerImplTest : SysuiTestCase() {
+    private val useBaseStateDeviceState = SUPPORTED_POSTURES_SIZE
+    private val deviceStateToPostureMapping =
+        arrayOf(
+            "$DEVICE_POSTURE_UNKNOWN:$DEVICE_POSTURE_UNKNOWN",
+            "$DEVICE_POSTURE_CLOSED:$DEVICE_POSTURE_CLOSED",
+            "$DEVICE_POSTURE_HALF_OPENED:$DEVICE_POSTURE_HALF_OPENED",
+            "$DEVICE_POSTURE_OPENED:$DEVICE_POSTURE_OPENED",
+            "$DEVICE_POSTURE_FLIPPED:$DEVICE_POSTURE_FLIPPED",
+            "$useBaseStateDeviceState:1000"
+        )
+    @Mock private lateinit var deviceStateManager: DeviceStateManager
+    @Captor
+    private lateinit var deviceStateCallback: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
+
+    private lateinit var mainExecutor: FakeExecutor
+    private lateinit var testableResources: TestableResources
+    private lateinit var underTest: DevicePostureControllerImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mainExecutor = FakeExecutor(FakeSystemClock())
+        testableResources = context.getOrCreateTestableResources()
+        testableResources.addOverride(
+            com.android.internal.R.array.config_device_state_postures,
+            deviceStateToPostureMapping
+        )
+        underTest =
+            DevicePostureControllerImpl(
+                context,
+                deviceStateManager,
+                mainExecutor,
+            )
+        verifyRegistersForDeviceStateCallback()
+    }
+
+    @Test
+    fun testPostureChanged_updates() {
+        var posture = -1
+        underTest.addCallback { posture = it }
+
+        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_UNKNOWN)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
+
+        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_CLOSED)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
+
+        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_OPENED)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_OPENED)
+
+        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_FLIPPED)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_FLIPPED)
+    }
+
+    @Test
+    fun testPostureChanged_useBaseUpdate() {
+        var posture = -1
+        underTest.addCallback { posture = it }
+
+        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+        // base state change doesn't change the posture
+        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+        // WHEN the display state maps to using the base state, then posture updates
+        deviceStateCallback.value.onStateChanged(useBaseStateDeviceState)
+        assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
+    }
+
+    @Test
+    fun baseStateChanges_doesNotUpdatePosture() {
+        var numPostureChanges = 0
+        underTest.addCallback { numPostureChanges++ }
+
+        deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+        assertThat(numPostureChanges).isEqualTo(1)
+
+        // base state changes doesn't send another posture update since the device state isn't
+        // useBaseStateDeviceState
+        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_HALF_OPENED)
+        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_FLIPPED)
+        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_OPENED)
+        deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_UNKNOWN)
+        assertThat(numPostureChanges).isEqualTo(1)
+    }
+
+    private fun verifyRegistersForDeviceStateCallback() {
+        verify(deviceStateManager).registerCallback(eq(mainExecutor), deviceStateCallback.capture())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
new file mode 100644
index 0000000..4e61b89
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold.progress
+
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.utils.os.FakeHandler
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+class MainThreadUnfoldTransitionProgressProviderTest : SysuiTestCase() {
+
+    private val wrappedProgressProvider = TestUnfoldTransitionProvider()
+    private val fakeHandler = FakeHandler(Looper.getMainLooper())
+    private val listener = TestUnfoldProgressListener()
+
+    private val progressProvider =
+        MainThreadUnfoldTransitionProgressProvider(fakeHandler, wrappedProgressProvider)
+
+    @Test
+    fun onTransitionStarted_propagated() {
+        progressProvider.addCallback(listener)
+
+        wrappedProgressProvider.onTransitionStarted()
+        fakeHandler.dispatchQueuedMessages()
+
+        listener.assertStarted()
+    }
+
+    @Test
+    fun onTransitionProgress_propagated() {
+        progressProvider.addCallback(listener)
+
+        wrappedProgressProvider.onTransitionStarted()
+        wrappedProgressProvider.onTransitionProgress(0.5f)
+        fakeHandler.dispatchQueuedMessages()
+
+        listener.assertLastProgress(0.5f)
+    }
+
+    @Test
+    fun onTransitionFinished_propagated() {
+        progressProvider.addCallback(listener)
+
+        wrappedProgressProvider.onTransitionStarted()
+        wrappedProgressProvider.onTransitionProgress(0.5f)
+        wrappedProgressProvider.onTransitionFinished()
+        fakeHandler.dispatchQueuedMessages()
+
+        listener.ensureTransitionFinished()
+    }
+
+    @Test
+    fun onTransitionFinishing_propagated() {
+        progressProvider.addCallback(listener)
+
+        wrappedProgressProvider.onTransitionStarted()
+        wrappedProgressProvider.onTransitionProgress(0.5f)
+        wrappedProgressProvider.onTransitionFinished()
+        fakeHandler.dispatchQueuedMessages()
+
+        listener.ensureTransitionFinished()
+    }
+
+    @Test
+    fun onTransitionStarted_afterCallbackRemoved_notPropagated() {
+        progressProvider.addCallback(listener)
+        progressProvider.removeCallback(listener)
+
+        wrappedProgressProvider.onTransitionStarted()
+        fakeHandler.dispatchQueuedMessages()
+
+        listener.assertNotStarted()
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
index 3d7fb6d..01f4535 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
@@ -17,7 +17,10 @@
 package com.android.systemui.statusbar.notification.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 
 val Kosmos.activeNotificationsInteractor by
-    Kosmos.Fixture { ActiveNotificationsInteractor(activeNotificationListRepository) }
+    Kosmos.Fixture {
+        ActiveNotificationsInteractor(activeNotificationListRepository, testDispatcher)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
index 61a38b8..432464e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
@@ -18,11 +18,13 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 
 val Kosmos.notificationsKeyguardInteractor by Fixture {
     NotificationsKeyguardInteractor(
         repository = notificationsKeyguardViewStateRepository,
+        backgroundDispatcher = testDispatcher,
     )
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index 42d31b3..f7fb014 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -19,8 +19,10 @@
 import android.os.Handler
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.dagger.UnfoldBg
+import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.systemui.unfold.progress.MainThreadUnfoldTransitionProgressProvider
 import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
 import com.android.systemui.unfold.updates.DeviceFoldStateProvider
@@ -36,15 +38,18 @@
 import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
 import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManagerImpl
 import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
+import dagger.BindsOptionalOf
 import dagger.Module
 import dagger.Provides
 import java.util.Optional
 import javax.inject.Provider
 import javax.inject.Singleton
+import kotlin.jvm.optionals.getOrDefault
 
 @Module(
     includes =
         [
+            UnfoldFlagsModule::class,
             UnfoldSharedInternalModule::class,
             UnfoldRotationProviderInternalModule::class,
             HingeAngleProviderInternalModule::class,
@@ -69,6 +74,16 @@
     fun foldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository = impl
 }
 
+@Module
+abstract class UnfoldFlagsModule {
+    /**
+     * Users of the library can bind this boolean to notify whether the progress should be
+     * calculated only in the background (and the main thread provider is generated by posting the
+     * background events in the main handler).
+     */
+    @BindsOptionalOf @UnfoldBgProgressFlag abstract fun unfoldBgProgressFlag(): Boolean
+}
+
 /**
  * Needed as methods inside must be public, but their parameters can be internal (and, a public
  * method can't have internal parameters). Making the module internal and included in a public one
@@ -87,17 +102,34 @@
         fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
         foldStateProvider: FoldStateProvider,
         @UnfoldMain mainHandler: Handler,
+        mainThreadUnfoldTransitionProgressProviderFactory:
+            MainThreadUnfoldTransitionProgressProvider.Factory,
+        @UnfoldBg bgProvider: Provider<Optional<UnfoldTransitionProgressProvider>>,
+        @UnfoldBgProgressFlag unfoldBgProgressFlag: Optional<Boolean>,
     ): Optional<UnfoldTransitionProgressProvider> {
-        return createOptionalUnfoldTransitionProgressProvider(
-            config = config,
-            scaleAwareProviderFactory = scaleAwareProviderFactory,
-            tracingListener = tracingListener.create("MainThread"),
-            physicsBasedUnfoldTransitionProgressProvider =
-                physicsBasedUnfoldTransitionProgressProvider,
-            fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
-            foldStateProvider = foldStateProvider,
-            progressHandler = mainHandler,
-        )
+        if (unfoldBgProgressFlag.getOrDefault(false)) {
+            // In this case, we wrap the background progress provider
+            val mainThreadProvider: Optional<UnfoldTransitionProgressProvider> =
+                bgProvider.get().map {
+                    mainThreadUnfoldTransitionProgressProviderFactory.create(it)
+                }
+            mainThreadProvider.ifPresent {
+                it.addCallback(tracingListener.create("MainThreadFromBgProgress"))
+            }
+            return mainThreadProvider
+        } else {
+            // TODO(b/277879146): Remove this once unfold_animation_background_progress is launched.
+            return createOptionalUnfoldTransitionProgressProvider(
+                config = config,
+                scaleAwareProviderFactory = scaleAwareProviderFactory,
+                tracingListener = tracingListener.create("MainThread"),
+                physicsBasedUnfoldTransitionProgressProvider =
+                    physicsBasedUnfoldTransitionProgressProvider,
+                fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
+                foldStateProvider = foldStateProvider,
+                progressHandler = mainHandler,
+            )
+        }
     }
 
     @Provides
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt
similarity index 63%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
copy to packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt
index ee36989..0e371fa 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.credentialmanager.common
+package com.android.systemui.unfold.dagger
 
-import android.app.PendingIntent
-import android.content.Intent
+import javax.inject.Qualifier
 
-open class BaseEntry (
-    val providerId: String,
-    val entryKey: String,
-    val entrySubkey: String,
-    val pendingIntent: PendingIntent?,
-    val fillInIntent: Intent?,
-    val shouldTerminateUiUponSuccessfulProviderResult: Boolean,
-)
\ No newline at end of file
+/**
+ * Annotates the boolean representing whether we are calculating progresses in the background.
+ *
+ * Used to allow clients to provide this value, without depending on the flags directly.
+ */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldBgProgressFlag
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
new file mode 100644
index 0000000..9bdf3d5
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold.progress
+
+import android.os.Handler
+import androidx.annotation.FloatRange
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.dagger.UnfoldMain
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * [UnfoldTransitionProgressProvider] that forwards all progress to the main thread handler.
+ *
+ * This is needed when progress are calculated in the background, but some listeners need the
+ * callbacks in the main thread.
+ */
+class MainThreadUnfoldTransitionProgressProvider
+@AssistedInject
+constructor(
+    @UnfoldMain private val mainHandler: Handler,
+    @Assisted private val rootProvider: UnfoldTransitionProgressProvider
+) : UnfoldTransitionProgressProvider {
+
+    private val listenerMap = mutableMapOf<TransitionProgressListener, TransitionProgressListener>()
+
+    override fun addCallback(listener: TransitionProgressListener) {
+        assertMainThread()
+        val proxy = TransitionProgressListerProxy(listener)
+        rootProvider.addCallback(proxy)
+        listenerMap[listener] = proxy
+    }
+
+    override fun removeCallback(listener: TransitionProgressListener) {
+        assertMainThread()
+        val proxy = listenerMap.remove(listener) ?: return
+        rootProvider.removeCallback(proxy)
+    }
+
+    private fun assertMainThread() {
+        check(mainHandler.looper.isCurrentThread) {
+            "Should be called from the main thread, but this is ${Thread.currentThread()}"
+        }
+    }
+
+    override fun destroy() {
+        rootProvider.destroy()
+    }
+
+    inner class TransitionProgressListerProxy(private val listener: TransitionProgressListener) :
+        TransitionProgressListener {
+        override fun onTransitionStarted() {
+            mainHandler.post { listener.onTransitionStarted() }
+        }
+
+        override fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {
+            mainHandler.post { listener.onTransitionProgress(progress) }
+        }
+
+        override fun onTransitionFinishing() {
+            mainHandler.post { listener.onTransitionFinishing() }
+        }
+
+        override fun onTransitionFinished() {
+            mainHandler.post { listener.onTransitionFinished() }
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        /** Creates a [MainThreadUnfoldTransitionProgressProvider] that wraps the [rootProvider]. */
+        fun create(
+            rootProvider: UnfoldTransitionProgressProvider
+        ): MainThreadUnfoldTransitionProgressProvider
+    }
+}
diff --git a/ravenwood/README.md b/ravenwood/README.md
new file mode 100644
index 0000000..9c4fda7
--- /dev/null
+++ b/ravenwood/README.md
@@ -0,0 +1,28 @@
+# Ravenwood
+
+Ravenwood is an officially-supported lightweight unit testing environment for Android platform code that runs on the host.
+
+Ravenwood’s focus on Android platform use-cases, improved maintainability, and device consistency distinguishes it from Robolectric, which remains a popular choice for app testing.
+
+## Background
+
+Executing tests on a typical Android device has substantial overhead, such as flashing the build, waiting for the boot to complete, and retrying tests that fail due to general flakiness.
+
+In contrast, defining a lightweight unit testing environment mitigates these issues by running directly from build artifacts (no flashing required), runs immediately (no booting required), and runs in an isolated environment (less flakiness).
+
+## Guiding principles
+Here’s a summary of the guiding principles for Ravenwood, aimed at addressing Robolectric design concerns and better supporting Android platform developers:
+
+* **API support for Ravenwood is opt-in.**  Teams that own APIs decide exactly what, and how, they support their API functionality being available to tests.  When an API hasn’t opted-in, the API signatures remain available for tests to compile against and/or mock, but they throw when called under a Ravenwood environment.
+    * _Contrasted with Robolectric which attempts to run API implementations as-is, causing maintenance pains as teams maintain or redesign their API internals._
+* **API support and customizations for Ravenwood appear directly inline with relevant code.** This improves maintenance of APIs by providing awareness of what code runs under Ravenwood, including the ability to replace code at a per-method level when Ravenwood-specific customization is needed.
+    * _Contrasted with Robolectric which maintains customized behavior in separate “Shadow” classes that are difficult for maintainers to be aware of._
+* **APIs supported under Ravenwood are tested to remain consistent with physical devices.**  As teams progressively opt-in supporting APIs under Ravenwood, we’re requiring they bring along “bivalent” tests (such as the relevant CTS) to validate that Ravenwood behaves just like a physical device.
+    * _Contrasted with Robolectric, which has limited (and forked) testing of their environment, increasing their risk of accidental divergence over time and misleading “passing” signals._
+* **Ravenwood aims to support more “real” code.**  As API owners progressively opt-in their code, they have the freedom to provide either a limited “fake” that is a faithful emulation of how a device behaves, or they can bring more “real” code that runs on physical devices.
+    * _Contrasted with Robolectric, where support for “real” code ends at the app process boundary, such as a call into `system_server`._
+
+## More details
+
+* [Ravenwood for Test Authors](test-authors.md)
+* [Ravenwood for API Maintainers](api-maintainers.md)
diff --git a/ravenwood/api-maintainers.md b/ravenwood/api-maintainers.md
new file mode 100644
index 0000000..30e899c
--- /dev/null
+++ b/ravenwood/api-maintainers.md
@@ -0,0 +1,73 @@
+# Ravenwood for API Maintainers
+
+By default, Android APIs aren’t opted-in to Ravenwood, and they default to throwing when called under the Ravenwood environment.
+
+To opt-in to supporting an API under Ravenwood, you can use the inline annotations documented below to customize your API behavior when running under Ravenwood.  Because these annotations are inline in the relevant platform source code, they serve as valuable reminders to future API maintainers of Ravenwood support expectations.
+
+> **Note:** to ensure that API teams are well-supported during early Ravenwood onboarding, the Ravenwood team is manually maintaining an allow-list of classes that are able to use Ravenwood annotations.  Please reach out to ravenwood@ so we can offer design advice and allow-list your APIs.
+
+These Ravenwood-specific annotations have no bearing on the status of an API being public, `@SystemApi`, `@TestApi`, `@hide`, etc.  Ravenwood annotations are an orthogonal concept that are only consumed by the internal `hoststubgen` tool during a post-processing step that generates the Ravenwood runtime environment.  Teams that own APIs can continue to refactor opted-in `@hide` implementation details, as long as the test-visible behavior continues passing.
+
+As described in our Guiding Principles, when a team opts-in an API, we’re requiring that they bring along “bivalent” tests (such as the relevant CTS) to validate that Ravenwood behaves just like a physical device.  At the moment this means adding the bivalent tests to relevant `TEST_MAPPING` files to ensure they remain consistently passing over time.  These bivalent tests are important because they progressively provide the foundation on which higher-level unit tests place their trust.
+
+## Opt-in to supporting a single method while other methods remained opt-out
+
+```
+@RavenwoodKeepPartialClass
+public class MyManager {
+    @RavenwoodKeep
+    public static String modeToString(int mode) {
+        // This method implementation runs as-is on both devices and Ravenwood
+    }
+
+    public static void doComplex() {
+        // This method implementation runs as-is on devices, but because there
+        // is no method-level annotation, and the class-level default is
+        // “keep partial”, this method is not supported under Ravenwood and
+        // will throw
+    }
+}
+```
+
+## Opt-in an entire class with opt-out of specific methods
+
+```
+@RavenwoodKeepWholeClass
+public class MyStruct {
+    public void doSimple() {
+        // This method implementation runs as-is on both devices and Ravenwood,
+        // implicitly inheriting the class-level annotation
+    }
+
+    @RavenwoodThrow
+    public void doComplex() {
+        // This method implementation runs as-is on devices, but the
+        // method-level annotation overrides the class-level annotation, so
+        // this method is not supported under Ravenwood and will throw
+    }
+}
+```
+
+## Replace a complex method when under Ravenwood
+
+```
+@RavenwoodKeepWholeClass
+public class MyStruct {
+    @RavenwoodReplace
+    public void doComplex() {
+        // This method implementation runs as-is on devices, but the
+        // implementation is replaced/substituted by the
+        // doComplex$ravenwood() method implementation under Ravenwood
+    }
+
+    public void doComplex$ravenwood() {
+        // This method implementation only runs under Ravenwood
+    }
+}
+```
+
+## General strategies for side-stepping tricky dependencies
+
+The “replace” strategy described above is quite powerful, and can be used in creative ways to sidestep tricky underlying dependencies that aren’t ready yet.
+
+For example, consider a constructor or static initializer that relies on unsupported functionality from another team.  By factoring the unsupported logic into a dedicated method, that method can then be replaced under Ravenwood to offer baseline functionality.
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index c70c171..79bfa44 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -1,5 +1,8 @@
 # Ravenwood "policy" file for framework-minus-apex.
 
+# Keep all AIDL interfaces
+class :aidl stubclass
+
 # Collections
 class android.util.ArrayMap stubclass
 class android.util.ArraySet stubclass
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
new file mode 100644
index 0000000..2b5bd908
--- /dev/null
+++ b/ravenwood/test-authors.md
@@ -0,0 +1,132 @@
+# Ravenwood for Test Authors
+
+The Ravenwood testing environment runs inside a single Java process on the host side, and provides a limited yet growing set of Android API functionality.
+
+Ravenwood explicitly does not support “large” integration tests that expect a fully booted Android OS.  Instead, it’s more suited for “small” and “medium” tests where your code-under-test has been factored to remove dependencies on a fully booted device.
+
+When writing tests under Ravenwood, all Android API symbols associated with your declared `sdk_version` are available to link against using, but unsupported APIs will throw an exception.  This design choice enables mocking of unsupported APIs, and supports sharing of test code to build “bivalent” test suites that run against either Ravenwood or a traditional device.
+
+## Typical test structure
+
+Below are the typical steps needed to add a straightforward “small” unit test:
+
+* Define an `android_ravenwood_test` rule in your `Android.bp` file:
+
+```
+android_ravenwood_test {
+    name: "MyTestsRavenwood",
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.rules",
+    ],
+    srcs: [
+        "src/com/example/MyCode.java",
+        "tests/src/com/example/MyCodeTest.java",
+    ],
+    sdk_version: "test_current",
+    auto_gen_config: true,
+}
+```
+
+* Write your unit test just like you would for an Android device:
+
+```
+@RunWith(AndroidJUnit4.class)
+public class MyCodeTest {
+    @Test
+    public void testSimple() {
+        // ...
+    }
+}
+```
+
+* APIs available under Ravenwood are stateless by default.  If your test requires explicit states (such as defining the UID you’re running under, or requiring a main `Looper` thread), add a `RavenwoodRule` to declare that:
+
+```
+@RunWith(AndroidJUnit4.class)
+public class MyCodeTest {
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProcessApp()
+            .setProvideMainThread(true)
+            .build();
+```
+
+Once you’ve defined your test, you can use typical commands to execute it locally:
+
+```
+$ atest MyTestsRavenwood
+```
+
+> **Note:** There's a known bug where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing.
+
+You can also run your new tests automatically via `TEST_MAPPING` rules like this:
+
+```
+{
+  "ravenwood-presubmit": [
+    {
+      "name": "MyTestsRavenwood",
+      "host": true
+    }
+  ]
+}
+```
+
+## Strategies for migration/bivalent tests
+
+Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can run on both a real Android device and under a Ravenwood environment.
+
+In situations where a test method depends on API functionality not yet available under Ravenwood, we provide an annotation to quietly “ignore” that test under Ravenwood, while continuing to validate that test on real devices.  Please note that your test must declare a `RavenwoodRule` for the annotation to take effect.
+
+Test authors are encouraged to provide a `blockedBy` or `reason` argument to help future maintainers understand why a test is being ignored, and under what conditions it might be supported in the future.
+
+```
+@RunWith(AndroidJUnit4.class)
+public class MyCodeTest {
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    @Test
+    public void testSimple() {
+        // Simple test that runs on both devices and Ravenwood
+    }
+
+    @Test
+    @IgnoreUnderRavenwood(blockedBy = PackageManager.class)
+    public void testComplex() {
+        // Complex test that runs on devices, but is ignored under Ravenwood
+    }
+}
+```
+
+## Strategies for unsupported APIs
+
+As you write tests against Ravenwood, you’ll likely discover API dependencies that aren’t supported yet.  Here’s a few strategies that can help you make progress:
+
+* Your code-under-test may benefit from subtle dependency refactoring to reduce coupling.  (For example, providing a specific `File` argument instead of deriving it internally from a `Context`.)
+* Although mocking code that your team doesn’t own is a generally discouraged testing practice, it can be a valuable pressure relief valve when a dependency isn’t yet supported.
+
+## Strategies for debugging test development
+
+When writing tests you may encounter odd or hard to debug behaviors.  One good place to start is at the beginning of the logs stored by atest:
+
+```
+$ atest MyTestsRavenwood
+...
+Test Logs have saved in /tmp/atest_result/20231128_094010_0e90t8v8/log
+Run 'atest --history' to review test result history.
+```
+
+The most useful logs are in the `isolated-java-logs` text file, which can typically be tab-completed by copy-pasting the logs path mentioned in the atest output:
+
+```
+$ less /tmp/atest_result/20231128_133105_h9al__79/log/i*/i*/isolated-java-logs*
+```
+
+Here are some common known issues and recommended workarounds:
+
+* Some code may unconditionally interact with unsupported APIs, such as via static initializers.  One strategy is to shift the logic into `@Before` methods and make it conditional by testing `RavenwoodRule.isUnderRavenwood()`.
+* Some code may reference API symbols not yet present in the Ravenwood runtime, such as ART or ICU internals, or APIs from Mainline modules.  One strategy is to refactor to avoid these internal dependencies, but Ravenwood aims to better support them soon.
+    * This may also manifest as very odd behavior, such as test not being executed at all, tracked by bug #312517322
+    * This may also manifest as an obscure Mockito error claiming “Mockito can only mock non-private & non-final classes”
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 74538ac..6cac6a4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -668,7 +668,7 @@
             final Context uiContext = displayContext.createWindowContext(
                     TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
             magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
-                    mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
+                    mAms.getMagnificationConnectionManager(), mAms.getTraceManager(),
                     mAms.getMagnificationController(),
                     detectControlGestures,
                     detectTwoFingerTripleTap,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b5e8c84..2eecb4d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -158,10 +158,10 @@
 import com.android.server.AccessibilityManagerInternal;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
 import com.android.server.accessibility.magnification.MagnificationController;
 import com.android.server.accessibility.magnification.MagnificationProcessor;
 import com.android.server.accessibility.magnification.MagnificationScaleProvider;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
@@ -3442,7 +3442,7 @@
                 && (userState.getMagnificationCapabilitiesLocked()
                 != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
                 || userHasMagnificationServicesLocked(userState);
-        getWindowMagnificationMgr().requestConnection(connect);
+        getMagnificationConnectionManager().requestConnection(connect);
     }
 
     /**
@@ -4122,17 +4122,17 @@
         mSecurityPolicy.enforceCallingOrSelfPermission(
                 android.Manifest.permission.STATUS_BAR_SERVICE);
 
-        getWindowMagnificationMgr().setConnection(connection);
+        getMagnificationConnectionManager().setConnection(connection);
     }
 
     /**
-     * Getter of {@link WindowMagnificationManager}.
+     * Getter of {@link MagnificationConnectionManager}.
      *
-     * @return WindowMagnificationManager
+     * @return MagnificationManager
      */
-    public WindowMagnificationManager getWindowMagnificationMgr() {
+    public MagnificationConnectionManager getMagnificationConnectionManager() {
         synchronized (mLock) {
-            return mMagnificationController.getWindowMagnificationMgr();
+            return mMagnificationController.getMagnificationConnectionManager();
         }
     }
 
@@ -4423,7 +4423,7 @@
                 pw.println();
             }
             pw.append("hasWindowMagnificationConnection=").append(
-                    String.valueOf(getWindowMagnificationMgr().isConnected()));
+                    String.valueOf(getMagnificationConnectionManager().isConnected()));
             pw.println();
             mMagnificationProcessor.dump(pw, getValidDisplayList());
             final int userCount = mUserStates.size();
@@ -5144,7 +5144,7 @@
 
             for (int i = 0; i < displays.size(); i++) {
                 final int displayId = displays.get(i).getDisplayId();
-                getWindowMagnificationMgr().removeMagnificationButton(displayId);
+                getMagnificationConnectionManager().removeMagnificationButton(displayId);
             }
         }
     }
@@ -5580,6 +5580,8 @@
 
     @Override
     public void injectInputEventToInputFilter(InputEvent event) {
+        mSecurityPolicy.enforceCallingPermission(Manifest.permission.INJECT_EVENTS,
+                "injectInputEventToInputFilter");
         synchronized (mLock) {
             final long endMillis =
                     SystemClock.uptimeMillis() + WAIT_INPUT_FILTER_INSTALL_TIMEOUT_MS;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
similarity index 94%
rename from services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
rename to services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index 3ea805b..5a3c070 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -60,19 +60,19 @@
 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
 
 /**
- * A class to manipulate window magnification through {@link MagnificationConnectionWrapper}
+ * A class to manipulate magnification through {@link MagnificationConnectionWrapper}
  * create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
  * SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}.
  * The applied magnification scale is constrained by
  * {@link MagnificationScaleProvider#constrainScale(float)}
  */
-public class WindowMagnificationManager implements
+public class MagnificationConnectionManager implements
         PanningScalingHandler.MagnificationDelegate,
         WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
 
     private static final boolean DBG = false;
 
-    private static final String TAG = "WindowMagnificationMgr";
+    private static final String TAG = "MagnificationConnectionManager";
 
     /**
      * Indicate that the magnification window is at the magnification center.
@@ -208,7 +208,7 @@
     private final AccessibilityTraceManager mTrace;
     private final MagnificationScaleProvider mScaleProvider;
 
-    public WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback,
+    public MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback,
             AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
         mContext = context;
         mLock = lock;
@@ -1040,7 +1040,7 @@
         private float mScale = MagnificationScaleProvider.MIN_SCALE;
         private boolean mEnabled;
 
-        private final WindowMagnificationManager mWindowMagnificationManager;
+        private final MagnificationConnectionManager mMagnificationConnectionManager;
         // Records the bounds of window magnification.
         private final Rect mBounds = new Rect();
         // The magnified bounds on the screen.
@@ -1058,11 +1058,15 @@
                         "mTrackingTypingFocusSumTime");
         private volatile long mTrackingTypingFocusSumTime = 0;
 
-        WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
+        WindowMagnifier(int displayId,
+                MagnificationConnectionManager magnificationConnectionManager) {
             mDisplayId = displayId;
-            mWindowMagnificationManager = windowMagnificationManager;
+            mMagnificationConnectionManager = magnificationConnectionManager;
         }
 
+        // TODO(b/312324808): Investigating whether
+        //  mMagnificationConnectionManager#enableWindowMagnificationInternal requires a sync lock
+        @SuppressWarnings("GuardedBy")
         boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
                 @Nullable MagnificationAnimationCallback animationCallback,
                 @WindowPosition int windowPosition, int id) {
@@ -1072,8 +1076,8 @@
             }
             final float normScale = MagnificationScaleProvider.constrainScale(scale);
             setMagnificationFrameOffsetRatioByWindowPosition(windowPosition);
-            if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
-                    centerX, centerY, mMagnificationFrameOffsetRatio.x,
+            if (mMagnificationConnectionManager.enableWindowMagnificationInternal(mDisplayId,
+                    normScale, centerX, centerY, mMagnificationFrameOffsetRatio.x,
                     mMagnificationFrameOffsetRatio.y, animationCallback)) {
                 mScale = normScale;
                 mEnabled = true;
@@ -1096,12 +1100,15 @@
             }
         }
 
+        // TODO(b/312324808): Investigating whether
+        //  mMagnificationConnectionManager#disableWindowMagnificationInternal requires a sync lock
+        @SuppressWarnings("GuardedBy")
         boolean disableWindowMagnificationInternal(
                 @Nullable MagnificationAnimationCallback animationResultCallback) {
             if (!mEnabled) {
                 return false;
             }
-            if (mWindowMagnificationManager.disableWindowMagnificationInternal(
+            if (mMagnificationConnectionManager.disableWindowMagnificationInternal(
                     mDisplayId, animationResultCallback)) {
                 mEnabled = false;
                 mIdOfLastServiceToControl = INVALID_SERVICE_ID;
@@ -1112,6 +1119,10 @@
             return false;
         }
 
+        // ErrorProne says the access of mMagnificationConnectionManager#setScaleInternal should
+        // be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one as
+        // 'mLock'. Therefore, we'll put @SuppressWarnings here.
+        @SuppressWarnings("GuardedBy")
         @GuardedBy("mLock")
         void setScale(float scale) {
             if (!mEnabled) {
@@ -1119,7 +1130,8 @@
             }
             final float normScale = MagnificationScaleProvider.constrainScale(scale);
             if (Float.compare(mScale, normScale) != 0
-                    && mWindowMagnificationManager.setScaleInternal(mDisplayId, scale)) {
+                    && mMagnificationConnectionManager
+                        .setScaleForWindowMagnificationInternal(mDisplayId, scale)) {
                 mScale = normScale;
             }
         }
@@ -1159,8 +1171,8 @@
         }
 
         void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) {
-            if (mWindowMagnificationManager.isWindowMagnifierEnabled(mDisplayId)
-                    && mWindowMagnificationManager.isImeVisible(mDisplayId)
+            if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
+                    && mMagnificationConnectionManager.isImeVisible(mDisplayId)
                     && trackingTypingFocusEnabled) {
                 startTrackingTypingFocusRecord();
             }
@@ -1206,7 +1218,7 @@
                     Slog.d(TAG, "stop and log: session duration = " + duration
                             + ", elapsed = " + elapsed);
                 }
-                mWindowMagnificationManager.logTrackingTypingFocus(duration);
+                mMagnificationConnectionManager.logTrackingTypingFocus(duration);
                 mTrackingTypingFocusStartTime = 0;
                 mTrackingTypingFocusSumTime = 0;
             }
@@ -1216,9 +1228,14 @@
             return mEnabled;
         }
 
+        // ErrorProne says the access of mMagnificationConnectionManager#moveWindowMagnifierInternal
+        // should be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one
+        // as 'mLock'. Therefore, we'll put @SuppressWarnings here.
+        @SuppressWarnings("GuardedBy")
         @GuardedBy("mLock")
         void move(float offsetX, float offsetY) {
-            mWindowMagnificationManager.moveWindowMagnifierInternal(mDisplayId, offsetX, offsetY);
+            mMagnificationConnectionManager.moveWindowMagnifierInternal(
+                    mDisplayId, offsetX, offsetY);
         }
 
         @GuardedBy("mLock")
@@ -1270,8 +1287,10 @@
                 animationCallback);
     }
 
-    private boolean setScaleInternal(int displayId, float scale) {
-        return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
+    @GuardedBy("mLock")
+    private boolean setScaleForWindowMagnificationInternal(int displayId, float scale) {
+        return mConnectionWrapper != null
+                && mConnectionWrapper.setScaleForWindowMagnification(displayId, scale);
     }
 
     @GuardedBy("mLock")
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
index f0c44d6..20538f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
@@ -82,16 +82,16 @@
         return true;
     }
 
-    boolean setScale(int displayId, float scale) {
+    boolean setScaleForWindowMagnification(int displayId, float scale) {
         if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
             mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
                     "displayId=" + displayId + ";scale=" + scale);
         }
         try {
-            mConnection.setScale(displayId, scale);
+            mConnection.setScaleForWindowMagnification(displayId, scale);
         } catch (RemoteException e) {
             if (DBG) {
-                Slog.e(TAG, "Error calling setScale()", e);
+                Slog.e(TAG, "Error calling setScaleForWindowMagnification()", e);
             }
             return false;
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index effd873..52e123a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -77,7 +77,7 @@
  *  <b>Note</b> Updates magnification switch UI when magnification mode transition
  *  is done and before invoking {@link TransitionCallBack#onResult}.
  */
-public class MagnificationController implements WindowMagnificationManager.Callback,
+public class MagnificationController implements MagnificationConnectionManager.Callback,
         MagnificationGestureHandler.Callback,
         FullScreenMagnificationController.MagnificationInfoChangedCallback,
         WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
@@ -96,7 +96,7 @@
     private final AlwaysOnMagnificationFeatureFlag mAlwaysOnMagnificationFeatureFlag;
     private final MagnificationScaleProvider mScaleProvider;
     private FullScreenMagnificationController mFullScreenMagnificationController;
-    private WindowMagnificationManager mWindowMagnificationMgr;
+    private MagnificationConnectionManager mMagnificationConnectionManager;
     private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
     /** Whether the platform supports window magnification feature. */
     private final boolean mSupportWindowMagnification;
@@ -164,11 +164,11 @@
     @VisibleForTesting
     public MagnificationController(AccessibilityManagerService ams, Object lock,
             Context context, FullScreenMagnificationController fullScreenMagnificationController,
-            WindowMagnificationManager windowMagnificationManager,
+            MagnificationConnectionManager magnificationConnectionManager,
             MagnificationScaleProvider scaleProvider, Executor backgroundExecutor) {
         this(ams, lock, context, scaleProvider, backgroundExecutor);
         mFullScreenMagnificationController = fullScreenMagnificationController;
-        mWindowMagnificationMgr = windowMagnificationManager;
+        mMagnificationConnectionManager = magnificationConnectionManager;
     }
 
     @Override
@@ -179,10 +179,10 @@
             if (updatePersistence) {
                 getFullScreenMagnificationController().persistScale(displayId);
             }
-        } else if (getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId)) {
-            getWindowMagnificationMgr().setScale(displayId, scale);
+        } else if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) {
+            getMagnificationConnectionManager().setScale(displayId, scale);
             if (updatePersistence) {
-                getWindowMagnificationMgr().persistScale(displayId);
+                getMagnificationConnectionManager().persistScale(displayId);
             }
         }
     }
@@ -222,15 +222,15 @@
         }
 
         if (showModeSwitchButton) {
-            getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
+            getMagnificationConnectionManager().showMagnificationButton(displayId, mode);
         } else {
-            getWindowMagnificationMgr().removeMagnificationButton(displayId);
+            getMagnificationConnectionManager().removeMagnificationButton(displayId);
         }
 
         if (!enableSettingsPanel) {
             // Whether the settings panel needs to be shown is controlled in system UI.
             // Here, we only guarantee that the settings panel is closed when it is not needed.
-            getWindowMagnificationMgr().removeMagnificationSettingsPanel(displayId);
+            getMagnificationConnectionManager().removeMagnificationSettingsPanel(displayId);
         }
     }
 
@@ -284,7 +284,8 @@
 
         final FullScreenMagnificationController screenMagnificationController =
                 getFullScreenMagnificationController();
-        final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+        final MagnificationConnectionManager magnificationConnectionManager =
+                getMagnificationConnectionManager();
         final float scale = getTargetModeScaleFromCurrentMagnification(displayId, targetMode);
         final DisableMagnificationCallback animationEndCallback =
                 new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
@@ -295,7 +296,7 @@
         if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
             screenMagnificationController.reset(displayId, animationEndCallback);
         } else {
-            windowMagnificationMgr.disableWindowMagnification(displayId, false,
+            magnificationConnectionManager.disableWindowMagnification(displayId, false,
                     animationEndCallback);
         }
     }
@@ -340,7 +341,8 @@
             }
             final FullScreenMagnificationController screenMagnificationController =
                     getFullScreenMagnificationController();
-            final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+            final MagnificationConnectionManager magnificationConnectionManager =
+                    getMagnificationConnectionManager();
             final float targetScale = Float.isNaN(config.getScale())
                     ? getTargetModeScaleFromCurrentMagnification(displayId, targetMode)
                     : config.getScale();
@@ -353,14 +355,15 @@
                 if (targetMode == MAGNIFICATION_MODE_WINDOW) {
                     screenMagnificationController.reset(displayId, false);
                     if (targetActivated) {
-                        windowMagnificationMgr.enableWindowMagnification(displayId,
+                        magnificationConnectionManager.enableWindowMagnification(displayId,
                                 targetScale, magnificationCenter.x, magnificationCenter.y,
                                 magnificationAnimationCallback, id);
                     } else {
-                        windowMagnificationMgr.disableWindowMagnification(displayId, false);
+                        magnificationConnectionManager.disableWindowMagnification(displayId, false);
                     }
                 } else if (targetMode == MAGNIFICATION_MODE_FULLSCREEN) {
-                    windowMagnificationMgr.disableWindowMagnification(displayId, false, null);
+                    magnificationConnectionManager.disableWindowMagnification(
+                            displayId, false, null);
                     if (targetActivated) {
                         if (!screenMagnificationController.isRegistered(displayId)) {
                             screenMagnificationController.register(displayId);
@@ -409,7 +412,7 @@
         if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
             return getFullScreenMagnificationController().getScale(displayId);
         } else {
-            return getWindowMagnificationMgr().getScale(displayId);
+            return getMagnificationConnectionManager().getScale(displayId);
         }
     }
 
@@ -441,7 +444,8 @@
             mAccessibilityCallbacksDelegateArray.put(displayId,
                     getFullScreenMagnificationController());
         } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
-            mAccessibilityCallbacksDelegateArray.put(displayId, getWindowMagnificationMgr());
+            mAccessibilityCallbacksDelegateArray.put(
+                    displayId, getMagnificationConnectionManager());
         } else {
             mAccessibilityCallbacksDelegateArray.delete(displayId);
         }
@@ -462,13 +466,13 @@
 
     @Override
     public void onRequestMagnificationSpec(int displayId, int serviceId) {
-        final WindowMagnificationManager windowMagnificationManager;
+        final MagnificationConnectionManager magnificationConnectionManager;
         synchronized (mLock) {
             updateMagnificationUIControls(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-            windowMagnificationManager = mWindowMagnificationMgr;
+            magnificationConnectionManager = mMagnificationConnectionManager;
         }
-        if (windowMagnificationManager != null) {
-            mWindowMagnificationMgr.disableWindowMagnification(displayId, false);
+        if (magnificationConnectionManager != null) {
+            mMagnificationConnectionManager.disableWindowMagnification(displayId, false);
         }
     }
 
@@ -491,7 +495,7 @@
                 setCurrentMagnificationModeAndSwitchDelegate(displayId,
                         ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
                 duration = SystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId);
-                scale = mWindowMagnificationMgr.getLastActivatedScale(displayId);
+                scale = mMagnificationConnectionManager.getLastActivatedScale(displayId);
             }
             logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration, scale);
         }
@@ -507,13 +511,14 @@
     public void onSourceBoundsChanged(int displayId, Rect bounds) {
         if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_WINDOW)) {
             // notify sysui the magnification scale changed on window magnifier
-            mWindowMagnificationMgr.onUserMagnificationScaleChanged(
-                    mUserId, displayId, getWindowMagnificationMgr().getScale(displayId));
+            mMagnificationConnectionManager.onUserMagnificationScaleChanged(
+                    mUserId, displayId, getMagnificationConnectionManager().getScale(displayId));
 
             final MagnificationConfig config = new MagnificationConfig.Builder()
                     .setMode(MAGNIFICATION_MODE_WINDOW)
-                    .setActivated(getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId))
-                    .setScale(getWindowMagnificationMgr().getScale(displayId))
+                    .setActivated(
+                            getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId))
+                    .setScale(getMagnificationConnectionManager().getScale(displayId))
                     .setCenterX(bounds.exactCenterX())
                     .setCenterY(bounds.exactCenterY()).build();
             mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
@@ -525,7 +530,7 @@
             @NonNull MagnificationConfig config) {
         if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_FULLSCREEN)) {
             // notify sysui the magnification scale changed on fullscreen magnifier
-            mWindowMagnificationMgr.onUserMagnificationScaleChanged(
+            mMagnificationConnectionManager.onUserMagnificationScaleChanged(
                     mUserId, displayId, config.getScale());
 
             mAms.notifyMagnificationChanged(displayId, region, config);
@@ -548,8 +553,8 @@
         synchronized (mLock) {
             final boolean fullScreenActivated = mFullScreenMagnificationController != null
                     && mFullScreenMagnificationController.isActivated(displayId);
-            final boolean windowEnabled = mWindowMagnificationMgr != null
-                    && mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+            final boolean windowEnabled = mMagnificationConnectionManager != null
+                    && mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
             final Integer transitionMode = mTransitionModes.get(displayId);
             if (((changeMode == MAGNIFICATION_MODE_FULLSCREEN && fullScreenActivated)
                     || (changeMode == MAGNIFICATION_MODE_WINDOW && windowEnabled))
@@ -608,10 +613,10 @@
     }
 
     private void disableWindowMagnificationIfNeeded(int displayId) {
-        final WindowMagnificationManager windowMagnificationManager =
-                getWindowMagnificationMgr();
+        final MagnificationConnectionManager magnificationConnectionManager =
+                getMagnificationConnectionManager();
         if (isActivated(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
-            windowMagnificationManager.disableWindowMagnification(displayId, false);
+            magnificationConnectionManager.disableWindowMagnification(displayId, false);
         }
     }
 
@@ -620,7 +625,7 @@
         synchronized (mLock) {
             mIsImeVisibleArray.put(displayId, shown);
         }
-        getWindowMagnificationMgr().onImeWindowVisibilityChanged(displayId, shown);
+        getMagnificationConnectionManager().onImeWindowVisibilityChanged(displayId, shown);
         logMagnificationModeWithImeOnIfNeeded(displayId);
     }
 
@@ -661,7 +666,7 @@
 
     /**
      * Updates the active user ID of {@link FullScreenMagnificationController} and {@link
-     * WindowMagnificationManager}.
+     * MagnificationConnectionManager}.
      *
      * @param userId the currently active user ID
      */
@@ -671,10 +676,10 @@
         }
         mUserId = userId;
         final FullScreenMagnificationController fullMagnificationController;
-        final WindowMagnificationManager windowMagnificationManager;
+        final MagnificationConnectionManager magnificationConnectionManager;
         synchronized (mLock) {
             fullMagnificationController = mFullScreenMagnificationController;
-            windowMagnificationManager = mWindowMagnificationMgr;
+            magnificationConnectionManager = mMagnificationConnectionManager;
             mAccessibilityCallbacksDelegateArray.clear();
             mCurrentMagnificationModeArray.clear();
             mLastMagnificationActivatedModeArray.clear();
@@ -685,8 +690,8 @@
         if (fullMagnificationController != null) {
             fullMagnificationController.resetAllIfNeeded(false);
         }
-        if (windowMagnificationManager != null) {
-            windowMagnificationManager.disableAllWindowMagnifiers();
+        if (magnificationConnectionManager != null) {
+            magnificationConnectionManager.disableAllWindowMagnifiers();
         }
     }
 
@@ -700,8 +705,8 @@
             if (mFullScreenMagnificationController != null) {
                 mFullScreenMagnificationController.onDisplayRemoved(displayId);
             }
-            if (mWindowMagnificationMgr != null) {
-                mWindowMagnificationMgr.onDisplayRemoved(displayId);
+            if (mMagnificationConnectionManager != null) {
+                mMagnificationConnectionManager.onDisplayRemoved(displayId);
             }
             mAccessibilityCallbacksDelegateArray.delete(displayId);
             mCurrentMagnificationModeArray.delete(displayId);
@@ -728,7 +733,7 @@
      * @param enabled Enable the following typing focus feature
      */
     public void setMagnificationFollowTypingEnabled(boolean enabled) {
-        getWindowMagnificationMgr().setMagnificationFollowTypingEnabled(enabled);
+        getMagnificationConnectionManager().setMagnificationFollowTypingEnabled(enabled);
         getFullScreenMagnificationController().setMagnificationFollowTypingEnabled(enabled);
     }
 
@@ -805,29 +810,29 @@
     }
 
     /**
-     * Getter of {@link WindowMagnificationManager}.
+     * Getter of {@link MagnificationConnectionManager}.
      *
-     * @return {@link WindowMagnificationManager}.
+     * @return {@link MagnificationConnectionManager}.
      */
-    public WindowMagnificationManager getWindowMagnificationMgr() {
+    public MagnificationConnectionManager getMagnificationConnectionManager() {
         synchronized (mLock) {
-            if (mWindowMagnificationMgr == null) {
-                mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
+            if (mMagnificationConnectionManager == null) {
+                mMagnificationConnectionManager = new MagnificationConnectionManager(mContext,
                         mLock, this, mAms.getTraceManager(),
                         mScaleProvider);
             }
-            return mWindowMagnificationMgr;
+            return mMagnificationConnectionManager;
         }
     }
 
     private @Nullable PointF getCurrentMagnificationCenterLocked(int displayId, int targetMode) {
         if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
-            if (mWindowMagnificationMgr == null
-                    || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) {
+            if (mMagnificationConnectionManager == null
+                    || !mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId)) {
                 return null;
             }
-            mTempPoint.set(mWindowMagnificationMgr.getCenterX(displayId),
-                    mWindowMagnificationMgr.getCenterY(displayId));
+            mTempPoint.set(mMagnificationConnectionManager.getCenterX(displayId),
+                    mMagnificationConnectionManager.getCenterY(displayId));
         } else {
             if (mFullScreenMagnificationController == null
                     || !mFullScreenMagnificationController.isActivated(displayId)) {
@@ -858,10 +863,10 @@
             }
         } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
             synchronized (mLock) {
-                if (mWindowMagnificationMgr == null) {
+                if (mMagnificationConnectionManager == null) {
                     return false;
                 }
-                isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+                isActivated = mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
             }
         }
         return isActivated;
@@ -980,7 +985,7 @@
                         mCurrentCenter.x, mCurrentCenter.y, mAnimate,
                         MAGNIFICATION_GESTURE_HANDLER_ID);
             } else {
-                getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
+                getMagnificationConnectionManager().enableWindowMagnification(mDisplayId,
                         mCurrentScale, mCurrentCenter.x,
                         mCurrentCenter.y, mAnimate ? STUB_ANIMATION_CALLBACK : null,
                         MAGNIFICATION_GESTURE_HANDLER_ID);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 5cf2a63..ed8f1ab 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -84,13 +84,13 @@
                     .setCenterX(fullScreenMagnificationController.getCenterX(displayId))
                     .setCenterY(fullScreenMagnificationController.getCenterY(displayId));
         } else if (mode == MAGNIFICATION_MODE_WINDOW) {
-            final WindowMagnificationManager windowMagnificationManager =
-                    mController.getWindowMagnificationMgr();
+            final MagnificationConnectionManager magnificationConnectionManager =
+                    mController.getMagnificationConnectionManager();
             builder.setMode(mode)
                     .setActivated(mController.isActivated(displayId, MAGNIFICATION_MODE_WINDOW))
-                    .setScale(windowMagnificationManager.getScale(displayId))
-                    .setCenterX(windowMagnificationManager.getCenterX(displayId))
-                    .setCenterY(windowMagnificationManager.getCenterY(displayId));
+                    .setScale(magnificationConnectionManager.getScale(displayId))
+                    .setCenterX(magnificationConnectionManager.getCenterX(displayId))
+                    .setCenterY(magnificationConnectionManager.getCenterY(displayId));
         } else {
             // For undefined mode, set enabled to false
             builder.setActivated(false);
@@ -135,12 +135,12 @@
             }
         } else if (configMode == MAGNIFICATION_MODE_WINDOW) {
             if (configActivated) {
-                return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
-                        config.getScale(), config.getCenterX(), config.getCenterY(),
+                return mController.getMagnificationConnectionManager().enableWindowMagnification(
+                        displayId, config.getScale(), config.getCenterX(), config.getCenterY(),
                         animate ? STUB_ANIMATION_CALLBACK : null,
                         id);
             } else {
-                return mController.getWindowMagnificationMgr()
+                return mController.getMagnificationConnectionManager()
                         .disableWindowMagnification(displayId, false);
             }
         }
@@ -256,7 +256,7 @@
         if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) {
             getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
         } else if (currentMode == MAGNIFICATION_MODE_WINDOW) {
-            mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+            mController.getMagnificationConnectionManager().getMagnificationSourceBounds(displayId,
                     outRegion);
         }
     }
@@ -297,8 +297,8 @@
         if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
             return mController.getFullScreenMagnificationController().reset(displayId, animate);
         } else if (mode == MAGNIFICATION_MODE_WINDOW) {
-            return mController.getWindowMagnificationMgr().disableWindowMagnification(displayId,
-                    false, animate ? STUB_ANIMATION_CALLBACK : null);
+            return mController.getMagnificationConnectionManager().disableWindowMagnification(
+                    displayId, false, animate ? STUB_ANIMATION_CALLBACK : null);
         }
         return false;
     }
@@ -325,19 +325,20 @@
      */
     public void resetAllIfNeeded(int connectionId) {
         mController.getFullScreenMagnificationController().resetAllIfNeeded(connectionId);
-        mController.getWindowMagnificationMgr().resetAllIfNeeded(connectionId);
+        mController.getMagnificationConnectionManager().resetAllIfNeeded(connectionId);
     }
 
     /**
      * {@link FullScreenMagnificationController#isActivated(int)}
-     * {@link WindowMagnificationManager#isWindowMagnifierEnabled(int)}
+     * {@link MagnificationConnectionManager#isWindowMagnifierEnabled(int)}
      */
     public boolean isMagnifying(int displayId) {
         int mode = getControllingMode(displayId);
         if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
             return mController.getFullScreenMagnificationController().isActivated(displayId);
         } else if (mode == MAGNIFICATION_MODE_WINDOW) {
-            return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
+            return mController.getMagnificationConnectionManager().isWindowMagnifierEnabled(
+                    displayId);
         }
         return false;
     }
@@ -416,22 +417,23 @@
         pw.append("    SupportWindowMagnification="
                 + mController.supportWindowMagnification()).println();
         pw.append("    WindowMagnificationConnectionState="
-                + mController.getWindowMagnificationMgr().getConnectionState()).println();
+                + mController.getMagnificationConnectionManager().getConnectionState()).println();
     }
 
     private int getIdOfLastServiceToMagnify(int mode, int displayId) {
         return (mode == MAGNIFICATION_MODE_FULLSCREEN)
                 ? mController.getFullScreenMagnificationController()
                 .getIdOfLastServiceToMagnify(displayId)
-                : mController.getWindowMagnificationMgr().getIdOfLastServiceToMagnify(
+                : mController.getMagnificationConnectionManager().getIdOfLastServiceToMagnify(
                         displayId);
     }
 
     private void dumpTrackingTypingFocusEnabledState(final PrintWriter pw, int displayId,
             int mode) {
         if (mode == MAGNIFICATION_MODE_WINDOW) {
-            pw.append("    TrackingTypingFocusEnabled="  + mController
-                            .getWindowMagnificationMgr().isTrackingTypingFocusEnabled(displayId))
+            pw.append("    TrackingTypingFocusEnabled="
+                            + mController.getMagnificationConnectionManager()
+                                .isTrackingTypingFocusEnabled(displayId))
                     .println();
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
index 9455628..6b48d2b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
@@ -45,6 +45,7 @@
     private static final String TAG = "PanningScalingHandler";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    // TODO(b/312372035): Revisit the scope of usage of the interface
     interface MagnificationDelegate {
         boolean processScroll(int displayId, float distanceX, float distanceY);
         void setScale(int displayId, float scale);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 36e75118..73c267a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -79,7 +79,7 @@
     private static final float MIN_SCALE = 1.0f;
     private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;
 
-    private final WindowMagnificationManager mWindowMagnificationMgr;
+    private final MagnificationConnectionManager mMagnificationConnectionManager;
     @VisibleForTesting
     final DelegatingState mDelegatingState;
     @VisibleForTesting
@@ -101,7 +101,7 @@
     private long mTripleTapAndHoldStartedTime = 0;
 
     public WindowMagnificationGestureHandler(@UiContext Context context,
-            WindowMagnificationManager windowMagnificationMgr,
+            MagnificationConnectionManager magnificationConnectionManager,
             AccessibilityTraceManager trace,
             Callback callback,
             boolean detectSingleFingerTripleTap,
@@ -115,7 +115,7 @@
                     "WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
         }
         mContext = context;
-        mWindowMagnificationMgr = windowMagnificationMgr;
+        mMagnificationConnectionManager = magnificationConnectionManager;
         mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context,
                 (event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent,
                         policyFlags));
@@ -128,18 +128,18 @@
                             @Override
                             public boolean processScroll(int displayId, float distanceX,
                                     float distanceY) {
-                                return mWindowMagnificationMgr.processScroll(displayId, distanceX,
-                                        distanceY);
+                                return mMagnificationConnectionManager.processScroll(
+                                        displayId, distanceX, distanceY);
                             }
 
                             @Override
                             public void setScale(int displayId, float scale) {
-                                mWindowMagnificationMgr.setScale(displayId, scale);
+                                mMagnificationConnectionManager.setScale(displayId, scale);
                             }
 
                             @Override
                             public float getScale(int displayId) {
-                                return mWindowMagnificationMgr.getScale(displayId);
+                                return mMagnificationConnectionManager.getScale(displayId);
                             }
                         }));
 
@@ -167,7 +167,7 @@
             Slog.i(mLogTag, "onDestroy(); delayed = "
                     + mDetectingState.toString());
         }
-        mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+        mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
         resetToDetectState();
     }
 
@@ -176,7 +176,7 @@
         final Point screenSize = mTempPoint;
         getScreenSize(mTempPoint);
         toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f,
-                WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+                MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
     }
 
     private  void getScreenSize(Point outSize) {
@@ -190,28 +190,29 @@
     }
 
     private void enableWindowMagnifier(float centerX, float centerY,
-            @WindowMagnificationManager.WindowPosition int windowPosition) {
+            @MagnificationConnectionManager.WindowPosition int windowPosition) {
         if (DEBUG_ALL) {
             Slog.i(mLogTag, "enableWindowMagnifier :"
                     + centerX + ", " + centerY + ", " + windowPosition);
         }
 
         final float scale = MathUtils.constrain(
-                mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE);
-        mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY,
-                windowPosition);
+                mMagnificationConnectionManager.getPersistedScale(mDisplayId),
+                MIN_SCALE, MAX_SCALE);
+        mMagnificationConnectionManager.enableWindowMagnification(
+                mDisplayId, scale, centerX, centerY, windowPosition);
     }
 
     private void disableWindowMagnifier() {
         if (DEBUG_ALL) {
             Slog.i(mLogTag, "disableWindowMagnifier()");
         }
-        mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
+        mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, false);
     }
 
     private void toggleMagnification(float centerX, float centerY,
-            @WindowMagnificationManager.WindowPosition int windowPosition) {
-        if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
+            @MagnificationConnectionManager.WindowPosition int windowPosition) {
+        if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)) {
             disableWindowMagnifier();
         } else {
             enableWindowMagnifier(centerX, centerY, windowPosition);
@@ -223,7 +224,7 @@
             Slog.i(mLogTag, "onTripleTap()");
         }
         toggleMagnification(up.getX(), up.getY(),
-                WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+                MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
     }
 
     @VisibleForTesting
@@ -232,9 +233,9 @@
             Slog.i(mLogTag, "onTripleTapAndHold()");
         }
         mViewportDraggingState.mEnabledBeforeDrag =
-                mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId);
+                mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId);
         enableWindowMagnifier(up.getX(), up.getY(),
-                WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
+                MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
         mTripleTapAndHoldStartedTime = SystemClock.uptimeMillis();
         transitionTo(mViewportDraggingState);
     }
@@ -242,7 +243,7 @@
     @VisibleForTesting
     void releaseTripleTapAndHold() {
         if (!mViewportDraggingState.mEnabledBeforeDrag) {
-            mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+            mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
         }
         transitionTo(mDetectingState);
         if (mTripleTapAndHoldStartedTime != 0) {
@@ -331,7 +332,7 @@
         @Override
         public void onExit() {
             mPanningScalingHandler.setEnabled(false);
-            mWindowMagnificationMgr.persistScale(mDisplayId);
+            mMagnificationConnectionManager.persistScale(mDisplayId);
             clear();
         }
 
@@ -404,7 +405,7 @@
                     if (!Float.isNaN(mLastX) && !Float.isNaN(mLastY)) {
                         float offsetX = event.getX() - mLastX;
                         float offsetY = event.getY() - mLastY;
-                        mWindowMagnificationMgr.moveWindowMagnification(mDisplayId, offsetX,
+                        mMagnificationConnectionManager.moveWindowMagnification(mDisplayId, offsetX,
                                 offsetY);
                     }
                     mLastX = event.getX();
@@ -522,7 +523,7 @@
 
         @Override
         public boolean shouldStopDetection(MotionEvent motionEvent) {
-            return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
+            return !mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
                     && !mDetectSingleFingerTripleTap
                     && !(mDetectTwoFingerTripleTap
                     && Flags.enableMagnificationMultipleFingerMultipleTapGesture());
@@ -540,7 +541,8 @@
                         "onGestureDetected : delayedEventQueue = " + delayedEventQueue);
             }
             if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
-                    && mWindowMagnificationMgr.pointersInWindow(mDisplayId, motionEvent) > 0) {
+                    && mMagnificationConnectionManager
+                        .pointersInWindow(mDisplayId, motionEvent) > 0) {
                 transitionTo(mObservePanningScalingState);
             } else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
                 onTripleTap(motionEvent);
@@ -584,7 +586,7 @@
                 + ", mMagnifiedInteractionState=" + mObservePanningScalingState
                 + ", mCurrentState=" + State.nameOf(mCurrentState)
                 + ", mPreviousState=" + State.nameOf(mPreviousState)
-                + ", mWindowMagnificationMgr=" + mWindowMagnificationMgr
+                + ", mMagnificationConnectionManager=" + mMagnificationConnectionManager
                 + ", mDisplayId=" + mDisplayId
                 + '}';
     }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index d194bb2..9b78ed4 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -460,11 +460,11 @@
 
             synchronized (mVirtualDeviceManagerLock) {
                 if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
-                    final long callindId = Binder.clearCallingIdentity();
+                    final long callingId = Binder.clearCallingIdentity();
                     try {
                         registerCdmAssociationListener();
                     } finally {
-                        Binder.restoreCallingIdentity(callindId);
+                        Binder.restoreCallingIdentity(callingId);
                     }
                 }
                 mVirtualDevices.put(deviceId, virtualDevice);
@@ -498,13 +498,16 @@
             synchronized (mVirtualDeviceManagerLock) {
                 virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getDeviceId());
                 if (virtualDeviceImpl == null) {
-                    throw new SecurityException("Invalid VirtualDevice");
+                    throw new SecurityException(
+                            "Invalid VirtualDevice (deviceId = " + virtualDevice.getDeviceId()
+                                    + ")");
                 }
             }
             if (virtualDeviceImpl.getOwnerUid() != callingUid) {
                 throw new SecurityException(
                         "uid " + callingUid
-                                + " is not the owner of the supplied VirtualDevice");
+                                + " is not the owner of the supplied VirtualDevice (deviceId = "
+                                + virtualDevice.getDeviceId() + ")");
             }
 
             return virtualDeviceImpl.createVirtualDisplay(
@@ -851,6 +854,11 @@
         }
 
         @Override
+        public int getDeviceIdForDisplayId(int displayId) {
+            return mImpl.getDeviceIdForDisplayId(displayId);
+        }
+
+        @Override
         public @Nullable String getPersistentIdForDevice(int deviceId) {
             if (deviceId == Context.DEVICE_ID_DEFAULT) {
                 return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 5665ad5..d089b05 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -27,14 +27,14 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.util.ArraySet;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
-import java.util.Set;
+import java.util.Map;
 
 /**
  * Manages the registration and removal of virtual camera from the server side.
@@ -47,10 +47,13 @@
     private static final String VIRTUAL_CAMERA_SERVICE_NAME = "virtual_camera";
     private static final String TAG = "VirtualCameraController";
 
+    private final Object mServiceLock = new Object();
+
+    @GuardedBy("mServiceLock")
     @Nullable private IVirtualCameraService mVirtualCameraService;
 
     @GuardedBy("mCameras")
-    private final Set<VirtualCameraConfig> mCameras = new ArraySet<>();
+    private final Map<IBinder, CameraDescriptor> mCameras = new ArrayMap<>();
 
     public VirtualCameraController() {
         connectVirtualCameraService();
@@ -71,8 +74,12 @@
 
         try {
             if (registerCameraWithService(cameraConfig)) {
+                CameraDescriptor cameraDescriptor =
+                        new CameraDescriptor(cameraConfig);
+                IBinder binder = cameraConfig.getCallback().asBinder();
+                binder.linkToDeath(cameraDescriptor, 0 /* flags */);
                 synchronized (mCameras) {
-                    mCameras.add(cameraConfig);
+                    mCameras.put(binder, cameraDescriptor);
                 }
             } else {
                 // TODO(b/310857519): Revisit this to find a better way of indicating failure.
@@ -89,17 +96,22 @@
      * @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
      */
     public void unregisterCamera(@NonNull VirtualCameraConfig cameraConfig) {
-        try {
-            if (mVirtualCameraService == null) {
-                Slog.w(TAG, "Virtual camera service is not connected.");
+        synchronized (mCameras) {
+            IBinder binder = cameraConfig.getCallback().asBinder();
+            if (!mCameras.containsKey(binder)) {
+                Slog.w(TAG, "Virtual camera was not registered.");
             } else {
-                mVirtualCameraService.unregisterCamera(cameraConfig.getCallback().asBinder());
+                connectVirtualCameraServiceIfNeeded();
+
+                try {
+                    synchronized (mServiceLock) {
+                        mVirtualCameraService.unregisterCamera(binder);
+                    }
+                    mCameras.remove(binder);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
             }
-            synchronized (mCameras) {
-                mCameras.remove(cameraConfig);
-            }
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
         }
     }
 
@@ -108,7 +120,9 @@
         connectVirtualCameraServiceIfNeeded();
 
         try {
-            return mVirtualCameraService.getCameraId(cameraConfig.getCallback().asBinder());
+            synchronized (mServiceLock) {
+                return mVirtualCameraService.getCameraId(cameraConfig.getCallback().asBinder());
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -117,7 +131,9 @@
     @Override
     public void binderDied() {
         Slog.d(TAG, "Virtual camera service died.");
-        mVirtualCameraService = null;
+        synchronized (mServiceLock) {
+            mVirtualCameraService = null;
+        }
         synchronized (mCameras) {
             mCameras.clear();
         }
@@ -126,44 +142,49 @@
     /** Release resources associated with this controller. */
     public void close() {
         synchronized (mCameras) {
-            if (mVirtualCameraService == null) {
-                Slog.w(TAG, "Virtual camera service is not connected.");
-            } else {
-                for (VirtualCameraConfig config : mCameras) {
-                    try {
-                        mVirtualCameraService.unregisterCamera(config.getCallback().asBinder());
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "close(): Camera failed to be removed on camera "
-                                + "service.", e);
+            if (!mCameras.isEmpty()) {
+                connectVirtualCameraServiceIfNeeded();
+
+                synchronized (mServiceLock) {
+                    for (IBinder binder : mCameras.keySet()) {
+                        try {
+                            mVirtualCameraService.unregisterCamera(binder);
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "close(): Camera failed to be removed on camera "
+                                    + "service.", e);
+                        }
                     }
                 }
+                mCameras.clear();
             }
-            mCameras.clear();
         }
-        mVirtualCameraService = null;
+        synchronized (mServiceLock) {
+            mVirtualCameraService = null;
+        }
     }
 
     /** Dumps information about this {@link VirtualCameraController} for debugging purposes. */
     public void dump(PrintWriter fout, String indent) {
         fout.println(indent + "VirtualCameraController:");
         indent += indent;
-        fout.printf("%sService:%s\n", indent, mVirtualCameraService);
         synchronized (mCameras) {
             fout.printf("%sRegistered cameras:%d%n\n", indent, mCameras.size());
-            for (VirtualCameraConfig config : mCameras) {
-                fout.printf("%s token: %s\n", indent, config);
+            for (CameraDescriptor descriptor : mCameras.values()) {
+                fout.printf("%s token: %s\n", indent, descriptor.mConfig);
             }
         }
     }
 
     private void connectVirtualCameraServiceIfNeeded() {
-        // Try to connect to service if not connected already.
-        if (mVirtualCameraService == null) {
-            connectVirtualCameraService();
-        }
-        // Throw exception if we are unable to connect to service.
-        if (mVirtualCameraService == null) {
-            throw new IllegalStateException("Virtual camera service is not connected.");
+        synchronized (mServiceLock) {
+            // Try to connect to service if not connected already.
+            if (mVirtualCameraService == null) {
+                connectVirtualCameraService();
+            }
+            // Throw exception if we are unable to connect to service.
+            if (mVirtualCameraService == null) {
+                throw new IllegalStateException("Virtual camera service is not connected.");
+            }
         }
     }
 
@@ -188,7 +209,24 @@
 
     private boolean registerCameraWithService(VirtualCameraConfig config) throws RemoteException {
         VirtualCameraConfiguration serviceConfiguration = getServiceCameraConfiguration(config);
-        return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
-                serviceConfiguration);
+        synchronized (mServiceLock) {
+            return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
+                    serviceConfiguration);
+        }
+    }
+
+    private final class CameraDescriptor implements IBinder.DeathRecipient {
+
+        private final VirtualCameraConfig mConfig;
+
+        CameraDescriptor(VirtualCameraConfig config) {
+            mConfig = config;
+        }
+
+        @Override
+        public void binderDied() {
+            Slog.d(TAG, "Virtual camera binder died");
+            unregisterCamera(mConfig);
+        }
     }
 }
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index fc56511..23c008e 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -27,6 +27,7 @@
 import android.content.LocusId;
 import android.content.res.Configuration;
 import android.os.IBinder;
+import android.os.PersistableBundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -134,6 +135,18 @@
             @Nullable LocusId locusId, @NonNull IBinder appToken);
 
     /**
+     * Report a user interaction event to UsageStatsManager
+     *
+     * @param pkgName The package for which this user interaction event occurred.
+     * @param userId The user id to which component belongs to.
+     * @param extras The extra details about this user interaction event.
+     * {@link UsageEvents.Event#USER_INTERACTION}
+     * {@link UsageStatsManager#reportUserInteraction(String, int, PersistableBundle)}
+     */
+    public abstract void reportUserInteractionEvent(@NonNull String pkgName, @UserIdInt int userId,
+            @NonNull PersistableBundle extras);
+
+    /**
      * Prepares the UsageStatsService for shutdown.
      */
     public abstract void prepareShutdown();
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 7fe0682..c258370 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
-# BootReceiver
-per-file BootReceiver.java = gaillard@google.com
+# BootReceiver / Watchdog
+per-file BootReceiver.java,Watchdog.java = gaillard@google.com
 
 # Connectivity / Networking
 per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 23a30f9..c5c2b0b 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -1044,7 +1044,6 @@
         if (DEBUG) {
             Slog.d(TAG, "pin file: " + fileToPin + " use-pinlist: " + attemptPinIntrospection);
         }
-        Trace.beginSection("pinFile:" + fileToPin);
         ZipFile fileAsZip = null;
         InputStream pinRangeStream = null;
         try {
@@ -1067,7 +1066,6 @@
         } finally {
             safeClose(pinRangeStream);
             safeClose(fileAsZip);  // Also closes any streams we've opened
-            Trace.endSection();
         }
     }
 
diff --git a/services/core/java/com/android/server/SecurityStateManagerService.java b/services/core/java/com/android/server/SecurityStateManagerService.java
new file mode 100644
index 0000000..98039be
--- /dev/null
+++ b/services/core/java/com/android/server/SecurityStateManagerService.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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;
+
+import static android.os.SecurityStateManager.KEY_KERNEL_VERSION;
+import static android.os.SecurityStateManager.KEY_SYSTEM_SPL;
+import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ISecurityStateManager;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewUpdateService;
+
+import com.android.internal.R;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SecurityStateManagerService extends ISecurityStateManager.Stub {
+
+    private static final String TAG = "SecurityStateManagerService";
+
+    static final String VENDOR_SECURITY_PATCH_PROPERTY_KEY = "ro.vendor.build"
+            + ".security_patch";
+    static final Pattern KERNEL_RELEASE_PATTERN = Pattern.compile("(\\d+\\.\\d+\\.\\d+)("
+            + ".*)");
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+
+    public SecurityStateManagerService(Context context) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+    }
+
+    @Override
+    public Bundle getGlobalSecurityState() {
+        Bundle globalSecurityState = new Bundle();
+        globalSecurityState.putString(KEY_SYSTEM_SPL, Build.VERSION.SECURITY_PATCH);
+        globalSecurityState.putString(KEY_VENDOR_SPL,
+                SystemProperties.get(VENDOR_SECURITY_PATCH_PROPERTY_KEY, ""));
+        String moduleMetadataProviderPackageName =
+                mContext.getString(R.string.config_defaultModuleMetadataProvider);
+        if (!moduleMetadataProviderPackageName.isEmpty()) {
+            globalSecurityState.putString(moduleMetadataProviderPackageName,
+                    getSpl(moduleMetadataProviderPackageName));
+        }
+        globalSecurityState.putString(KEY_KERNEL_VERSION, getKernelVersion());
+        addWebViewPackages(globalSecurityState);
+        addSecurityStatePackages(globalSecurityState);
+        return globalSecurityState;
+    }
+
+    private String getSpl(String packageName) {
+        if (!TextUtils.isEmpty(packageName)) {
+            try {
+                return mPackageManager.getPackageInfo(packageName, 0 /* flags */).versionName;
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.e(TAG, TextUtils.formatSimple("Failed to get SPL for package %s.",
+                        packageName), e);
+            }
+        }
+        return "";
+    }
+
+    private String getKernelVersion() {
+        Matcher matcher = KERNEL_RELEASE_PATTERN.matcher(VintfRuntimeInfo.getKernelVersion());
+        if (matcher.matches()) {
+            return matcher.group(1);
+        }
+        return "";
+    }
+
+    private void addWebViewPackages(Bundle bundle) {
+        for (WebViewProviderInfo info : WebViewUpdateService.getAllWebViewPackages()) {
+            String packageName = info.packageName;
+            bundle.putString(packageName, getSpl(packageName));
+        }
+    }
+
+    private void addSecurityStatePackages(Bundle bundle) {
+        String[] packageNames;
+        packageNames = mContext.getResources().getStringArray(R.array.config_securityStatePackages);
+        for (String packageName : packageNames) {
+            bundle.putString(packageName, getSpl(packageName));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 003046a..382ee6e 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -69,6 +69,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.time.Clock;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -98,11 +99,17 @@
     //         applications may not work with a debug build. CTS will fail.
     private static final long DEFAULT_TIMEOUT = DB ? 10 * 1000 : 60 * 1000;
 
+    // This ratio is used to compute the pre-watchdog timeout (2 means that the pre-watchdog timeout
+    // will be half the full timeout).
+    //
+    // The pre-watchdog event is similar to a full watchdog except it does not crash system server.
+    private static final int PRE_WATCHDOG_TIMEOUT_RATIO = 3;
+
     // These are temporally ordered: larger values as lateness increases
-    private static final int COMPLETED = 0;
-    private static final int WAITING = 1;
-    private static final int WAITED_HALF = 2;
-    private static final int OVERDUE = 3;
+    static final int COMPLETED = 0;
+    static final int WAITING = 1;
+    static final int WAITED_UNTIL_PRE_WATCHDOG = 2;
+    static final int OVERDUE = 3;
 
     // Track watchdog timeout history and break the crash loop if there is.
     private static final String TIMEOUT_HISTORY_FILE = "/data/system/watchdog-timeout-history.txt";
@@ -237,10 +244,8 @@
         }
     }
 
-    /**
-     * Used for checking status of handle threads and scheduling monitor callbacks.
-     */
-    public final class HandlerChecker implements Runnable {
+    /** Used for checking status of handle threads and scheduling monitor callbacks. */
+    public static class HandlerChecker implements Runnable {
         private final Handler mHandler;
         private final String mName;
         private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
@@ -251,11 +256,19 @@
         private long mStartTimeMillis;
         private int mPauseCount;
         private long mPauseEndTimeMillis;
+        private Clock mClock;
+        private Object mLock;
 
-        HandlerChecker(Handler handler, String name) {
+        HandlerChecker(Handler handler, String name, Object lock, Clock clock) {
             mHandler = handler;
             mName = name;
+            mLock = lock;
             mCompleted = true;
+            mClock = clock;
+        }
+
+        HandlerChecker(Handler handler, String name, Object lock) {
+            this(handler, name, lock, SystemClock.uptimeClock());
         }
 
         void addMonitorLocked(Monitor monitor) {
@@ -278,11 +291,9 @@
                 mMonitorQueue.clear();
             }
 
-            long nowMillis = SystemClock.uptimeMillis();
-            boolean isPaused = mPauseCount > 0
-                    || (mPauseEndTimeMillis > 0 && mPauseEndTimeMillis < nowMillis);
-            if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
-                    || isPaused) {
+            long nowMillis = mClock.millis();
+            boolean isPaused = mPauseCount > 0 || mPauseEndTimeMillis > nowMillis;
+            if ((mMonitors.size() == 0 && isHandlerPolling()) || isPaused) {
                 // Don't schedule until after resume OR
                 // If the target looper has recently been polling, then
                 // there is no reason to enqueue our checker on it since that
@@ -305,15 +316,19 @@
             mHandler.postAtFrontOfQueue(this);
         }
 
+        boolean isHandlerPolling() {
+            return mHandler.getLooper().getQueue().isPolling();
+        }
+
         public int getCompletionStateLocked() {
             if (mCompleted) {
                 return COMPLETED;
             } else {
-                long latency = SystemClock.uptimeMillis() - mStartTimeMillis;
-                if (latency < mWaitMaxMillis / 2) {
+                long latency = mClock.millis() - mStartTimeMillis;
+                if (latency < mWaitMaxMillis / PRE_WATCHDOG_TIMEOUT_RATIO) {
                     return WAITING;
                 } else if (latency < mWaitMaxMillis) {
-                    return WAITED_HALF;
+                    return WAITED_UNTIL_PRE_WATCHDOG;
                 }
             }
             return OVERDUE;
@@ -334,7 +349,7 @@
             } else {
                 prefix =  "Blocked in monitor " + mCurrentMonitor.getClass().getName();
             }
-            long latencySeconds = (SystemClock.uptimeMillis() - mStartTimeMillis) / 1000;
+            long latencySeconds = (mClock.millis() - mStartTimeMillis) / 1000;
             return prefix + " on " + mName + " (" + getThread().getName() + ")"
                 + " for " + latencySeconds + "s";
         }
@@ -366,10 +381,11 @@
          * the given time.
          */
         public void pauseForLocked(int pauseMillis, String reason) {
-            mPauseEndTimeMillis = SystemClock.uptimeMillis() + pauseMillis;
+            mPauseEndTimeMillis = mClock.millis() + pauseMillis;
             // Mark as completed, because there's a chance we called this after the watchog
-            // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
-            // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+            // thread loop called Object#wait after 'WAITED_UNTIL_PRE_WATCHDOG'. In that case we
+            // want to ensure the next call to #getCompletionStateLocked for this checker returns
+            // 'COMPLETED'
             mCompleted = true;
             Slog.i(TAG, "Pausing of HandlerChecker: " + mName + " for reason: "
                     + reason + ". Pause end time: " + mPauseEndTimeMillis);
@@ -379,8 +395,9 @@
         public void pauseLocked(String reason) {
             mPauseCount++;
             // Mark as completed, because there's a chance we called this after the watchog
-            // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
-            // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+            // thread loop called Object#wait after 'WAITED_UNTIL_PRE_WATCHDOG'. In that case we
+            // want to ensure the next call to #getCompletionStateLocked for this checker returns
+            // 'COMPLETED'
             mCompleted = true;
             Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
                     + reason + ". Pause count: " + mPauseCount);
@@ -396,6 +413,11 @@
                 Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);
             }
         }
+
+        @Override
+        public String toString() {
+            return "CheckerHandler for " + mName;
+        }
     }
 
     final class RebootRequestReceiver extends BroadcastReceiver {
@@ -445,31 +467,40 @@
         ServiceThread t = new ServiceThread("watchdog.monitor",
                 android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
         t.start();
-        mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread");
+        mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread", mLock);
         mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker));
 
-        mHandlerCheckers.add(withDefaultTimeout(
-                new HandlerChecker(FgThread.getHandler(), "foreground thread")));
+        mHandlerCheckers.add(
+                withDefaultTimeout(
+                        new HandlerChecker(FgThread.getHandler(), "foreground thread", mLock)));
         // Add checker for main thread.  We only do a quick check since there
         // can be UI running on the thread.
-        mHandlerCheckers.add(withDefaultTimeout(
-                new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread")));
+        mHandlerCheckers.add(
+                withDefaultTimeout(
+                        new HandlerChecker(
+                                new Handler(Looper.getMainLooper()), "main thread", mLock)));
         // Add checker for shared UI thread.
-        mHandlerCheckers.add(withDefaultTimeout(
-                new HandlerChecker(UiThread.getHandler(), "ui thread")));
+        mHandlerCheckers.add(
+                withDefaultTimeout(new HandlerChecker(UiThread.getHandler(), "ui thread", mLock)));
         // And also check IO thread.
-        mHandlerCheckers.add(withDefaultTimeout(
-                new HandlerChecker(IoThread.getHandler(), "i/o thread")));
+        mHandlerCheckers.add(
+                withDefaultTimeout(new HandlerChecker(IoThread.getHandler(), "i/o thread", mLock)));
         // And the display thread.
-        mHandlerCheckers.add(withDefaultTimeout(
-                new HandlerChecker(DisplayThread.getHandler(), "display thread")));
+        mHandlerCheckers.add(
+                withDefaultTimeout(
+                        new HandlerChecker(DisplayThread.getHandler(), "display thread", mLock)));
         // And the animation thread.
-        mHandlerCheckers.add(withDefaultTimeout(
-                 new HandlerChecker(AnimationThread.getHandler(), "animation thread")));
+        mHandlerCheckers.add(
+                withDefaultTimeout(
+                        new HandlerChecker(
+                                AnimationThread.getHandler(), "animation thread", mLock)));
         // And the surface animation thread.
-        mHandlerCheckers.add(withDefaultTimeout(
-                new HandlerChecker(SurfaceAnimationThread.getHandler(),
-                    "surface animation thread")));
+        mHandlerCheckers.add(
+                withDefaultTimeout(
+                        new HandlerChecker(
+                                SurfaceAnimationThread.getHandler(),
+                                "surface animation thread",
+                                mLock)));
         // Initialize monitor for Binder threads.
         addMonitor(new BinderThreadMonitor());
 
@@ -609,7 +640,7 @@
     public void addThread(Handler thread) {
         synchronized (mLock) {
             final String name = thread.getLooper().getThread().getName();
-            mHandlerCheckers.add(withDefaultTimeout(new HandlerChecker(thread, name)));
+            mHandlerCheckers.add(withDefaultTimeout(new HandlerChecker(thread, name, mLock)));
         }
     }
 
@@ -617,7 +648,7 @@
         synchronized (mLock) {
             final String name = thread.getLooper().getThread().getName();
             mHandlerCheckers.add(
-                    withCustomTimeout(new HandlerChecker(thread, name), timeoutMillis));
+                    withCustomTimeout(new HandlerChecker(thread, name, mLock), timeoutMillis));
         }
     }
 
@@ -797,11 +828,11 @@
             String subject = "";
             boolean allowRestart = true;
             int debuggerWasConnected = 0;
-            boolean doWaitedHalfDump = false;
+            boolean doWaitedPreDump = false;
             // The value of mWatchdogTimeoutMillis might change while we are executing the loop.
             // We store the current value to use a consistent value for all handlers.
             final long watchdogTimeoutMillis = mWatchdogTimeoutMillis;
-            final long checkIntervalMillis = watchdogTimeoutMillis / 2;
+            final long checkIntervalMillis = watchdogTimeoutMillis / PRE_WATCHDOG_TIMEOUT_RATIO;
             final ArrayList<Integer> pids;
             synchronized (mLock) {
                 long timeout = checkIntervalMillis;
@@ -848,15 +879,16 @@
                 } else if (waitState == WAITING) {
                     // still waiting but within their configured intervals; back off and recheck
                     continue;
-                } else if (waitState == WAITED_HALF) {
+                } else if (waitState == WAITED_UNTIL_PRE_WATCHDOG) {
                     if (!waitedHalf) {
-                        Slog.i(TAG, "WAITED_HALF");
+                        Slog.i(TAG, "WAITED_UNTIL_PRE_WATCHDOG");
                         waitedHalf = true;
-                        // We've waited half, but we'd need to do the stack trace dump w/o the lock.
-                        blockedCheckers = getCheckersWithStateLocked(WAITED_HALF);
+                        // We've waited until the pre-watchdog, but we'd need to do the stack trace
+                        // dump w/o the lock.
+                        blockedCheckers = getCheckersWithStateLocked(WAITED_UNTIL_PRE_WATCHDOG);
                         subject = describeCheckersLocked(blockedCheckers);
                         pids = new ArrayList<>(mInterestingJavaPids);
-                        doWaitedHalfDump = true;
+                        doWaitedPreDump = true;
                     } else {
                         continue;
                     }
@@ -874,12 +906,12 @@
             // First collect stack traces from all threads of the system process.
             //
             // Then, if we reached the full timeout, kill this process so that the system will
-            // restart. If we reached half of the timeout, just log some information and continue.
-            logWatchog(doWaitedHalfDump, subject, pids);
+            // restart. If we reached pre-watchdog timeout, just log some information and continue.
+            logWatchog(doWaitedPreDump, subject, pids);
 
-            if (doWaitedHalfDump) {
-                // We have waited for only half of the timeout, we continue to wait for the duration
-                // of the full timeout before killing the process.
+            if (doWaitedPreDump) {
+                // We have waited for only pre-watchdog timeout, we continue to wait for the
+                // duration of the full timeout before killing the process.
                 continue;
             }
 
@@ -928,8 +960,8 @@
         }
     }
 
-    private void logWatchog(boolean halfWatchdog, String subject, ArrayList<Integer> pids) {
-        // Get critical event log before logging the half watchdog so that it doesn't
+    private void logWatchog(boolean preWatchdog, String subject, ArrayList<Integer> pids) {
+        // Get critical event log before logging the pre-watchdog so that it doesn't
         // occur in the log.
         String criticalEvents =
                 CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
@@ -941,7 +973,7 @@
         }
 
         final String dropboxTag;
-        if (halfWatchdog) {
+        if (preWatchdog) {
             dropboxTag = "pre_watchdog";
             CriticalEventLog.getInstance().logHalfWatchdog(subject);
             FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_PRE_WATCHDOG_OCCURRED);
@@ -971,7 +1003,7 @@
         report.append(processCpuTracker.printCurrentState(anrTime, 10));
         report.append(tracesFileException.getBuffer());
 
-        if (!halfWatchdog) {
+        if (!preWatchdog) {
             // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the
             // kernel log
             doSysRq('w');
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 54c8ed3..b87d02d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -79,7 +79,6 @@
 import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
 import static android.os.PowerExemptionManager.REASON_BOOT_COMPLETED;
 import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
-import static android.os.PowerExemptionManager.REASON_DENIED;
 import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
 import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
 import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP;
@@ -5051,7 +5050,7 @@
                     REASON_LOCKED_BOOT_COMPLETED);
         }
         // Send BOOT_COMPLETED if the user is unlocked
-        if (StorageManager.isUserKeyUnlocked(app.userId)) {
+        if (StorageManager.isCeStorageUnlocked(app.userId)) {
             sendBootBroadcastToAppLocked(app, new Intent(Intent.ACTION_BOOT_COMPLETED),
                     REASON_BOOT_COMPLETED);
         }
@@ -14150,7 +14149,8 @@
                                 || action.startsWith("android.intent.action.PACKAGE_")
                                 || action.startsWith("android.intent.action.UID_")
                                 || action.startsWith("android.intent.action.EXTERNAL_")
-                                || action.startsWith("android.bluetooth.")) {
+                                || action.startsWith("android.bluetooth.")
+                                || action.equals(Intent.ACTION_SHUTDOWN)) {
                             if (DEBUG_BROADCAST) {
                                 Slog.wtf(TAG,
                                         "System internals registering for " + filter.toLongString()
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index e656030..f2d9759 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -480,6 +480,7 @@
     }
 
     public void systemServicesReady() {
+        mStats.setPowerStatsCollectorEnabled(Flags.streamlinedBatteryStats());
         mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(Flags.streamlinedBatteryStats());
         mWorker.systemServicesReady();
         mStats.systemServicesReady(mContext);
@@ -2648,7 +2649,9 @@
         pw.println("     --proto: output as a binary protobuffer");
         pw.println("     --model power-profile: use the power profile model"
                 + " even if measured energy is available");
-        pw.println("  --sample: collect and dump a sample of stats for debugging purpose");
+        if (Flags.streamlinedBatteryStats()) {
+            pw.println("  --sample: collect and dump a sample of stats for debugging purpose");
+        }
         pw.println("  <package.name>: optional name of package to filter output by.");
         pw.println("  -h: print this help text.");
         pw.println("Battery stats (batterystats) commands:");
@@ -2938,10 +2941,10 @@
                     mCpuWakeupStats.dump(new IndentingPrintWriter(pw, "  "),
                             SystemClock.elapsedRealtime());
                     return;
-                } else if ("--sample".equals(arg)) {
+                } else if (Flags.streamlinedBatteryStats() && "--sample".equals(arg)) {
                     dumpStatsSample(pw);
                     return;
-                } else if ("--aggregated".equals(arg)) {
+                } else if (Flags.streamlinedBatteryStats() && "--aggregated".equals(arg)) {
                     dumpAggregatedStats(pw);
                     return;
                 } else if ("--store".equals(arg)) {
@@ -3013,7 +3016,7 @@
                                         mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
                                         null, mStats.mHandler, null, null,
                                         mUserManagerUserInfoProvider, mPowerProfile,
-                                        mCpuScalingPolicies, null);
+                                        mCpuScalingPolicies, new PowerStatsUidResolver());
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
                                 checkinStats.dumpProtoLocked(mContext, fd, apps, flags,
@@ -3055,7 +3058,7 @@
                                         mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
                                         null, mStats.mHandler, null, null,
                                         mUserManagerUserInfoProvider, mPowerProfile,
-                                        mCpuScalingPolicies, null);
+                                        mCpuScalingPolicies, new PowerStatsUidResolver());
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
                                 checkinStats.dumpCheckin(mContext, pw, apps, flags,
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 192fd6f..f47482d 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -142,6 +142,7 @@
         "core_experiments_team_internal",
         "core_graphics",
         "dck_framework",
+        "devoptions_settings",
         "game",
         "haptics",
         "hardware_backed_security_mainline",
@@ -152,11 +153,13 @@
         "mainline_sdk",
         "media_audio",
         "media_drm",
+        "media_reliability",
         "media_tv",
         "media_solutions",
         "nfc",
         "pdf_viewer",
         "pixel_audio_android",
+        "pixel_bluetooth",
         "pixel_system_sw_touch",
         "pixel_watch",
         "platform_security",
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3f27ca0..5d47e35 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -10667,8 +10667,8 @@
 
     /** @see LoudnessCodecConfigurator#addMediaCodec(MediaCodec) */
     @Override
-    public void addLoudnessCodecInfo(int piid, LoudnessCodecInfo codecInfo) {
-        mLoudnessCodecHelper.addLoudnessCodecInfo(piid, codecInfo);
+    public void addLoudnessCodecInfo(int piid, int mediaCodecHash, LoudnessCodecInfo codecInfo) {
+        mLoudnessCodecHelper.addLoudnessCodecInfo(piid, mediaCodecHash, codecInfo);
     }
 
     /** @see LoudnessCodecConfigurator#removeMediaCodec(MediaCodec) */
@@ -11372,6 +11372,8 @@
     static final int LOG_NB_EVENTS_SPATIAL = 30;
     static final int LOG_NB_EVENTS_SOUND_DOSE = 30;
 
+    static final int LOG_NB_EVENTS_LOUDNESS_CODEC = 30;
+
     static final EventLogger
             sLifecycleLogger = new EventLogger(LOG_NB_EVENTS_LIFECYCLE,
             "audio services lifecycle");
@@ -11571,6 +11573,10 @@
         mSpatializerHelper.dump(pw);
         sSpatialLogger.dump(pw);
 
+        pw.println("\n");
+        pw.println("\nLoudness alignment:");
+        mLoudnessCodecHelper.dump(pw);
+
         mAudioSystem.dump(pw);
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index f69b9f6..de89011 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -622,6 +622,55 @@
         }
     }
 
+    static final class LoudnessEvent extends EventLogger.Event {
+        static final int START_PIID = 0;
+
+        static final int STOP_PIID = 1;
+
+        static final int CLIENT_DIED = 2;
+
+        final int mEventType;
+        final int mIntValue1;
+        final int mIntValue2;
+
+        private LoudnessEvent(int event, int i1, int i2) {
+            mEventType = event;
+            mIntValue1 = i1;
+            mIntValue2 = i2;
+        }
+
+        static LoudnessEvent getStartPiid(int piid, int pid) {
+            return new LoudnessEvent(START_PIID, piid, pid);
+        }
+
+        static LoudnessEvent getStopPiid(int piid, int pid) {
+            return new LoudnessEvent(STOP_PIID, piid, pid);
+        }
+
+        static LoudnessEvent getClientDied(int pid) {
+            return new LoudnessEvent(CLIENT_DIED, 0 /* ignored */, pid);
+        }
+
+
+        @Override
+        public String eventToString() {
+            switch (mEventType) {
+                case START_PIID:
+                    return String.format(
+                            "Start loudness updates for piid %d for client pid %d",
+                            mIntValue1, mIntValue2);
+                case STOP_PIID:
+                    return String.format(
+                            "Stop loudness updates for piid %d for client pid %d",
+                            mIntValue1, mIntValue2);
+                case CLIENT_DIED:
+                    return String.format("Loudness client with pid %d died", mIntValue2);
+
+            }
+            return new StringBuilder("FIXME invalid event type:").append(mEventType).toString();
+        }
+    }
+
     /**
      * Class to log stream type mute/unmute events
      */
diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
index 3c67e9d..bbe819f 100644
--- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
+++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
@@ -21,6 +21,11 @@
 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID;
 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH;
 import static android.media.AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -41,7 +46,11 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.audio.AudioServiceEvents.LoudnessEvent;
+import com.android.server.utils.EventLogger;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -50,6 +59,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Class to handle the updates in loudness parameters and responsible to generate parameters that
@@ -70,10 +80,14 @@
     private static final String SYSTEM_PROPERTY_SPEAKER_SPL_RANGE_SIZE =
             "audio.loudness.builtin-speaker-spl-range-size";
 
-    private static final int SPL_RANGE_UNKNOWN = 0;
-    private static final int SPL_RANGE_SMALL = 1;
-    private static final int SPL_RANGE_MEDIUM = 2;
-    private static final int SPL_RANGE_LARGE = 3;
+    @VisibleForTesting
+    static final int SPL_RANGE_UNKNOWN = 0;
+    @VisibleForTesting
+    static final int SPL_RANGE_SMALL = 1;
+    @VisibleForTesting
+    static final int SPL_RANGE_MEDIUM = 2;
+    @VisibleForTesting
+    static final int SPL_RANGE_LARGE = 3;
 
     /** The possible transducer SPL ranges as defined in CTA2075 */
     @IntDef({
@@ -99,12 +113,19 @@
                 pid = (Integer) cookie;
             }
             if (pid != null) {
-                mLoudnessCodecHelper.removePid(pid);
+                if (DEBUG) {
+                    Log.d(TAG, "Client with pid " + pid + " died, removing from receiving updates");
+                }
+                sLogger.enqueue(LoudnessEvent.getClientDied(pid));
+                mLoudnessCodecHelper.onClientPidDied(pid);
             }
             super.onCallbackDied(callback, cookie);
         }
     }
 
+    private static final EventLogger sLogger = new EventLogger(
+            AudioService.LOG_NB_EVENTS_LOUDNESS_CODEC, "Loudness updates");
+
     private final LoudnessRemoteCallbackList mLoudnessUpdateDispatchers =
             new LoudnessRemoteCallbackList(this);
 
@@ -125,7 +146,8 @@
     private final AudioService mAudioService;
 
     /** Contains the properties necessary to compute the codec loudness related parameters. */
-    private static final class LoudnessCodecInputProperties {
+    @VisibleForTesting
+    static final class LoudnessCodecInputProperties {
         private final int mMetadataType;
 
         private final boolean mIsDownmixing;
@@ -200,10 +222,53 @@
         }
 
         PersistableBundle createLoudnessParameters() {
-            // TODO: create bundle with new parameters
-            return new PersistableBundle();
-        }
+            PersistableBundle loudnessParams = new PersistableBundle();
 
+            switch (mDeviceSplRange) {
+                case SPL_RANGE_LARGE:
+                    // corresponds to -31dB attenuation
+                    loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 124);
+                    if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+                        loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 0);
+                    } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+                        // general compression
+                        loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+                    }
+                    break;
+                case SPL_RANGE_MEDIUM:
+                    // corresponds to -24dB attenuation
+                    loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+                    if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+                        loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+                    } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+                        // general compression
+                        loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+                    }
+                    break;
+                case SPL_RANGE_SMALL:
+                    // corresponds to -16dB attenuation
+                    loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 64);
+                    if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+                        loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 1);
+                    } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+                        // limited playback range compression
+                        loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 3);
+                    }
+                    break;
+                default:
+                    // corresponds to -24dB attenuation
+                    loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+                    if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+                        loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+                    } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+                        // general compression
+                        loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+                    }
+                    break;
+            }
+
+            return loudnessParams;
+        }
     }
 
     @GuardedBy("mLock")
@@ -227,22 +292,25 @@
         if (DEBUG) {
             Log.d(TAG, "startLoudnessCodecUpdates: piid " + piid + " codecInfos " + codecInfoList);
         }
-        Set<LoudnessCodecInfo> infoSet;
+
         synchronized (mLock) {
             if (mStartedPiids.contains(piid)) {
                 Log.w(TAG, "Already started loudness updates for piid " + piid);
                 return;
             }
-            infoSet = new HashSet<>(codecInfoList);
+            Set<LoudnessCodecInfo> infoSet = new HashSet<>(codecInfoList);
             mStartedPiids.put(piid, infoSet);
 
-            mPiidToPidCache.put(piid, Binder.getCallingPid());
+            int pid = Binder.getCallingPid();
+            mPiidToPidCache.put(piid, pid);
+
+            sLogger.enqueue(LoudnessEvent.getStartPiid(piid, pid));
         }
 
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
             mAudioService.getActivePlaybackConfigurations().stream().filter(
                     conf -> conf.getPlayerInterfaceId() == piid).findFirst().ifPresent(
-                            apc -> updateCodecParametersForConfiguration(apc, infoSet));
+                    this::updateCodecParametersForConfiguration);
         }
     }
 
@@ -250,20 +318,24 @@
         if (DEBUG) {
             Log.d(TAG, "stopLoudnessCodecUpdates: piid " + piid);
         }
+
         synchronized (mLock) {
             if (!mStartedPiids.contains(piid)) {
                 Log.w(TAG, "Loudness updates are already stopped for piid " + piid);
                 return;
             }
             mStartedPiids.remove(piid);
+
+            sLogger.enqueue(LoudnessEvent.getStopPiid(piid, mPiidToPidCache.get(piid, -1)));
             mPiidToDeviceIdCache.delete(piid);
             mPiidToPidCache.delete(piid);
         }
     }
 
-    void addLoudnessCodecInfo(int piid, LoudnessCodecInfo info) {
+    void addLoudnessCodecInfo(int piid, int mediaCodecHash, LoudnessCodecInfo info) {
         if (DEBUG) {
-            Log.d(TAG, "addLoudnessCodecInfo: piid " + piid + " info " + info);
+            Log.d(TAG, "addLoudnessCodecInfo: piid " + piid + " mcHash " + mediaCodecHash + " info "
+                    + info);
         }
 
         Set<LoudnessCodecInfo> infoSet;
@@ -280,7 +352,20 @@
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
             mAudioService.getActivePlaybackConfigurations().stream().filter(
                     conf -> conf.getPlayerInterfaceId() == piid).findFirst().ifPresent(
-                            apc -> updateCodecParametersForConfiguration(apc, Set.of(info)));
+                            apc -> {
+                                final AudioDeviceInfo deviceInfo = apc.getAudioDeviceInfo();
+                                if (deviceInfo != null) {
+                                    PersistableBundle updateBundle = new PersistableBundle();
+                                    synchronized (mLock) {
+                                        updateBundle.putPersistableBundle(
+                                                Integer.toString(mediaCodecHash),
+                                                getCodecBundle_l(deviceInfo, info));
+                                    }
+                                    if (!updateBundle.isDefinitelyEmpty()) {
+                                        dispatchNewLoudnessParameters(piid, updateBundle);
+                                    }
+                                }
+                            });
         }
     }
 
@@ -298,24 +383,6 @@
         }
     }
 
-    void removePid(int pid) {
-        if (DEBUG) {
-            Log.d(TAG, "Removing pid " + pid + " from receiving updates");
-        }
-        synchronized (mLock) {
-            for (int i = 0; i < mPiidToPidCache.size(); ++i) {
-                int piid = mPiidToPidCache.keyAt(i);
-                if (mPiidToPidCache.get(piid) == pid) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Removing piid  " + piid);
-                    }
-                    mStartedPiids.delete(piid);
-                    mPiidToDeviceIdCache.delete(piid);
-                }
-            }
-        }
-    }
-
     PersistableBundle getLoudnessParams(int piid, LoudnessCodecInfo codecInfo) {
         if (DEBUG) {
             Log.d(TAG, "getLoudnessParams: piid " + piid + " codecInfo " + codecInfo);
@@ -381,48 +448,77 @@
             }
         }
 
-        updateApcList.forEach(apc -> updateCodecParametersForConfiguration(apc, null));
+        updateApcList.forEach(apc -> updateCodecParametersForConfiguration(apc));
     }
 
-    /** Updates and dispatches the new loudness parameters for the {@code codecInfos} set.
+    /** Updates and dispatches the new loudness parameters for all its registered codecs. */
+    void dump(PrintWriter pw) {
+        // Registered clients
+        pw.println("\nRegistered clients:\n");
+        synchronized (mLock) {
+            for (int i = 0; i < mStartedPiids.size(); ++i) {
+                int piid = mStartedPiids.keyAt(i);
+                int pid = mPiidToPidCache.get(piid, -1);
+                final Set<LoudnessCodecInfo> codecInfos = mStartedPiids.get(piid);
+                pw.println(String.format("Player piid %d pid %d active codec types %s\n", piid,
+                        pid, codecInfos.stream().map(Object::toString).collect(
+                                Collectors.joining(", "))));
+            }
+            pw.println();
+        }
+
+        sLogger.dump(pw);
+        pw.println();
+    }
+
+    private void onClientPidDied(int pid) {
+        synchronized (mLock) {
+            for (int i = 0; i < mPiidToPidCache.size(); ++i) {
+                int piid = mPiidToPidCache.keyAt(i);
+                if (mPiidToPidCache.get(piid) == pid) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Removing piid  " + piid);
+                    }
+                    mStartedPiids.delete(piid);
+                    mPiidToDeviceIdCache.delete(piid);
+                }
+            }
+        }
+    }
+
+    /**
+     * Updates and dispatches the new loudness parameters for the {@code codecInfos} set.
      *
      * @param apc the player configuration for which the loudness parameters are updated.
-     * @param codecInfos the codec info for which the parameters are updated. If {@code null},
-     *                   send updates for all the started codecs assigned to {@code apc}
      */
-    private void updateCodecParametersForConfiguration(AudioPlaybackConfiguration apc,
-            Set<LoudnessCodecInfo> codecInfos) {
+    private void updateCodecParametersForConfiguration(AudioPlaybackConfiguration apc) {
         if (DEBUG) {
-            Log.d(TAG, "updateCodecParametersForConfiguration apc:" + apc + " codecInfos: "
-                    + codecInfos);
+            Log.d(TAG, "updateCodecParametersForConfiguration apc:" + apc);
         }
+
         final PersistableBundle allBundles = new PersistableBundle();
         final int piid = apc.getPlayerInterfaceId();
+
         synchronized (mLock) {
-            if (codecInfos == null) {
-                codecInfos = mStartedPiids.get(piid);
-            }
+            final Set<LoudnessCodecInfo> codecInfos = mStartedPiids.get(piid);
 
             final AudioDeviceInfo deviceInfo = apc.getAudioDeviceInfo();
             if (codecInfos != null && deviceInfo != null) {
                 for (LoudnessCodecInfo info : codecInfos) {
-                    allBundles.putPersistableBundle(Integer.toString(info.mediaCodecHashCode),
+                    allBundles.putPersistableBundle(Integer.toString(info.hashCode()),
                             getCodecBundle_l(deviceInfo, info));
                 }
             }
         }
 
         if (!allBundles.isDefinitelyEmpty()) {
-            if (DEBUG) {
-                Log.d(TAG, "Dispatching for piid: " + piid + " bundle: " + allBundles);
-            }
             dispatchNewLoudnessParameters(piid, allBundles);
         }
     }
 
     private void dispatchNewLoudnessParameters(int piid, PersistableBundle bundle) {
         if (DEBUG) {
-            Log.d(TAG, "dispatchNewLoudnessParameters: piid " + piid);
+            Log.d(TAG, "dispatchNewLoudnessParameters: piid " + piid + " bundle: " + bundle);
         }
         final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; ++i) {
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index be78ea2..cecde55 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -149,6 +149,14 @@
     public abstract @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId);
 
     /**
+     * Returns the ID of the device which owns the display with the given ID.
+     *
+     * <p>In case the virtual display ID is invalid or doesn't belong to a virtual device, then
+     * {@link android.content.Context#DEVICE_ID_DEFAULT} is returned.</p>
+     */
+    public abstract int getDeviceIdForDisplayId(int displayId);
+
+    /**
      * Gets the persistent ID for the VirtualDevice with the given device ID.
      *
      * @param deviceId which device we're asking about
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index debf828..d848f4b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -121,6 +121,7 @@
 
         // Display independent, mode dependent values
         float[] brightnessLevelsNits;
+        float[] brightnessLevels = null;
         float[] luxLevels;
         if (isForIdleMode) {
             brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
@@ -130,11 +131,21 @@
         } else {
             brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
             luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux();
+
+            brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels();
+            if (brightnessLevels == null || brightnessLevels.length == 0) {
+                // Load the old configuration in the range [0, 255]. The values need to be
+                // normalized to the range [0, 1].
+                int[] brightnessLevelsInt = resources.getIntArray(
+                        com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+                brightnessLevels = new float[brightnessLevelsInt.length];
+                for (int i = 0; i < brightnessLevels.length; i++) {
+                    brightnessLevels[i] = normalizeAbsoluteBrightness(brightnessLevelsInt[i]);
+                }
+            }
         }
 
         // Display independent, mode independent values
-        int[] brightnessLevelsBacklight = resources.getIntArray(
-                com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
         float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
                 com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
                 1, 1);
@@ -155,8 +166,8 @@
             builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
             return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
                     autoBrightnessAdjustmentMaxGamma, isForIdleMode, displayWhiteBalanceController);
-        } else if (isValidMapping(luxLevels, brightnessLevelsBacklight) && !isForIdleMode) {
-            return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
+        } else if (isValidMapping(luxLevels, brightnessLevels)) {
+            return new SimpleMappingStrategy(luxLevels, brightnessLevels,
                     autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout);
         } else {
             return null;
@@ -620,7 +631,7 @@
         private float mUserBrightness;
         private long mShortTermModelTimeout;
 
-        private SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma,
+        private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma,
                 long timeout) {
             Preconditions.checkArgument(lux.length != 0 && brightness.length != 0,
                     "Lux and brightness arrays must not be empty!");
@@ -635,7 +646,7 @@
             mBrightness = new float[N];
             for (int i = 0; i < N; i++) {
                 mLux[i] = lux[i];
-                mBrightness[i] = normalizeAbsoluteBrightness(brightness[i]);
+                mBrightness[i] = brightness[i];
             }
 
             mMaxGamma = maxGamma;
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 70d4ad2..c26118e 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -20,6 +20,8 @@
 import android.os.Handler;
 import android.view.Display;
 
+import com.android.server.display.feature.DisplayManagerFlags;
+
 import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -39,6 +41,7 @@
     private final Handler mHandler;
     private final Listener mListener;
     private final String mName;
+    private final DisplayManagerFlags mFeatureFlags;
 
     public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
     public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
@@ -50,13 +53,14 @@
     private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1);  // 0 = no mode.
 
     // Called with SyncRoot lock held.
-    public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
-            Context context, Handler handler, Listener listener, String name) {
+    DisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
+            Listener listener, String name, DisplayManagerFlags featureFlags) {
         mSyncRoot = syncRoot;
         mContext = context;
         mHandler = handler;
         mListener = listener;
         mName = name;
+        mFeatureFlags = featureFlags;
     }
 
     /**
@@ -88,6 +92,10 @@
         return mName;
     }
 
+    public final DisplayManagerFlags getFeatureFlags() {
+        return mFeatureFlags;
+    }
+
     /**
      * Registers the display adapter with the display manager.
      *
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 2fdf90d..3b05b47 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -400,6 +400,7 @@
     }
 
     private DisplayDeviceConfig loadDisplayDeviceConfig() {
-        return DisplayDeviceConfig.create(mContext, false);
+        return DisplayDeviceConfig.create(mContext, /* useConfigXml= */ false,
+                mDisplayAdapter.getFeatureFlags());
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index b99de5c..d97127c 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -57,6 +57,7 @@
 import com.android.server.display.config.HighBrightnessMode;
 import com.android.server.display.config.IntegerArray;
 import com.android.server.display.config.LuxThrottling;
+import com.android.server.display.config.LuxToBrightnessMapping;
 import com.android.server.display.config.NitsMap;
 import com.android.server.display.config.NonNegativeFloatToFloatPoint;
 import com.android.server.display.config.Point;
@@ -77,6 +78,7 @@
 import com.android.server.display.config.ThresholdPoint;
 import com.android.server.display.config.UsiVersion;
 import com.android.server.display.config.XmlParser;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.utils.DebugUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -310,16 +312,18 @@
  *          <darkeningLightDebounceIdleMillis>
  *              1000
  *          </darkeningLightDebounceIdleMillis>
- *          <displayBrightnessMapping>
- *              <displayBrightnessPoint>
- *                  <lux>50</lux>
- *                  <nits>45.32</nits>
- *              </displayBrightnessPoint>
- *              <displayBrightnessPoint>
- *                  <lux>80</lux>
- *                  <nits>75.43</nits>
- *              </displayBrightnessPoint>
- *          </displayBrightnessMapping>
+ *          <luxToBrightnessMapping>
+ *            <map>
+ *              <point>
+ *                <first>0</first>
+ *                <second>0.2</second>
+ *              </point>
+ *              <point>
+ *                <first>80</first>
+ *                <second>0.3</second>
+ *              </point>
+ *            </map>
+ *          </luxToBrightnessMapping>
  *      </autoBrightness>
  *
  *      <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
@@ -528,6 +532,7 @@
  *         <majorVersion>2</majorVersion>
  *         <minorVersion>0</minorVersion>
  *     </usiVersion>
+ *     <screenBrightnessCapForWearBedtimeMode>0.1</screenBrightnessCapForWearBedtimeMode>
  *    </displayConfiguration>
  *  }
  *  </pre>
@@ -629,7 +634,6 @@
     // for the corresponding values above
     private float[] mBrightness;
 
-
     /**
      * Array of desired screen brightness in nits corresponding to the lux values
      * in the mBrightnessLevelsLux array. The display brightness is defined as the
@@ -639,20 +643,25 @@
     private float[] mBrightnessLevelsNits;
 
     /**
-     * Array of light sensor lux values to define our levels for auto backlight
-     * brightness support.
+     * Array of desired screen brightness corresponding to the lux values
+     * in the mBrightnessLevelsLux array. The brightness values must be non-negative and
+     * non-decreasing. They must be between {@link PowerManager.BRIGHTNESS_MIN} and
+     * {@link PowerManager.BRIGHTNESS_MAX}. This must be overridden in platform specific overlays
+     */
+    private float[] mBrightnessLevels;
+
+    /**
+     * Array of light sensor lux values to define our levels for auto-brightness support.
      *
-     * The N + 1 entries of this array define N control points defined in mBrightnessLevelsNits,
-     * with first value always being 0 lux
+     * The first lux value is always 0.
      *
-     * The control points must be strictly increasing.  Each control point
-     * corresponds to an entry in the brightness backlight values arrays.
-     * For example, if lux == level[1] (second element of the levels array)
-     * then the brightness will be determined by value[0] (first element
-     * of the brightness values array).
+     * The control points must be strictly increasing. Each control point corresponds to an entry
+     * in the brightness values arrays. For example, if lux == luxLevels[1] (second element
+     * of the levels array) then the brightness will be determined by brightnessLevels[1] (second
+     * element of the brightness values array).
      *
-     * Spline interpolation is used to determine the auto-brightness
-     * backlight values for lux levels between these control points.
+     * Spline interpolation is used to determine the auto-brightness values for lux levels between
+     * these control points.
      */
     private float[] mBrightnessLevelsLux;
 
@@ -843,9 +852,17 @@
     @Nullable
     private HdrBrightnessData mHdrBrightnessData;
 
+    /**
+     * Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+     */
+    private float mBrightnessCapForWearBedtimeMode;
+
+    private final DisplayManagerFlags mFlags;
+
     @VisibleForTesting
-    DisplayDeviceConfig(Context context) {
+    DisplayDeviceConfig(Context context, DisplayManagerFlags flags) {
         mContext = context;
+        mFlags = flags;
     }
 
     /**
@@ -861,9 +878,9 @@
      * @return A configuration instance for the specified display.
      */
     public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
-            boolean isFirstDisplay) {
+            boolean isFirstDisplay, DisplayManagerFlags flags) {
         final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
-                isFirstDisplay);
+                isFirstDisplay, flags);
 
         config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
         return config;
@@ -878,28 +895,29 @@
      *                     or the default values.
      * @return A configuration instance.
      */
-    public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
+    public static DisplayDeviceConfig create(Context context, boolean useConfigXml,
+            DisplayManagerFlags flags) {
         final DisplayDeviceConfig config;
         if (useConfigXml) {
-            config = getConfigFromGlobalXml(context);
+            config = getConfigFromGlobalXml(context, flags);
         } else {
-            config = getConfigFromPmValues(context);
+            config = getConfigFromPmValues(context, flags);
         }
         return config;
     }
 
     private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
-            long physicalDisplayId, boolean isFirstDisplay) {
+            long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
         DisplayDeviceConfig config;
 
         config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
-                physicalDisplayId);
+                physicalDisplayId, flags);
         if (config != null) {
             return config;
         }
 
         config = loadConfigFromDirectory(context, Environment.getVendorDirectory(),
-                physicalDisplayId);
+                physicalDisplayId, flags);
         if (config != null) {
             return config;
         }
@@ -907,7 +925,7 @@
         // If no config can be loaded from any ddc xml at all,
         // prepare a whole config using the global config.xml.
         // Guaranteed not null
-        return create(context, isFirstDisplay);
+        return create(context, isFirstDisplay, flags);
     }
 
     private static DisplayConfiguration loadDefaultConfigurationXml(Context context) {
@@ -960,18 +978,19 @@
     }
 
     private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
-            File baseDirectory, long physicalDisplayId) {
+            File baseDirectory, long physicalDisplayId, DisplayManagerFlags flags) {
         DisplayDeviceConfig config;
         // Create config using filename from physical ID (including "stable" bit).
         config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT,
-                physicalDisplayId);
+                physicalDisplayId, flags);
         if (config != null) {
             return config;
         }
 
         // Create config using filename from physical ID (excluding "stable" bit).
         final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
-        config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
+        config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag,
+                flags);
         if (config != null) {
             return config;
         }
@@ -980,7 +999,7 @@
         final DisplayAddress.Physical physicalAddress =
                 DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
         int port = physicalAddress.getPort();
-        config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port);
+        config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port, flags);
         return config;
     }
 
@@ -1599,6 +1618,13 @@
     }
 
     /**
+     * @return Auto brightness brightening levels
+     */
+    public float[] getAutoBrightnessBrighteningLevels() {
+        return mBrightnessLevels;
+    }
+
+    /**
      * @return Default peak refresh rate of the associated display
      */
     public int getDefaultPeakRefreshRate() {
@@ -1741,6 +1767,13 @@
         return mHostUsiVersion;
     }
 
+    /**
+     * @return Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+     */
+    public float getBrightnessCapForWearBedtimeMode() {
+        return mBrightnessCapForWearBedtimeMode;
+    }
+
     @Override
     public String toString() {
         return "DisplayDeviceConfig{"
@@ -1844,6 +1877,7 @@
                 + mAutoBrightnessDarkeningLightDebounceIdle
                 + ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
                 + ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+                + ", mBrightnessLevels= " + Arrays.toString(mBrightnessLevels)
                 + ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
                 + ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable
                 + "\n"
@@ -1867,36 +1901,39 @@
                 + ", mHighAmbientBrightnessThresholds= "
                 + Arrays.toString(mHighAmbientBrightnessThresholds)
                 + "\n"
-                + "mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
+                + "mScreenOffBrightnessSensorValueToLux= " + Arrays.toString(
                 mScreenOffBrightnessSensorValueToLux)
                 + "\n"
                 + "mUsiVersion= " + mHostUsiVersion + "\n"
-                + "mHdrBrightnessData" + mHdrBrightnessData
+                + "mHdrBrightnessData= " + mHdrBrightnessData + "\n"
+                + "mBrightnessCapForWearBedtimeMode= " + mBrightnessCapForWearBedtimeMode
                 + "}";
     }
 
     private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
-            String suffixFormat, long idNumber) {
+            String suffixFormat, long idNumber, DisplayManagerFlags flags) {
 
         final String suffix = String.format(Locale.ROOT, suffixFormat, idNumber);
         final String filename = String.format(Locale.ROOT, CONFIG_FILE_FORMAT, suffix);
         final File filePath = Environment.buildPath(
                 baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
-        final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+        final DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
         if (config.initFromFile(filePath)) {
             return config;
         }
         return null;
     }
 
-    private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) {
-        DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+    private static DisplayDeviceConfig getConfigFromGlobalXml(Context context,
+            DisplayManagerFlags flags) {
+        DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
         config.initFromGlobalXml();
         return config;
     }
 
-    private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
-        DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+    private static DisplayDeviceConfig getConfigFromPmValues(Context context,
+            DisplayManagerFlags flags) {
+        DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
         config.initFromDefaultValues();
         return config;
     }
@@ -1938,6 +1975,7 @@
                 loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
                 loadUsiVersion(config);
                 mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
+                loadBrightnessCapForWearBedtimeMode(config);
             } else {
                 Slog.w(TAG, "DisplayDeviceConfig file is null");
             }
@@ -1961,6 +1999,7 @@
         loadAutoBrightnessConfigsFromConfigXml();
         loadAutoBrightnessAvailableFromConfigXml();
         loadRefreshRateSetting(null);
+        loadBrightnessCapForWearBedtimeModeFromConfigXml();
         mLoadedFrom = "<config.xml>";
     }
 
@@ -2599,8 +2638,23 @@
      * loading the value from the display config, and if not present, falls back to config.xml.
      */
     private void loadAutoBrightnessDisplayBrightnessMapping(AutoBrightness autoBrightnessConfig) {
-        if (autoBrightnessConfig == null
-                || autoBrightnessConfig.getDisplayBrightnessMapping() == null) {
+        if (mFlags.areAutoBrightnessModesEnabled() && autoBrightnessConfig != null
+                && autoBrightnessConfig.getLuxToBrightnessMapping() != null) {
+            LuxToBrightnessMapping mapping = autoBrightnessConfig.getLuxToBrightnessMapping();
+            final int size = mapping.getMap().getPoint().size();
+            mBrightnessLevels = new float[size];
+            mBrightnessLevelsLux = new float[size];
+            for (int i = 0; i < size; i++) {
+                float backlight = mapping.getMap().getPoint().get(i).getSecond().floatValue();
+                mBrightnessLevels[i] = mBacklightToBrightnessSpline.interpolate(backlight);
+                mBrightnessLevelsLux[i] = mapping.getMap().getPoint().get(i).getFirst()
+                        .floatValue();
+            }
+            if (size > 0 && mBrightnessLevelsLux[0] != 0) {
+                throw new IllegalArgumentException(
+                        "The first lux value in the display brightness mapping must be 0");
+            }
+        } else {
             mBrightnessLevelsNits = getFloatArray(mContext.getResources()
                     .obtainTypedArray(com.android.internal.R.array
                             .config_autoBrightnessDisplayValuesNits), PowerManager
@@ -2608,18 +2662,6 @@
             mBrightnessLevelsLux = getLuxLevels(mContext.getResources()
                     .getIntArray(com.android.internal.R.array
                             .config_autoBrightnessLevels));
-        } else {
-            final int size = autoBrightnessConfig.getDisplayBrightnessMapping()
-                    .getDisplayBrightnessPoint().size();
-            mBrightnessLevelsNits = new float[size];
-            // The first control point is implicit and always at 0 lux.
-            mBrightnessLevelsLux = new float[size + 1];
-            for (int i = 0; i < size; i++) {
-                mBrightnessLevelsNits[i] = autoBrightnessConfig.getDisplayBrightnessMapping()
-                        .getDisplayBrightnessPoint().get(i).getNits().floatValue();
-                mBrightnessLevelsLux[i + 1] = autoBrightnessConfig.getDisplayBrightnessMapping()
-                        .getDisplayBrightnessPoint().get(i).getLux().floatValue();
-            }
         }
     }
 
@@ -3350,6 +3392,23 @@
                 : null;
     }
 
+    private void loadBrightnessCapForWearBedtimeMode(DisplayConfiguration config) {
+        if (config != null) {
+            BigDecimal configBrightnessCap = config.getScreenBrightnessCapForWearBedtimeMode();
+            if (configBrightnessCap != null) {
+                mBrightnessCapForWearBedtimeMode = configBrightnessCap.floatValue();
+            } else {
+                loadBrightnessCapForWearBedtimeModeFromConfigXml();
+            }
+        }
+    }
+
+    private void loadBrightnessCapForWearBedtimeModeFromConfigXml() {
+        mBrightnessCapForWearBedtimeMode = BrightnessSynchronizer.brightnessIntToFloat(
+                mContext.getResources().getInteger(com.android.internal.R.integer
+                        .config_screenBrightnessCapForWearBedtimeMode));
+    }
+
     /**
      * Container for high brightness mode configuration data.
      */
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6a7c17d..2ab15e6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1853,7 +1853,7 @@
             // early apps like SetupWizard/Launcher. In particular, SUW is displayed using
             // the virtual display inside VR before any VR-specific apps even run.
             mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
-                    mHandler, mDisplayDeviceRepo);
+                    mHandler, mDisplayDeviceRepo, mFlags);
             if (mVirtualDisplayAdapter != null) {
                 registerDisplayAdapterLocked(mVirtualDisplayAdapter);
             }
@@ -1871,7 +1871,7 @@
 
     private void registerOverlayDisplayAdapterLocked() {
         registerDisplayAdapterLocked(new OverlayDisplayAdapter(
-                mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
+                mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler, mFlags));
     }
 
     private void registerWifiDisplayAdapterLocked() {
@@ -1880,7 +1880,7 @@
                 || SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
             mWifiDisplayAdapter = new WifiDisplayAdapter(
                     mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
-                    mPersistentDataStore);
+                    mPersistentDataStore, mFlags);
             registerDisplayAdapterLocked(mWifiDisplayAdapter);
         }
     }
@@ -3288,8 +3288,10 @@
     @VisibleForTesting
     static class Injector {
         VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
-                Handler handler, DisplayAdapter.Listener displayAdapterListener) {
-            return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+                Handler handler, DisplayAdapter.Listener displayAdapterListener,
+                DisplayManagerFlags flags) {
+            return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+                    flags);
         }
 
         LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index ff9a1ab..22898a6 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -84,8 +84,6 @@
 
     private final boolean mIsBootDisplayModeSupported;
 
-    private final DisplayManagerFlags mFlags;
-
     private final DisplayNotificationManager mDisplayNotificationManager;
 
     private Context mOverlayContext;
@@ -103,12 +101,11 @@
             Listener listener, DisplayManagerFlags flags,
             DisplayNotificationManager displayNotificationManager,
             Injector injector) {
-        super(syncRoot, context, handler, listener, TAG);
+        super(syncRoot, context, handler, listener, TAG, flags);
         mDisplayNotificationManager = displayNotificationManager;
         mInjector = injector;
         mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
         mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
-        mFlags = flags;
     }
 
     @Override
@@ -510,7 +507,7 @@
             // Load display device config
             final Context context = getOverlayContext();
             mDisplayDeviceConfig = mInjector.createDisplayDeviceConfig(context, mPhysicalDisplayId,
-                    mIsFirstDisplay);
+                    mIsFirstDisplay, getFeatureFlags());
 
             // Load brightness HWC quirk
             mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk(
@@ -831,7 +828,8 @@
                                     + ", state=" + Display.stateToString(state) + ")");
                         }
 
-                        boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
+                        boolean isDisplayOffloadEnabled =
+                                getFeatureFlags().isDisplayOffloadEnabled();
 
                         // We must tell sidekick/displayoffload to stop controlling the display
                         // before we can change its power mode, so do that first.
@@ -1377,8 +1375,8 @@
         }
 
         public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
-                long physicalDisplayId, boolean isFirstDisplay) {
-            return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay);
+                long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
+            return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay, flags);
         }
     }
 
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 2ce7690..22ff2d0 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -36,6 +36,7 @@
 
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.mode.DisplayModeDirector;
 
 import java.io.PrintWriter;
@@ -134,8 +135,9 @@
 
     // Called with SyncRoot lock held.
     public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
-            Context context, Handler handler, Listener listener, Handler uiHandler) {
-        super(syncRoot, context, handler, listener, TAG);
+            Context context, Handler handler, Listener listener, Handler uiHandler,
+            DisplayManagerFlags featureFlags) {
+        super(syncRoot, context, handler, listener, TAG, featureFlags);
         mUiHandler = uiHandler;
     }
 
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index edbd424..ec5ad7d 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -61,6 +61,7 @@
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -88,7 +89,7 @@
 
     // Called with SyncRoot lock held.
     public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
-            Context context, Handler handler, Listener listener) {
+            Context context, Handler handler, Listener listener, DisplayManagerFlags featureFlags) {
         this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() {
             @Override
             public IBinder createDisplay(String name, boolean secure, float requestedRefreshRate) {
@@ -99,14 +100,15 @@
             public void destroyDisplay(IBinder displayToken) {
                 DisplayControl.destroyDisplay(displayToken);
             }
-        });
+        }, featureFlags);
     }
 
     @VisibleForTesting
     VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener,
-            SurfaceControlDisplayFactory surfaceControlDisplayFactory) {
-        super(syncRoot, context, handler, listener, TAG);
+            SurfaceControlDisplayFactory surfaceControlDisplayFactory,
+            DisplayManagerFlags featureFlags) {
+        super(syncRoot, context, handler, listener, TAG, featureFlags);
         mHandler = handler;
         mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
     }
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 7660cf8..aa98cd8 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.utils.DebugUtils;
 
 import java.io.PrintWriter;
@@ -99,8 +100,8 @@
     // Called with SyncRoot lock held.
     public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener,
-            PersistentDataStore persistentDataStore) {
-        super(syncRoot, context, handler, listener, TAG);
+            PersistentDataStore persistentDataStore, DisplayManagerFlags featureFlags) {
+        super(syncRoot, context, handler, listener, TAG, featureFlags);
 
         if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
             throw new RuntimeException("WiFi display was requested, "
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index dfcda40..42ebc40 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -17,6 +17,7 @@
 package com.android.server.display.brightness.clamper;
 
 import android.annotation.NonNull;
+import android.os.Handler;
 import android.os.PowerManager;
 
 import com.android.server.display.DisplayBrightnessState;
@@ -24,13 +25,25 @@
 import java.io.PrintWriter;
 
 /**
- * Provides max allowed brightness
+ * Provides brightness range constraints
  */
 abstract class BrightnessClamper<T> {
 
     protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
     protected boolean mIsActive = false;
 
+    @NonNull
+    protected final Handler mHandler;
+
+    @NonNull
+    protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
+
+    BrightnessClamper(Handler handler,
+            BrightnessClamperController.ClamperChangeListener changeListener) {
+        mHandler = handler;
+        mChangeListener = changeListener;
+    }
+
     float getBrightnessCap() {
         return mBrightnessCap;
     }
@@ -60,6 +73,8 @@
 
     protected enum Type {
         THERMAL,
-        POWER
+        POWER,
+        BEDTIME_MODE,
+        LUX,
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index b574919..01694dd 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -68,14 +68,14 @@
     private boolean mClamperApplied = false;
 
     public BrightnessClamperController(Handler handler,
-            ClamperChangeListener clamperChangeListener, DisplayDeviceData data,  Context context,
+            ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
             DisplayManagerFlags flags) {
         this(new Injector(), handler, clamperChangeListener, data, context, flags);
     }
 
     @VisibleForTesting
     BrightnessClamperController(Injector injector, Handler handler,
-            ClamperChangeListener clamperChangeListener, DisplayDeviceData data,  Context context,
+            ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
             DisplayManagerFlags flags) {
         mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
         mHandler = handler;
@@ -90,7 +90,8 @@
             }
         };
 
-        mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags);
+        mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
+                context);
         mModifiers = injector.getModifiers(context);
         mOnPropertiesChangedListener =
                 properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
@@ -146,7 +147,8 @@
      * Should be moved to DisplayBrightnessState OR derived from DisplayBrightnessState
      * TODO: b/263362199
      */
-    @BrightnessInfo.BrightnessMaxReason public int getBrightnessMaxReason() {
+    @BrightnessInfo.BrightnessMaxReason
+    public int getBrightnessMaxReason() {
         if (mClamperType == null) {
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
         } else if (mClamperType == Type.THERMAL) {
@@ -234,13 +236,20 @@
 
         List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
                 ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
-                DisplayManagerFlags flags) {
+                DisplayManagerFlags flags, Context context) {
             List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
             clampers.add(
                     new BrightnessThermalClamper(handler, clamperChangeListener, data));
             if (flags.isPowerThrottlingClamperEnabled()) {
                 clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
-                            data));
+                        data));
+            }
+            if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
+                clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
+                        clamperChangeListener, data));
+            }
+            if (flags.isEvenDimmerEnabled()) {
+                clampers.add(new BrightnessMinClamper(handler, clamperChangeListener, context));
             }
             return clampers;
         }
@@ -257,7 +266,8 @@
      * Config Data for clampers
      */
     public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
-                BrightnessPowerClamper.PowerData {
+                BrightnessPowerClamper.PowerData,
+            BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
         @NonNull
         private final String mUniqueDisplayId;
         @NonNull
@@ -315,5 +325,10 @@
         public PowerThrottlingConfigData getPowerThrottlingConfigData() {
             return mDisplayDeviceConfig.getPowerThrottlingConfigData();
         }
+
+        @Override
+        public float getBrightnessWearBedtimeModeCap() {
+            return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java
new file mode 100644
index 0000000..71efca1
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessMinClamper.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 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.brightness.clamper;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.utils.DebugUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Class used to prevent the screen brightness dipping below a certain value, based on current
+ * lux conditions.
+ */
+public class BrightnessMinClamper extends BrightnessClamper {
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.BrightnessMinClamper DEBUG && adb reboot'
+    private static final String TAG = "BrightnessMinClamper";
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+
+    private final SettingsObserver mSettingsObserver;
+
+    ContentResolver mContentResolver;
+    private float mNitsLowerBound;
+
+    @VisibleForTesting
+    BrightnessMinClamper(Handler handler,
+            BrightnessClamperController.ClamperChangeListener listener, Context context) {
+        super(handler, listener);
+
+        mContentResolver = context.getContentResolver();
+        mSettingsObserver = new SettingsObserver(mHandler);
+        mHandler.post(() -> {
+            start();
+        });
+    }
+
+    private void recalculateLowerBound() {
+        final int userId = UserHandle.USER_CURRENT;
+        float settingNitsLowerBound = Settings.Secure.getFloatForUser(
+                mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS,
+                /* def= */ PowerManager.BRIGHTNESS_MIN, userId);
+
+        boolean isActive = Settings.Secure.getIntForUser(mContentResolver,
+                Settings.Secure.EVEN_DIMMER_ACTIVATED,
+                /* def= */ 0, userId) == 1;
+
+        // TODO: luxBasedNitsLowerBound = mMinNitsToLuxSpline(currentLux);
+        float luxBasedNitsLowerBound = PowerManager.BRIGHTNESS_MIN;
+        final float nitsLowerBound = Math.max(settingNitsLowerBound, luxBasedNitsLowerBound);
+
+        if (mNitsLowerBound != nitsLowerBound || mIsActive != isActive) {
+            mIsActive = isActive;
+            mNitsLowerBound = nitsLowerBound;
+            if (DEBUG) {
+                Slog.i(TAG, "mIsActive: " + mIsActive);
+            }
+            // TODO: mBrightnessCap = nitsToBrightnessSpline(mNitsLowerBound);
+            mChangeListener.onChanged();
+        }
+    }
+
+    void start() {
+        recalculateLowerBound();
+    }
+
+
+    @Override
+    Type getType() {
+        return Type.LUX;
+    }
+
+    @Override
+    void onDeviceConfigChanged() {
+        // TODO
+    }
+
+    @Override
+    void onDisplayChanged(Object displayData) {
+
+    }
+
+    @Override
+    void stop() {
+        mContentResolver.unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
+    void dump(PrintWriter pw) {
+        pw.println("BrightnessMinClamper:");
+        pw.println("  mBrightnessCap=" + mBrightnessCap);
+        pw.println("  mIsActive=" + mIsActive);
+        pw.println("  mNitsLowerBound=" + mNitsLowerBound);
+        super.dump(pw);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+            mContentResolver.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_MIN_NITS),
+                    false, this);
+            mContentResolver.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_ACTIVATED),
+                    false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            recalculateLowerBound();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
index 339b589..790322d 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
@@ -49,10 +49,6 @@
     private final Injector mInjector;
     @NonNull
     private final DeviceConfigParameterProvider mConfigParameterProvider;
-    @NonNull
-    private final Handler mHandler;
-    @NonNull
-    private final ClamperChangeListener mChangeListener;
     @Nullable
     private PmicMonitor mPmicMonitor;
     // data from DeviceConfig, for all displays, for all dataSets
@@ -99,10 +95,9 @@
     @VisibleForTesting
     BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
             PowerData powerData) {
+        super(handler, listener);
         mInjector = injector;
         mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
-        mHandler = handler;
-        mChangeListener = listener;
 
         mHandler.post(() -> {
             setDisplayData(powerData);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
index 8ae962b..944a8a6 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
@@ -54,10 +54,6 @@
     private final IThermalService mThermalService;
     @NonNull
     private final DeviceConfigParameterProvider mConfigParameterProvider;
-    @NonNull
-    private final Handler mHandler;
-    @NonNull
-    private final ClamperChangeListener mChangelistener;
     // data from DeviceConfig, for all displays, for all dataSets
     // mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
     @NonNull
@@ -108,10 +104,9 @@
     @VisibleForTesting
     BrightnessThermalClamper(Injector injector, Handler handler,
             ClamperChangeListener listener, ThermalData thermalData) {
+        super(handler, listener);
         mThermalService = injector.getThermalService();
         mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
-        mHandler = handler;
-        mChangelistener = listener;
         mHandler.post(() -> {
             setDisplayData(thermalData);
             loadOverrideData();
@@ -220,7 +215,7 @@
         if (brightnessCap  != mBrightnessCap || mIsActive != isActive) {
             mBrightnessCap = brightnessCap;
             mIsActive = isActive;
-            mChangelistener.onChanged();
+            mChangeListener.onChanged();
         }
     }
 
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
new file mode 100644
index 0000000..7e853bf
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 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.brightness.clamper;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+public class BrightnessWearBedtimeModeClamper extends
+        BrightnessClamper<BrightnessWearBedtimeModeClamper.WearBedtimeModeData> {
+
+    public static final int BEDTIME_MODE_OFF = 0;
+    public static final int BEDTIME_MODE_ON = 1;
+
+    private final Context mContext;
+
+    private final ContentObserver mSettingsObserver;
+
+    BrightnessWearBedtimeModeClamper(Handler handler, Context context,
+            BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+        this(new Injector(), handler, context, listener, data);
+    }
+
+    @VisibleForTesting
+    BrightnessWearBedtimeModeClamper(Injector injector, Handler handler, Context context,
+            BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+        super(handler, listener);
+        mContext = context;
+        mBrightnessCap = data.getBrightnessWearBedtimeModeCap();
+        mSettingsObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                final int bedtimeModeSetting = Settings.Global.getInt(
+                        mContext.getContentResolver(),
+                        Settings.Global.Wearable.BEDTIME_MODE,
+                        BEDTIME_MODE_OFF);
+                mIsActive = bedtimeModeSetting == BEDTIME_MODE_ON;
+                mChangeListener.onChanged();
+            }
+        };
+        injector.registerBedtimeModeObserver(context.getContentResolver(), mSettingsObserver);
+    }
+
+    @NonNull
+    @Override
+    Type getType() {
+        return Type.BEDTIME_MODE;
+    }
+
+    @Override
+    void onDeviceConfigChanged() {}
+
+    @Override
+    void onDisplayChanged(WearBedtimeModeData displayData) {
+        mHandler.post(() -> {
+            mBrightnessCap = displayData.getBrightnessWearBedtimeModeCap();
+            mChangeListener.onChanged();
+        });
+    }
+
+    @Override
+    void stop() {
+        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+    }
+
+    interface WearBedtimeModeData {
+        float getBrightnessWearBedtimeModeCap();
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE),
+                    /* notifyForDescendants= */ false, observer, UserHandle.USER_ALL);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 5cfbf26..2d5da71 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -78,6 +78,9 @@
             Flags.FLAG_ENABLE_POWER_THROTTLING_CLAMPER,
             Flags::enablePowerThrottlingClamper);
 
+    private final FlagState mEvenDimmerFlagState = new FlagState(
+            Flags.FLAG_EVEN_DIMMER,
+            Flags::evenDimmer);
     private final FlagState mSmallAreaDetectionFlagState = new FlagState(
             com.android.graphics.surfaceflinger.flags.Flags.FLAG_ENABLE_SMALL_AREA_DETECTION,
             com.android.graphics.surfaceflinger.flags.Flags::enableSmallAreaDetection);
@@ -90,6 +93,14 @@
             Flags.FLAG_ENABLE_EXTERNAL_VSYNC_PROXIMITY_VOTE,
             Flags::enableExternalVsyncProximityVote);
 
+    private final FlagState mBrightnessWearBedtimeModeClamperFlagState = new FlagState(
+            Flags.FLAG_BRIGHTNESS_WEAR_BEDTIME_MODE_CLAMPER,
+            Flags::brightnessWearBedtimeModeClamper);
+
+    private final FlagState mAutoBrightnessModesFlagState = new FlagState(
+            Flags.FLAG_AUTO_BRIGHTNESS_MODES,
+            Flags::autoBrightnessModes);
+
     /** Returns whether connected display management is enabled or not. */
     public boolean isConnectedDisplayManagementEnabled() {
         return mConnectedDisplayManagementFlagState.isEnabled();
@@ -166,6 +177,11 @@
         return mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState.isEnabled();
     }
 
+    /** Returns whether brightness range is allowed to extend below traditional range. */
+    public boolean isEvenDimmerEnabled() {
+        return mEvenDimmerFlagState.isEnabled();
+    }
+
     public boolean isSmallAreaDetectionEnabled() {
         return mSmallAreaDetectionFlagState.isEnabled();
     }
@@ -178,6 +194,17 @@
         return mVsyncProximityVote.isEnabled();
     }
 
+    public boolean isBrightnessWearBedtimeModeClamperEnabled() {
+        return mBrightnessWearBedtimeModeClamperFlagState.isEnabled();
+    }
+
+    /**
+     * @return Whether generic auto-brightness modes are enabled
+     */
+    public boolean areAutoBrightnessModesEnabled() {
+        return mAutoBrightnessModesFlagState.isEnabled();
+    }
+
     /**
      * dumps all flagstates
      * @param pw printWriter
@@ -197,6 +224,8 @@
         pw.println(" " + mSmallAreaDetectionFlagState);
         pw.println(" " + mBrightnessIntRangeUserPerceptionFlagState);
         pw.println(" " + mVsyncProximityVote);
+        pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
+        pw.println(" " + mAutoBrightnessModesFlagState);
     }
 
     private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index d95bdae..1b4d74c 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -106,6 +106,14 @@
 }
 
 flag {
+    name: "even_dimmer"
+    namespace: "display_manager"
+    description: "Feature flag for extending the brightness below traditional range"
+    bug: "179428400"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "brightness_int_range_user_perception"
     namespace: "display_manager"
     description: "Feature flag for converting the brightness integer range to the user perception scale"
@@ -120,3 +128,19 @@
     bug: "284866750"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "brightness_wear_bedtime_mode_clamper"
+    namespace: "display_manager"
+    description: "Feature flag for the Wear Bedtime mode brightness clamper"
+    bug: "293613040"
+    is_fixed_read_only: true
+}
+
+flag {
+    name: "auto_brightness_modes"
+    namespace: "display_manager"
+    description: "Feature flag for generic auto-brightness modes"
+    bug: "293613040"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 8eb03ec..8fa838d 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -21,8 +21,8 @@
 import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
 import static android.view.Display.Mode.INVALID_MODE_ID;
 
-import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRate;
 import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
+import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay;
 
 import android.annotation.IntegerRes;
 import android.annotation.NonNull;
@@ -925,16 +925,11 @@
     }
 
     @VisibleForTesting
-    DisplayObserver getDisplayObserver() {
-        return mDisplayObserver;
-    }
-
-    @VisibleForTesting
     DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
             float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
         synchronized (mLock) {
-            mSettingsObserver.updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate,
-                    defaultRefreshRate, Display.DEFAULT_DISPLAY);
+            mSettingsObserver.updateRefreshRateSettingLocked(
+                    minRefreshRate, peakRefreshRate, defaultRefreshRate);
             return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
         }
     }
@@ -1268,23 +1263,9 @@
             mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
         }
 
-        /**
-         * Update refresh rate settings for all displays
-         */
         private void updateRefreshRateSettingLocked() {
-            Display[] displays = mInjector.getDisplays();
-            for (int i = 0; i < displays.length; i++) {
-                updateRefreshRateSettingLocked(displays[i].getDisplayId());
-            }
-        }
-
-        /**
-         * Update refresh rate settings for a specific display
-         * @param displayId The display ID
-         */
-        private void updateRefreshRateSettingLocked(int displayId) {
             final ContentResolver cr = mContext.getContentResolver();
-            float highestRefreshRate = findHighestRefreshRate(mContext, displayId);
+            float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext);
 
             float minRefreshRate = Settings.System.getFloatForUser(cr,
                     Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
@@ -1312,12 +1293,11 @@
                 }
             }
 
-            updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate,
-                    displayId);
+            updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
         }
 
-        private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate,
-                float defaultRefreshRate, int displayId) {
+        private void updateRefreshRateSettingLocked(
+                float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
             // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
             // used to predict if we're going to be doing frequent refresh rate switching, and if
             // so, enable the brightness observer. The logic here is more complicated and fragile
@@ -1325,9 +1305,9 @@
             Vote peakVote = peakRefreshRate == 0f
                     ? null
                     : Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate));
-            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+            mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
                     peakVote);
-            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+            mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                     Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY));
             Vote defaultVote =
                     defaultRefreshRate == 0f
@@ -1484,8 +1464,7 @@
         }
     }
 
-    @VisibleForTesting
-    public final class DisplayObserver implements DisplayManager.DisplayListener {
+    private final class DisplayObserver implements DisplayManager.DisplayListener {
         // Note that we can never call into DisplayManager or any of the non-POD classes it
         // returns, while holding mLock since it may call into DMS, which might be simultaneously
         // calling into us already holding its own lock.
@@ -1577,7 +1556,6 @@
             updateDisplayModes(displayId, displayInfo);
             updateLayoutLimitedFrameRate(displayId, displayInfo);
             updateUserSettingDisplayPreferredSize(displayInfo);
-            mSettingsObserver.updateRefreshRateSettingLocked(displayId);
         }
 
         @Nullable
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 6f9b7d6..27b01a5 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -67,6 +67,7 @@
 import android.location.LocationManager;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.flags.Flags;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
 import android.location.util.identity.CallerIdentity;
@@ -1048,10 +1049,22 @@
                 stopBatching();
 
                 if (mStarted && mGnssNative.getCapabilities().hasScheduling()) {
-                    // change period and/or lowPowerMode
-                    if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
-                            mFixInterval, mProviderRequest.isLowPower())) {
-                        Log.e(TAG, "set_position_mode failed in updateRequirements");
+                    if (Flags.gnssCallStopBeforeSetPositionMode()) {
+                        GnssPositionMode positionMode = new GnssPositionMode(mPositionMode,
+                                GNSS_POSITION_RECURRENCE_PERIODIC, mFixInterval,
+                                /* preferredAccuracy= */ 0,
+                                /* preferredTime= */ 0,
+                                mProviderRequest.isLowPower());
+                        if (!positionMode.equals(mLastPositionMode)) {
+                            stopNavigating();
+                            startNavigating();
+                        }
+                    } else {
+                        // change period and/or lowPowerMode
+                        if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+                                mFixInterval, mProviderRequest.isLowPower())) {
+                            Log.e(TAG, "set_position_mode failed in updateRequirements");
+                        }
                     }
                 } else if (!mStarted) {
                     // start GPS
@@ -1234,11 +1247,32 @@
             }
 
             int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
-            if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
-                    interval, mProviderRequest.isLowPower())) {
-                setStarted(false);
-                Log.e(TAG, "set_position_mode failed in startNavigating()");
-                return;
+
+            if (Flags.gnssCallStopBeforeSetPositionMode()) {
+                boolean success = mGnssNative.setPositionMode(mPositionMode,
+                        GNSS_POSITION_RECURRENCE_PERIODIC, interval,
+                        /* preferredAccuracy= */ 0,
+                        /* preferredTime= */ 0,
+                        mProviderRequest.isLowPower());
+                if (success) {
+                    mLastPositionMode = new GnssPositionMode(mPositionMode,
+                            GNSS_POSITION_RECURRENCE_PERIODIC, interval,
+                            /* preferredAccuracy= */ 0,
+                            /* preferredTime= */ 0,
+                            mProviderRequest.isLowPower());
+                } else {
+                    mLastPositionMode = null;
+                    setStarted(false);
+                    Log.e(TAG, "set_position_mode failed in startNavigating()");
+                    return;
+                }
+            } else {
+                if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+                        interval, mProviderRequest.isLowPower())) {
+                    setStarted(false);
+                    Log.e(TAG, "set_position_mode failed in startNavigating()");
+                    return;
+                }
             }
             if (!mGnssNative.start()) {
                 setStarted(false);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index aa0b9b8..3c6887c1 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -26,6 +26,7 @@
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
 import static android.app.Notification.FLAG_INSISTENT;
+import static android.app.Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
 import static android.app.Notification.FLAG_NO_CLEAR;
 import static android.app.Notification.FLAG_NO_DISMISS;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
@@ -58,6 +59,7 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.app.Flags.lifetimeExtensionRefactor;
 import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
@@ -1224,7 +1226,7 @@
         public void onClearAll(int callingUid, int callingPid, int userId) {
             synchronized (mNotificationLock) {
                 cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
-                        /*includeCurrentProfiles*/ true);
+                        /*includeCurrentProfiles*/ true, FLAG_ONGOING_EVENT | FLAG_NO_CLEAR);
             }
         }
 
@@ -1498,6 +1500,7 @@
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
+                    r.recordSmartReplied();
                     LogMaker logMaker = r.getLogMaker()
                             .setCategory(MetricsEvent.SMART_REPLY_ACTION)
                             .setSubtype(replyIndex)
@@ -1804,11 +1807,22 @@
                     record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
                 }
                 if (record != null) {
-                    cancelNotification(record.getSbn().getUid(), record.getSbn().getInitialPid(),
-                            record.getSbn().getPackageName(), record.getSbn().getTag(),
-                            record.getSbn().getId(), 0,
-                            FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
-                            true, record.getUserId(), REASON_TIMEOUT, null);
+                    if (lifetimeExtensionRefactor()) {
+                        cancelNotification(record.getSbn().getUid(),
+                                record.getSbn().getInitialPid(),
+                                record.getSbn().getPackageName(), record.getSbn().getTag(),
+                                record.getSbn().getId(), 0,
+                                FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
+                                        | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
+                                true, record.getUserId(), REASON_TIMEOUT, null);
+                    } else {
+                        cancelNotification(record.getSbn().getUid(),
+                                record.getSbn().getInitialPid(),
+                                record.getSbn().getPackageName(), record.getSbn().getTag(),
+                                record.getSbn().getId(), 0,
+                                FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
+                                true, record.getUserId(), REASON_TIMEOUT, null);
+                    }
                 }
             }
         }
@@ -3728,8 +3742,16 @@
         @Override
         public void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id,
                 int userId) {
+            // Don't allow client applications to cancel foreground service notifs, user-initiated
+            // job notifs, autobundled summaries, or notifs that have been replied to.
+            int mustNotHaveFlags = isCallingUidSystem() ? 0 :
+                    (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
+            if (lifetimeExtensionRefactor()) {
+                mustNotHaveFlags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+            }
+
             cancelNotificationInternal(pkg, opPkg, Binder.getCallingUid(), Binder.getCallingPid(),
-                    tag, id, userId);
+                    tag, id, userId, mustNotHaveFlags);
         }
 
         @Override
@@ -3740,9 +3762,16 @@
                     Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
 
             // Don't allow the app to cancel active FGS or UIJ notifications
-            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
-                    pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
-                    userId, REASON_APP_CANCEL_ALL);
+            if (lifetimeExtensionRefactor()) {
+                cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
+                        pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
+                                | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
+                        userId, REASON_APP_CANCEL_ALL);
+            } else {
+                cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
+                        pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
+                        userId, REASON_APP_CANCEL_ALL);
+            }
         }
 
         @Override
@@ -4808,8 +4837,16 @@
                                     r.getSbn().getId(), userId, reason);
                         }
                     } else {
-                        cancelAllLocked(callingUid, callingPid, info.userid,
-                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
+                        if (lifetimeExtensionRefactor()) {
+                            cancelAllLocked(callingUid, callingPid, info.userid,
+                                    REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles(),
+                                    FLAG_ONGOING_EVENT | FLAG_NO_CLEAR
+                                            | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+                        } else {
+                            cancelAllLocked(callingUid, callingPid, info.userid,
+                                    REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles(),
+                                    FLAG_ONGOING_EVENT | FLAG_NO_CLEAR);
+                        }
                     }
                 }
             } finally {
@@ -4923,6 +4960,9 @@
                 int callingUid, int callingPid, String pkg, String tag, int id, int userId,
                 int reason) {
             int mustNotHaveFlags = FLAG_ONGOING_EVENT;
+            if (lifetimeExtensionRefactor()) {
+                mustNotHaveFlags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+            }
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0 /* mustHaveFlags */,
                     mustNotHaveFlags,
                     true,
@@ -6712,7 +6752,12 @@
         @Override
         public void cancelNotification(String pkg, String opPkg, int callingUid, int callingPid,
                 String tag, int id, int userId) {
-            cancelNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, userId);
+            // Don't allow client applications to cancel foreground service notifs,
+            // user-initiated job notifs or autobundled summaries.
+            final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
+                    (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
+            cancelNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, userId,
+                    mustNotHaveFlags);
         }
 
         @Override
@@ -6907,7 +6952,7 @@
     }
 
     void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid,
-            String tag, int id, int userId) {
+            String tag, int id, int userId, int mustNotHaveFlags) {
         userId = ActivityManager.handleIncomingUser(callingPid,
                 callingUid, userId, true, false, "cancelNotificationWithTag", pkg);
 
@@ -6935,10 +6980,6 @@
             }
         }
 
-        // Don't allow client applications to cancel foreground service notifs, user-initiated job
-        // notifs or autobundled summaries.
-        final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
-                (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
         cancelNotification(uid, callingPid, pkg, tag, id, 0,
                 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
     }
@@ -7294,6 +7335,11 @@
 
         notification.flags &= ~FLAG_FSI_REQUESTED_BUT_DENIED;
 
+        // Apps should not create notifications that are lifetime extended.
+        if (lifetimeExtensionRefactor()) {
+            notification.flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        }
+
         if (notification.fullScreenIntent != null) {
             final AttributionSource attributionSource =
                     new AttributionSource.Builder(notificationUid).setPackageName(pkg).build();
@@ -10088,7 +10134,7 @@
 
     @GuardedBy("mNotificationLock")
     void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
-            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
+            ManagedServiceInfo listener, boolean includeCurrentProfiles, int mustNotHaveFlags) {
         final long cancellationElapsedTimeMs = SystemClock.elapsedRealtime();
         mHandler.post(new Runnable() {
             @Override
@@ -10100,7 +10146,7 @@
                             null, userId, 0, 0, reason, listenerName);
 
                     FlagChecker flagChecker = (int flags) -> {
-                        int flagsToCheck = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+                        int flagsToCheck = mustNotHaveFlags;
                         if (REASON_LISTENER_CANCEL_ALL == reason
                                 || REASON_CANCEL_ALL == reason) {
                             flagsToCheck |= FLAG_BUBBLE;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 100c638..64d3a20 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -24,7 +24,9 @@
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
 
+import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
+import android.app.Flags;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -1257,10 +1259,27 @@
         mStats.setExpanded();
     }
 
+    /** Run when the notification is direct replied. */
     public void recordDirectReplied() {
+        if (Flags.lifetimeExtensionRefactor()) {
+            // Mark the NotificationRecord as lifetime extended.
+            Notification notification = getSbn().getNotification();
+            notification.flags |= Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        }
+
         mStats.setDirectReplied();
     }
 
+
+    /** Run when the notification is smart replied. */
+    @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+    public void recordSmartReplied() {
+        Notification notification = getSbn().getNotification();
+        notification.flags |= Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+
+        mStats.setSmartReplied();
+    }
+
     public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
         mStats.setDismissalSurface(surface);
     }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3b3d79e..07e0ddf 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -357,6 +357,12 @@
         final DeletePackageAction action;
         synchronized (mPm.mLock) {
             final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+            if (ps == null) {
+                if (DEBUG_REMOVE) {
+                    Slog.d(TAG, "Attempted to remove non-existent package " + packageName);
+                }
+                return false;
+            }
             final PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
             if (PackageManagerServiceUtils.isSystemApp(ps)
                     && mPm.checkPermission(CONTROL_KEYGUARD, packageName, UserHandle.USER_SYSTEM)
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 0a81b2b..5494bd9 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -539,6 +539,12 @@
     }
 
     @Nullable
+    public PackageSetting getScanRequestDisabledPackageSetting() {
+        assertScanResultExists();
+        return mScanResult.mRequest.mDisabledPkgSetting;
+    }
+
+    @Nullable
     public String getRealPackageName() {
         assertScanResultExists();
         return mScanResult.mRequest.mRealPkgName;
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index d0fdfa9..9384c13 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -856,9 +856,9 @@
         // We may not yet have disabled the updated package yet, so be sure to grab the
         // current setting if that's the case.
         final PackageSetting updatedSystemPs = isUpdatedSystemApp
-                ? installRequest.getDisabledPackageSetting() == null
+                ? installRequest.getScanRequestDisabledPackageSetting() == null
                 ? installRequest.getScanRequestOldPackageSetting()
-                : installRequest.getDisabledPackageSetting()
+                : installRequest.getScanRequestDisabledPackageSetting()
                 : null;
         if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
                 || updatedSystemPs.getPkg().getLibraryNames() == null)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f90bf4b..b89b4a2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1566,6 +1566,7 @@
                 : now - userData.info.creationTime);
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.REQUEST_QUIET_MODE_ENABLED)
+                .setInt(UserJourneyLogger.getUserTypeForStatsd(userData.info.userType))
                 .setStrings(callingPackage)
                 .setBoolean(enableQuietMode)
                 .setTimePeriod(period)
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 62766b8..30bce2f4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -22,6 +22,9 @@
 import static android.app.AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.AppOpsManager.OP_TOAST_WINDOW;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
 import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -563,6 +566,7 @@
     int mShortPressOnWindowBehavior;
     int mPowerVolUpBehavior;
     boolean mStylusButtonsEnabled = true;
+    boolean mKidsModeEnabled;
     boolean mHasSoftInput = false;
     boolean mUseTvRouting;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
@@ -887,6 +891,9 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.STYLUS_BUTTONS_ENABLED), false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.NAV_BAR_KIDS_MODE), false, this,
+                    UserHandle.USER_ALL);
             updateSettings();
         }
 
@@ -2964,12 +2971,44 @@
             mStylusButtonsEnabled = Settings.Secure.getIntForUser(resolver,
                     Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
             mInputManagerInternal.setStylusButtonMotionEventsEnabled(mStylusButtonsEnabled);
+
+            final boolean kidsModeEnabled = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1;
+            if (mKidsModeEnabled != kidsModeEnabled) {
+                mKidsModeEnabled = kidsModeEnabled;
+                updateKidsModeSettings();
+            }
         }
         if (updateRotation) {
             updateRotation(true);
         }
     }
 
+    private void updateKidsModeSettings() {
+        if (mKidsModeEnabled) {
+            // Needed since many Kids apps aren't optimised to support both orientations and it
+            // will be hard for kids to understand the app compat mode.
+            // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once
+            //                  possible.
+            if (mContext.getResources().getBoolean(R.bool.config_reverseDefaultRotation)) {
+                mWindowManagerInternal.setOrientationRequestPolicy(
+                        true /* isIgnoreOrientationRequestDisabled */,
+                        new int[]{SCREEN_ORIENTATION_LANDSCAPE,
+                                SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
+                        new int[]{SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
+                                SCREEN_ORIENTATION_SENSOR_LANDSCAPE});
+            } else {
+                mWindowManagerInternal.setOrientationRequestPolicy(
+                        true /* isIgnoreOrientationRequestDisabled */,
+                        null /* fromOrientations */, null /* toOrientations */);
+            }
+        } else {
+            mWindowManagerInternal.setOrientationRequestPolicy(
+                    false /* isIgnoreOrientationRequestDisabled */,
+                    null /* fromOrientations */, null /* toOrientations */);
+        }
+    }
+
     private DreamManagerInternal getDreamManagerInternal() {
         if (mDreamManagerInternal == null) {
             // If mDreamManagerInternal is null, attempt to re-fetch it.
@@ -3433,7 +3472,7 @@
                 }
                 break;
             case KeyEvent.KEYCODE_DEL:
-            case KeyEvent.KEYCODE_GRAVE:
+            case KeyEvent.KEYCODE_ESCAPE:
                 if (firstDown && event.isMetaPressed()) {
                     logKeyboardSystemsEvent(event, KeyboardLogEvent.BACK);
                     injectBackGesture(event.getDownTime());
@@ -3638,15 +3677,6 @@
                     return true;
                 }
                 break;
-            case KeyEvent.KEYCODE_SPACE:
-                // Handle keyboard layout switching. (META + SPACE)
-                if (firstDown && event.isMetaPressed()) {
-                    int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
-                    sendSwitchKeyboardLayout(event, direction);
-                    logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
-                    return true;
-                }
-                break;
             case KeyEvent.KEYCODE_META_LEFT:
             case KeyEvent.KEYCODE_META_RIGHT:
                 if (down) {
@@ -6432,6 +6462,7 @@
                 pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty());
                 pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
                 pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
+        pw.print(prefix); pw.print("mKidsModeEnabled="); pw.println(mKidsModeEnabled);
 
         mHapticFeedbackVibrationProvider.dump(prefix, pw);
         mGlobalKeyManager.dump(prefix, pw);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index e3f3638..5d90851 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -32,7 +32,6 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
 import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 
@@ -41,6 +40,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ConcurrentUtils;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -809,12 +809,13 @@
 
         private static Policy fromSettings(String settings, String deviceSpecificSettings,
                 DeviceConfig.Properties properties, String configSuffix, Policy defaultPolicy) {
-            final KeyValueListParser parser = new KeyValueListParser(',');
+            final UserSettingDeviceConfigMediator userSettingDeviceConfigMediator =
+                    new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
             configSuffix = TextUtils.emptyIfNull(configSuffix);
 
             // Device-specific parameters.
             try {
-                parser.setString(deviceSpecificSettings == null ? "" : deviceSpecificSettings);
+                userSettingDeviceConfigMediator.setSettingsString(deviceSpecificSettings);
             } catch (IllegalArgumentException e) {
                 Slog.wtf(TAG, "Bad device specific battery saver constants: "
                         + deviceSpecificSettings);
@@ -822,68 +823,58 @@
 
             // Non-device-specific parameters.
             try {
-                parser.setString(settings == null ? "" : settings);
+                userSettingDeviceConfigMediator.setSettingsString(settings);
+                userSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
             } catch (IllegalArgumentException e) {
                 Slog.wtf(TAG, "Bad battery saver constants: " + settings);
             }
 
             // The Settings value overrides everything, since that will be set by the user.
             // The DeviceConfig value takes second place, with the default as the last choice.
-            final float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
-                    properties.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
-                            defaultPolicy.adjustBrightnessFactor));
-            final boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED,
-                    properties.getBoolean(KEY_ADVERTISE_IS_ENABLED + configSuffix,
-                            defaultPolicy.advertiseIsEnabled));
-            final boolean deferFullBackup = parser.getBoolean(KEY_DEFER_FULL_BACKUP,
-                    properties.getBoolean(KEY_DEFER_FULL_BACKUP + configSuffix,
-                            defaultPolicy.deferFullBackup));
-            final boolean deferKeyValueBackup = parser.getBoolean(KEY_DEFER_KEYVALUE_BACKUP,
-                    properties.getBoolean(KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
-                            defaultPolicy.deferKeyValueBackup));
-            final boolean disableAnimation = parser.getBoolean(KEY_DISABLE_ANIMATION,
-                    properties.getBoolean(KEY_DISABLE_ANIMATION + configSuffix,
-                            defaultPolicy.disableAnimation));
-            final boolean disableAod = parser.getBoolean(KEY_DISABLE_AOD,
-                    properties.getBoolean(KEY_DISABLE_AOD + configSuffix,
-                            defaultPolicy.disableAod));
-            final boolean disableLaunchBoost = parser.getBoolean(KEY_DISABLE_LAUNCH_BOOST,
-                    properties.getBoolean(KEY_DISABLE_LAUNCH_BOOST + configSuffix,
-                            defaultPolicy.disableLaunchBoost));
-            final boolean disableOptionalSensors = parser.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS,
-                    properties.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
-                            defaultPolicy.disableOptionalSensors));
-            final boolean disableVibrationConfig = parser.getBoolean(KEY_DISABLE_VIBRATION,
-                    properties.getBoolean(KEY_DISABLE_VIBRATION + configSuffix,
-                            defaultPolicy.disableVibration));
-            final boolean enableBrightnessAdjustment = parser.getBoolean(
-                    KEY_ENABLE_BRIGHTNESS_ADJUSTMENT,
-                    properties.getBoolean(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
-                            defaultPolicy.enableAdjustBrightness));
-            final boolean enableDataSaver = parser.getBoolean(KEY_ENABLE_DATASAVER,
-                    properties.getBoolean(KEY_ENABLE_DATASAVER + configSuffix,
-                            defaultPolicy.enableDataSaver));
-            final boolean enableFirewall = parser.getBoolean(KEY_ENABLE_FIREWALL,
-                    properties.getBoolean(KEY_ENABLE_FIREWALL + configSuffix,
-                            defaultPolicy.enableFirewall));
-            final boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE,
-                    properties.getBoolean(KEY_ENABLE_NIGHT_MODE + configSuffix,
-                            defaultPolicy.enableNightMode));
-            final boolean enableQuickDoze = parser.getBoolean(KEY_ENABLE_QUICK_DOZE,
-                    properties.getBoolean(KEY_ENABLE_QUICK_DOZE + configSuffix,
-                            defaultPolicy.enableQuickDoze));
-            final boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
-                    properties.getBoolean(KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
-                            defaultPolicy.forceAllAppsStandby));
-            final boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
-                    properties.getBoolean(KEY_FORCE_BACKGROUND_CHECK + configSuffix,
-                            defaultPolicy.forceBackgroundCheck));
-            final int locationMode = parser.getInt(KEY_LOCATION_MODE,
-                    properties.getInt(KEY_LOCATION_MODE + configSuffix,
-                            defaultPolicy.locationMode));
-            final int soundTriggerMode = parser.getInt(KEY_SOUNDTRIGGER_MODE,
-                    properties.getInt(KEY_SOUNDTRIGGER_MODE + configSuffix,
-                            defaultPolicy.soundTriggerMode));
+            final float adjustBrightnessFactor = userSettingDeviceConfigMediator.getFloat(
+                    KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
+                    defaultPolicy.adjustBrightnessFactor);
+            final boolean advertiseIsEnabled = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_ADVERTISE_IS_ENABLED + configSuffix,
+                    defaultPolicy.advertiseIsEnabled);
+            final boolean deferFullBackup = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_DEFER_FULL_BACKUP + configSuffix, defaultPolicy.deferFullBackup);
+            final boolean deferKeyValueBackup = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
+                    defaultPolicy.deferKeyValueBackup);
+            final boolean disableAnimation = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_DISABLE_ANIMATION + configSuffix, defaultPolicy.disableAnimation);
+            final boolean disableAod = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_DISABLE_AOD + configSuffix, defaultPolicy.disableAod);
+            final boolean disableLaunchBoost = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_DISABLE_LAUNCH_BOOST + configSuffix,
+                    defaultPolicy.disableLaunchBoost);
+            final boolean disableOptionalSensors = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
+                    defaultPolicy.disableOptionalSensors);
+            final boolean disableVibrationConfig = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_DISABLE_VIBRATION + configSuffix, defaultPolicy.disableVibration);
+            final boolean enableBrightnessAdjustment = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
+                    defaultPolicy.enableAdjustBrightness);
+            final boolean enableDataSaver = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_ENABLE_DATASAVER + configSuffix, defaultPolicy.enableDataSaver);
+            final boolean enableFirewall = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_ENABLE_FIREWALL + configSuffix, defaultPolicy.enableFirewall);
+            final boolean enableNightMode = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_ENABLE_NIGHT_MODE + configSuffix, defaultPolicy.enableNightMode);
+            final boolean enableQuickDoze = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_ENABLE_QUICK_DOZE + configSuffix, defaultPolicy.enableQuickDoze);
+            final boolean forceAllAppsStandby = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
+                    defaultPolicy.forceAllAppsStandby);
+            final boolean forceBackgroundCheck = userSettingDeviceConfigMediator.getBoolean(
+                    KEY_FORCE_BACKGROUND_CHECK + configSuffix,
+                    defaultPolicy.forceBackgroundCheck);
+            final int locationMode = userSettingDeviceConfigMediator.getInt(
+                    KEY_LOCATION_MODE + configSuffix, defaultPolicy.locationMode);
+            final int soundTriggerMode = userSettingDeviceConfigMediator.getInt(
+                    KEY_SOUNDTRIGGER_MODE + configSuffix, defaultPolicy.soundTriggerMode);
             return new Policy(
                     adjustBrightnessFactor,
                     advertiseIsEnabled,
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index dd39fb0..c17d6ab 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -51,6 +51,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 
 /** An hint service implementation that runs in System Server process. */
@@ -544,7 +545,11 @@
                 if (mHalSessionPtr == 0) return;
                 mNativeWrapper.halCloseHintSession(mHalSessionPtr);
                 mHalSessionPtr = 0;
-                mToken.unlinkToDeath(this, 0);
+                try {
+                    mToken.unlinkToDeath(this, 0);
+                } catch (NoSuchElementException ignored) {
+                    Slogf.d(TAG, "Death link does not exist for session with UID " + mUid);
+                }
             }
             synchronized (mLock) {
                 ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid);
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 0491c14..b8d26d9 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -286,6 +286,7 @@
     private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
     private int[] mCpuPowerBracketMap;
     private final CpuPowerStatsCollector mCpuPowerStatsCollector;
+    private boolean mPowerStatsCollectorEnabled;
 
     public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
         return mKernelMemoryStats;
@@ -601,6 +602,10 @@
     @SuppressWarnings("GuardedBy")    // errorprone false positive on getProcStateTimeCounter
     @VisibleForTesting
     public void updateProcStateCpuTimesLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
+        if (mPowerStatsCollectorEnabled) {
+            return;
+        }
+
         ensureKernelSingleUidTimeReaderLocked();
 
         final Uid u = getUidStatsLocked(uid);
@@ -653,8 +658,9 @@
      */
     @SuppressWarnings("GuardedBy")    // errorprone false positive on getProcStateTimeCounter
     public void updateCpuTimesForAllUids() {
-        if (mCpuPowerStatsCollector != null) {
+        if (mPowerStatsCollectorEnabled && mCpuPowerStatsCollector != null) {
             mCpuPowerStatsCollector.schedule();
+            return;
         }
 
         synchronized (BatteryStatsImpl.this) {
@@ -712,7 +718,7 @@
 
     @GuardedBy("this")
     private void ensureKernelSingleUidTimeReaderLocked() {
-        if (mKernelSingleUidTimeReader != null) {
+        if (mPowerStatsCollectorEnabled || mKernelSingleUidTimeReader != null) {
             return;
         }
 
@@ -8213,6 +8219,10 @@
 
         @GuardedBy("mBsi")
         private void ensureMultiStateCounters(long timestampMs) {
+            if (mBsi.mPowerStatsCollectorEnabled) {
+                throw new IllegalStateException("Multi-state counters used in streamlined mode");
+            }
+
             if (mProcStateTimeMs == null) {
                 mProcStateTimeMs =
                         new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
@@ -10511,7 +10521,7 @@
                     mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
                 }
 
-                if (mBsi.trackPerProcStateCpuTimes()) {
+                if (!mBsi.mPowerStatsCollectorEnabled && mBsi.trackPerProcStateCpuTimes()) {
                     mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs, uptimeMs);
 
                     LongArrayMultiStateCounter onBatteryCounter =
@@ -14211,7 +14221,7 @@
             mCpuUidFreqTimeReader.onSystemReady();
         }
         if (mCpuPowerStatsCollector != null) {
-            mCpuPowerStatsCollector.onSystemReady();
+            mCpuPowerStatsCollector.setEnabled(mPowerStatsCollectorEnabled);
         }
         mSystemReady = true;
     }
@@ -15232,6 +15242,15 @@
         return mCpuUidFreqTimeReader.isFastCpuTimesReader();
     }
 
+    /**
+     * Enables or disables the PowerStatsCollector mode.
+     */
+    public void setPowerStatsCollectorEnabled(boolean enabled) {
+        synchronized (this) {
+            mPowerStatsCollectorEnabled = enabled;
+        }
+    }
+
     @GuardedBy("this")
     public void systemServicesReady(Context context) {
         mConstants.startObserving(context.getContentResolver());
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index c05407c..4442845 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -35,7 +35,6 @@
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.PowerStats;
 import com.android.server.LocalServices;
-import com.android.server.power.optimization.Flags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -273,13 +272,6 @@
         mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
     }
 
-    /**
-     * Initializes the collector during the boot sequence.
-     */
-    public void onSystemReady() {
-        setEnabled(Flags.streamlinedBatteryStats());
-    }
-
     private boolean ensureInitialized() {
         if (mIsInitialized) {
             return true;
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 0656a6a..59766ec 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -35,6 +35,7 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS;
+import static android.hardware.SensorPrivacyManager.EXTRA_NOTIFICATION_ID;
 import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
 import static android.hardware.SensorPrivacyManager.EXTRA_TOGGLE_TYPE;
 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
@@ -164,6 +165,7 @@
     private final AppOpsManagerInternal mAppOpsManagerInternal;
     private final TelephonyManager mTelephonyManager;
     private final PackageManagerInternal mPackageManagerInternal;
+    private final NotificationManager mNotificationManager;
 
     private CameraPrivacyLightController mCameraPrivacyLightController;
 
@@ -188,6 +190,7 @@
         mActivityTaskManager = context.getSystemService(ActivityTaskManager.class);
         mTelephonyManager = context.getSystemService(TelephonyManager.class);
         mPackageManagerInternal = getLocalService(PackageManagerInternal.class);
+        mNotificationManager = mContext.getSystemService(NotificationManager.class);
         mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
     }
 
@@ -288,9 +291,18 @@
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     setToggleSensorPrivacy(
-                            ((UserHandle) intent.getParcelableExtra(
-                                    Intent.EXTRA_USER, android.os.UserHandle.class)).getIdentifier(), OTHER,
-                            intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
+                            intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class)
+                                    .getIdentifier(),
+                            OTHER,
+                            intent.getIntExtra(EXTRA_SENSOR, UNKNOWN),
+                            false
+                    );
+
+                    int notificationId =
+                            intent.getIntExtra(EXTRA_NOTIFICATION_ID, SystemMessage.NOTE_UNKNOWN);
+                    if (notificationId != SystemMessage.NOTE_UNKNOWN) {
+                        mNotificationManager.cancel(notificationId);
+                    }
                 }
             }, new IntentFilter(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY),
                     MANAGE_SENSOR_PRIVACY, null, Context.RECEIVER_EXPORTED);
@@ -635,8 +647,6 @@
                 notificationId = SystemMessage.NOTE_UNBLOCK_CAM_TOGGLE;
             }
 
-            NotificationManager notificationManager =
-                    mContext.getSystemService(NotificationManager.class);
             NotificationChannel channel = new NotificationChannel(
                     SENSOR_PRIVACY_CHANNEL_ID,
                     getUiContext().getString(R.string.sensor_privacy_notification_channel_label),
@@ -646,7 +656,7 @@
             channel.enableVibration(false);
             channel.setBlockable(false);
 
-            notificationManager.createNotificationChannel(channel);
+            mNotificationManager.createNotificationChannel(channel);
 
             Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes);
 
@@ -669,10 +679,11 @@
                     new Intent(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY)
                             .setPackage(mContext.getPackageName())
                             .putExtra(EXTRA_SENSOR, sensor)
+                            .putExtra(EXTRA_NOTIFICATION_ID, notificationId)
                             .putExtra(Intent.EXTRA_USER, user),
                     PendingIntent.FLAG_IMMUTABLE
                             | PendingIntent.FLAG_UPDATE_CURRENT);
-            notificationManager.notify(notificationId,
+            mNotificationManager.notify(notificationId,
                     new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID)
                             .setContentTitle(contentTitle)
                             .setContentText(contentText)
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index e6273d3..0467d0c 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -38,6 +38,7 @@
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvRecordingInfo;
 import android.media.tv.TvTrackInfo;
+import android.media.tv.ad.ITvAdManager;
 import android.media.tv.interactive.AppLinkInfo;
 import android.media.tv.interactive.ITvInteractiveAppClient;
 import android.media.tv.interactive.ITvInteractiveAppManager;
@@ -345,6 +346,7 @@
             Slogf.d(TAG, "onStart");
         }
         publishBinderService(Context.TV_INTERACTIVE_APP_SERVICE, new BinderService());
+        publishBinderService(Context.TV_AD_SERVICE, new TvAdBinderService());
     }
 
     @Override
@@ -688,6 +690,12 @@
         }
         return session;
     }
+    private final class TvAdBinderService extends ITvAdManager.Stub {
+        @Override
+        public void startAdService(IBinder sessionToken, int userId) {
+        }
+
+    }
 
     private final class BinderService extends ITvInteractiveAppManager.Stub {
 
diff --git a/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java b/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java
new file mode 100644
index 0000000..e542349
--- /dev/null
+++ b/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2023 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.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
+import android.util.KeyValueListParser;
+
+/**
+ * Helper class to mediate the value to use when a constant exists in both a key=value pair Settings
+ * constant (that can be parsed by {@link KeyValueListParser})
+ * and the {@link DeviceConfig} properties.
+ */
+public abstract class UserSettingDeviceConfigMediator {
+    private static final String TAG = UserSettingDeviceConfigMediator.class.getSimpleName();
+
+    @Nullable
+    protected DeviceConfig.Properties mProperties;
+    @NonNull
+    protected final KeyValueListParser mSettingsParser;
+
+    /**
+     * @param keyValueListDelimiter The delimiter passed into the {@link KeyValueListParser}.
+     */
+    protected UserSettingDeviceConfigMediator(char keyValueListDelimiter) {
+        mSettingsParser = new KeyValueListParser(keyValueListDelimiter);
+    }
+
+    /**
+     * Sets the key=value list string to read from. Setting {@code null} will clear any previously
+     * set string.
+     */
+    public void setSettingsString(@Nullable String settings) {
+        mSettingsParser.setString(settings);
+    }
+
+    /**
+     * Sets the DeviceConfig Properties to read from. Setting {@code null} will clear any previously
+     * set properties.
+     */
+    public void setDeviceConfigProperties(@Nullable DeviceConfig.Properties properties) {
+        mProperties = properties;
+    }
+
+    /**
+     * Get the value for key as a boolean.
+     *
+     * @param key          The key to lookup.
+     * @param defaultValue The value to return if the key was not found, or not properly defined.
+     */
+    public abstract boolean getBoolean(@NonNull String key, boolean defaultValue);
+
+    /**
+     * Get the value for key as a float.
+     *
+     * @param key          The key to lookup.
+     * @param defaultValue The value to return if the key was not found, or not properly defined.
+     */
+    public abstract float getFloat(@NonNull String key, float defaultValue);
+
+    /**
+     * Get the value for key as an int.
+     *
+     * @param key          The key to lookup.
+     * @param defaultValue The value to return if the key was not found, or not properly defined.
+     */
+    public abstract int getInt(@NonNull String key, int defaultValue);
+
+    /**
+     * Get the value for key as a long.
+     *
+     * @param key          The key to lookup.
+     * @param defaultValue The value to return if the key was not found, or not properly defined.
+     */
+    public abstract long getLong(@NonNull String key, long defaultValue);
+
+    /**
+     * Get the value for key as a String.
+     *
+     * @param key          The key to lookup.
+     * @param defaultValue The value to return if the key was not found, or not properly defined.
+     */
+    public abstract String getString(@NonNull String key, @Nullable String defaultValue);
+
+    /**
+     * A mediator in which the existence of a single settings key-value pair will override usage
+     * of DeviceConfig properties. That is, if the Settings constant has any values set,
+     * then everything in the DeviceConfig namespace will be ignored.
+     */
+    public static class SettingsOverridesAllMediator extends UserSettingDeviceConfigMediator {
+        public SettingsOverridesAllMediator(char keyValueListDelimiter) {
+            super(keyValueListDelimiter);
+        }
+
+        @Override
+        public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+            if (mSettingsParser.size() == 0) {
+                return mProperties == null
+                        ? defaultValue : mProperties.getBoolean(key, defaultValue);
+            }
+            return mSettingsParser.getBoolean(key, defaultValue);
+        }
+
+        @Override
+        public float getFloat(@NonNull String key, float defaultValue) {
+            if (mSettingsParser.size() == 0) {
+                return mProperties == null ? defaultValue : mProperties.getFloat(key, defaultValue);
+            }
+            return mSettingsParser.getFloat(key, defaultValue);
+        }
+
+        @Override
+        public int getInt(@NonNull String key, int defaultValue) {
+            if (mSettingsParser.size() == 0) {
+                return mProperties == null ? defaultValue : mProperties.getInt(key, defaultValue);
+            }
+            return mSettingsParser.getInt(key, defaultValue);
+        }
+
+        @Override
+        public long getLong(@NonNull String key, long defaultValue) {
+            if (mSettingsParser.size() == 0) {
+                return mProperties == null ? defaultValue : mProperties.getLong(key, defaultValue);
+            }
+            return mSettingsParser.getLong(key, defaultValue);
+        }
+
+        @Override
+        public String getString(@NonNull String key, @Nullable String defaultValue) {
+            if (mSettingsParser.size() == 0) {
+                return mProperties == null
+                        ? defaultValue : mProperties.getString(key, defaultValue);
+            }
+            return mSettingsParser.getString(key, defaultValue);
+        }
+    }
+
+    /**
+     * A mediator in which only individual keys in the DeviceConfig namespace will be overridden
+     * by the same key in the Settings constant. If the Settings constant does not have a specific
+     * key set, then the DeviceConfig value will be used instead.
+     */
+    public static class SettingsOverridesIndividualMediator
+            extends UserSettingDeviceConfigMediator {
+        public SettingsOverridesIndividualMediator(char keyValueListDelimiter) {
+            super(keyValueListDelimiter);
+        }
+
+        @Override
+        public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+            return mSettingsParser.getBoolean(key,
+                    mProperties == null ? defaultValue : mProperties.getBoolean(key, defaultValue));
+        }
+
+        @Override
+        public float getFloat(@NonNull String key, float defaultValue) {
+            return mSettingsParser.getFloat(key,
+                    mProperties == null ? defaultValue : mProperties.getFloat(key, defaultValue));
+        }
+
+        @Override
+        public int getInt(@NonNull String key, int defaultValue) {
+            return mSettingsParser.getInt(key,
+                    mProperties == null ? defaultValue : mProperties.getInt(key, defaultValue));
+        }
+
+        @Override
+        public long getLong(@NonNull String key, long defaultValue) {
+            return mSettingsParser.getLong(key,
+                    mProperties == null ? defaultValue : mProperties.getLong(key, defaultValue));
+        }
+
+        @Override
+        public String getString(@NonNull String key, @Nullable String defaultValue) {
+            return mSettingsParser.getString(key,
+                    mProperties == null ? defaultValue : mProperties.getString(key, defaultValue));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index b12da61..e9c4096 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -258,6 +258,11 @@
         }
 
         @Override // Binder call
+        public WebViewProviderInfo getDefaultWebViewPackage() {
+            return WebViewUpdateService.this.mImpl.getDefaultWebViewPackage();
+        }
+
+        @Override // Binder call
         public WebViewProviderInfo[] getAllWebViewPackages() {
             return WebViewUpdateService.this.mImpl.getWebViewPackages();
         }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 26c3fba..60dc4ff 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -421,6 +421,13 @@
         return providers;
     }
 
+    @Override
+    public WebViewProviderInfo getDefaultWebViewPackage() {
+        throw new IllegalStateException(
+                "getDefaultWebViewPackage shouldn't be called if update_service_v2 flag is"
+                        + " disabled.");
+    }
+
     private static class ProviderAndPackageInfo {
         public final WebViewProviderInfo provider;
         public final PackageInfo packageInfo;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index 89cb4c8..29782d9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -24,6 +24,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
 import android.util.Slog;
 import android.webkit.UserPackage;
 import android.webkit.WebViewFactory;
@@ -374,6 +375,23 @@
         return providers;
     }
 
+    /**
+     * Returns the default WebView provider which should be first availableByDefault option in the
+     * system config.
+     */
+    @Override
+    public WebViewProviderInfo getDefaultWebViewPackage() {
+        WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+        for (WebViewProviderInfo provider : webviewProviders) {
+            if (provider.availableByDefault) {
+                return provider;
+            }
+        }
+        // This should be unreachable because the config parser enforces that there is at least one
+        // availableByDefault provider.
+        throw new AndroidRuntimeException("No available by default WebView Provider.");
+    }
+
     private static class ProviderAndPackageInfo {
         public final WebViewProviderInfo provider;
         public final PackageInfo packageInfo;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
index a9c3dc4..1772ef9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
@@ -40,6 +40,8 @@
 
     WebViewProviderInfo[] getValidWebViewPackages();
 
+    WebViewProviderInfo getDefaultWebViewPackage();
+
     PackageInfo getCurrentWebViewPackage();
 
     boolean isMultiProcessEnabled();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d6302e0..bbaa691 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1582,6 +1582,7 @@
                 // An activity has changed order/visibility or the task is occluded by a transient
                 // activity, so this isn't just deliver-to-top
                 && mMovedToTopActivity == null
+                && !transitionController.hasOrderChanges()
                 && !transitionController.isTransientHide(startedActivityRootTask)) {
             // We just delivered to top, so there isn't an actual transition here.
             if (!forceTransientTransition) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index eafaf8c..4625b4fe 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -60,9 +60,9 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.Preconditions;
 import com.android.server.UiThread;
 import com.android.server.am.PendingIntentRecord;
+import com.android.window.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.util.HashMap;
@@ -236,6 +236,7 @@
         private final @ActivityManager.ProcessState int mCallingUidProcState;
         private final boolean mIsCallingUidPersistentSystemProcess;
         private final BackgroundStartPrivileges mBalAllowedByPiSender;
+        private final BackgroundStartPrivileges mBalAllowedByPiCreatorWithHardening;
         private final BackgroundStartPrivileges mBalAllowedByPiCreator;
         private final String mRealCallingPackage;
         private final int mRealCallingUid;
@@ -267,20 +268,33 @@
             mIntent = intent;
             mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
             if (originatingPendingIntent == null) {
-                // grant creator BAL privileges unless explicitly opted out
-                mBalAllowedByPiCreator =
+                // grant BAL privileges unless explicitly opted out
+                mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator =
                         checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
                                 == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
                                 ? BackgroundStartPrivileges.NONE
                                 : BackgroundStartPrivileges.ALLOW_BAL;
+                mBalAllowedByPiSender =
+                        checkedOptions.getPendingIntentBackgroundActivityStartMode()
+                                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+                                ? BackgroundStartPrivileges.NONE
+                                : BackgroundStartPrivileges.ALLOW_BAL;
             } else {
                 // for PendingIntents we restrict BAL based on target_sdk
-                mBalAllowedByPiCreator = getBackgroundStartPrivilegesAllowedByCreator(
+                mBalAllowedByPiCreatorWithHardening = getBackgroundStartPrivilegesAllowedByCreator(
                         callingUid, callingPackage, checkedOptions);
+                final BackgroundStartPrivileges mBalAllowedByPiCreatorWithoutHardening =
+                        checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                                == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+                                ? BackgroundStartPrivileges.NONE
+                                : BackgroundStartPrivileges.ALLOW_BAL;
+                mBalAllowedByPiCreator = balRequireOptInByPendingIntentCreator()
+                        ? mBalAllowedByPiCreatorWithHardening
+                        : mBalAllowedByPiCreatorWithoutHardening;
+                mBalAllowedByPiSender =
+                        PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+                                checkedOptions, realCallingUid, mRealCallingPackage);
             }
-            mBalAllowedByPiSender =
-                    PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
-                            checkedOptions, realCallingUid, mRealCallingPackage);
             mAppSwitchState = mService.getBalAppSwitchesState();
             mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
             mIsCallingUidPersistentSystemProcess =
@@ -319,10 +333,6 @@
                     return BackgroundStartPrivileges.NONE;
                 case ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
                     // no explicit choice by the app - let us decide what to do
-                    if (!balRequireOptInByPendingIntentCreator()) {
-                        // if feature is disabled allow
-                        return BackgroundStartPrivileges.ALLOW_BAL;
-                    }
                     if (callingPackage != null) {
                         // determine based on the calling/creating package
                         boolean changeEnabled = CompatChanges.isChangeEnabled(
@@ -367,11 +377,6 @@
             return mOriginatingPendingIntent != null && hasRealCaller();
         }
 
-        private String dump(BalVerdict resultIfPiCreatorAllowsBal) {
-            Preconditions.checkState(!isPendingIntent());
-            return dump(resultIfPiCreatorAllowsBal, null);
-        }
-
         private boolean callerIsRealCaller() {
             return mCallingUid == mRealCallingUid;
         }
@@ -396,6 +401,8 @@
                 sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
             }
             sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
+            sb.append("; balAllowedByPiCreatorWithHardening: ")
+                    .append(mBalAllowedByPiCreatorWithHardening);
             sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal);
             sb.append("; hasRealCaller: ").append(hasRealCaller());
             sb.append("; isPendingIntent: ").append(isPendingIntent());
@@ -579,11 +586,12 @@
                     resultForCaller.allows() && resultForRealCaller.blocks());
         }
 
+        // Handle cases with explicit opt-in
         if (resultForCaller.allows()
                 && checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
                 == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
             if (DEBUG_ACTIVITY_STARTS) {
-                Slog.d(TAG, "Activity start explicitly allowed by PI creator. "
+                Slog.d(TAG, "Activity start explicitly allowed by caller. "
                         + state.dump(resultForCaller, resultForRealCaller));
             }
             return statsLog(resultForCaller, state);
@@ -592,11 +600,12 @@
                 && checkedOptions.getPendingIntentBackgroundActivityStartMode()
                 == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
             if (DEBUG_ACTIVITY_STARTS) {
-                Slog.d(TAG, "Activity start explicitly allowed by PI sender. "
+                Slog.d(TAG, "Activity start explicitly allowed by real caller. "
                         + state.dump(resultForCaller, resultForRealCaller));
             }
             return statsLog(resultForRealCaller, state);
         }
+        // Handle PendingIntent cases with default behavior next
         boolean callerCanAllow = resultForCaller.allows()
                 && checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
                 == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
@@ -807,13 +816,29 @@
                     "realCallingUid has BAL permission.");
         }
 
-        // don't abort if the realCallingUid has a visible window
-        // TODO(b/171459802): We should check appSwitchAllowed also
-        if (state.mRealCallingUidHasAnyVisibleWindow) {
-            return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
-                    /*background*/ false,
-                    "realCallingUid has visible (non-toast) window.");
+        // Normal apps with visible app window will be allowed to start activity if app switching
+        // is allowed, or apps like live wallpaper with non app visible window will be allowed.
+        final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW
+                || state.mAppSwitchState == APP_SWITCH_FG_ONLY;
+        if (Flags.balImproveRealCallerVisibilityCheck()) {
+            if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) {
+                return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+                        /*background*/ false, "realCallingUid has visible window");
+            }
+            if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) {
+                return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+                        /*background*/ false, "realCallingUid has non-app visible window");
+            }
+        } else {
+            // don't abort if the realCallingUid has a visible window
+            // TODO(b/171459802): We should check appSwitchAllowed also
+            if (state.mRealCallingUidHasAnyVisibleWindow) {
+                return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+                        /*background*/ false,
+                        "realCallingUid has visible (non-toast) window.");
+            }
         }
+
         // if the realCallingUid is a persistent system process, abort if the IntentSender
         // wasn't allowed to start an activity
         if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index e6bbd52..c089d10 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -740,6 +740,8 @@
         private final Handler mHandler;
         private final String mName;
 
+        private boolean mInsetsAnimationRunning;
+
         Host(Handler handler, String name) {
             mHandler = handler;
             mName = name;
@@ -841,5 +843,10 @@
         public IBinder getWindowToken() {
             return null;
         }
+
+        @Override
+        public void notifyAnimationRunningStateChanged(boolean running) {
+            mInsetsAnimationRunning = running;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index de802b9..5f08212 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -422,6 +422,11 @@
     // TODO: remove this once the recents animation is moved to the Shell
     SurfaceControl mLastRecentsAnimationOverlay;
 
+    // A surface that is used by TaskFragmentOrganizer to place content on top of own activities and
+    // trusted TaskFragments.
+    @Nullable
+    DecorSurfaceContainer mDecorSurfaceContainer;
+
     static final int LAYER_RANK_INVISIBLE = -1;
     // Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
     // This number will be assigned when we evaluate OOM scores for all visible tasks.
@@ -1540,6 +1545,11 @@
             mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
         }
 
+        if (mDecorSurfaceContainer != null && r == mDecorSurfaceContainer.mOwnerTaskFragment) {
+            // Remove the decor surface if the owner TaskFragment is removed;
+            removeDecorSurface();
+        }
+
         if (hasChild()) {
             updateEffectiveIntent();
 
@@ -2638,6 +2648,9 @@
         }
         // If applicable let the TaskOrganizer know the Task is vanishing.
         setTaskOrganizer(null);
+        if (mDecorSurfaceContainer != null) {
+            mDecorSurfaceContainer.release();
+        }
 
         super.removeImmediately();
         mRemoving = false;
@@ -3644,7 +3657,8 @@
      */
     TaskFragmentParentInfo getTaskFragmentParentInfo() {
         return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(),
-                shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity());
+                shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity(),
+                getDecorSurface());
     }
 
     @Override
@@ -3666,6 +3680,62 @@
         }
     }
 
+    @Override
+    void assignChildLayers(@NonNull SurfaceControl.Transaction t) {
+        int layer = 0;
+        boolean decorSurfacePlaced = false;
+
+        // We use two passes as a way to promote children which
+        // need Z-boosting to the end of the list.
+        for (int j = 0; j < mChildren.size(); ++j) {
+            final WindowContainer wc = mChildren.get(j);
+            wc.assignChildLayers(t);
+            if (!wc.needsZBoost()) {
+                // Place the decor surface under any untrusted content.
+                if (mDecorSurfaceContainer != null && !decorSurfacePlaced
+                        && shouldPlaceDecorSurfaceBelowContainer(wc)) {
+                    mDecorSurfaceContainer.assignLayer(t, layer++);
+                    decorSurfacePlaced = true;
+                }
+                wc.assignLayer(t, layer++);
+
+                // Place the decor surface just above the owner TaskFragment.
+                if (mDecorSurfaceContainer != null && !decorSurfacePlaced
+                        && wc == mDecorSurfaceContainer.mOwnerTaskFragment) {
+                    mDecorSurfaceContainer.assignLayer(t, layer++);
+                    decorSurfacePlaced = true;
+                }
+            }
+        }
+
+        // If not placed yet, the decor surface should be on top of all non-boosted children.
+        if (mDecorSurfaceContainer != null && !decorSurfacePlaced) {
+            mDecorSurfaceContainer.assignLayer(t, layer++);
+            decorSurfacePlaced = true;
+        }
+
+        for (int j = 0; j < mChildren.size(); ++j) {
+            final WindowContainer wc = mChildren.get(j);
+            if (wc.needsZBoost()) {
+                wc.assignLayer(t, layer++);
+            }
+        }
+        if (mOverlayHost != null) {
+            mOverlayHost.setLayer(t, layer++);
+        }
+    }
+
+    boolean shouldPlaceDecorSurfaceBelowContainer(@NonNull WindowContainer wc) {
+        boolean isOwnActivity =
+                wc.asActivityRecord() != null
+                        && wc.asActivityRecord().isUid(effectiveUid);
+        boolean isTrustedTaskFragment =
+                wc.asTaskFragment() != null
+                        && wc.asTaskFragment().isEmbedded()
+                        && wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode();
+        return !isOwnActivity && !isTrustedTaskFragment;
+    }
+
     boolean isTaskId(int taskId) {
         return mTaskId == taskId;
     }
@@ -6673,4 +6743,77 @@
             mOverlayHost.dispatchInsetsChanged(s, mTmpRect);
         }
     }
+
+    /**
+     * Associates the decor surface with the given TF, or create one if there
+     * isn't one in the Task yet. The surface will be removed with the TF,
+     * and become invisible if the TF is invisible. */
+    void moveOrCreateDecorSurfaceFor(TaskFragment taskFragment) {
+        if (mDecorSurfaceContainer != null) {
+            mDecorSurfaceContainer.mOwnerTaskFragment = taskFragment;
+        } else {
+            mDecorSurfaceContainer = new DecorSurfaceContainer(taskFragment);
+            assignChildLayers();
+            sendTaskFragmentParentInfoChangedIfNeeded();
+        }
+    }
+
+    void removeDecorSurface() {
+        if (mDecorSurfaceContainer == null) {
+            return;
+        }
+        mDecorSurfaceContainer.release();
+        mDecorSurfaceContainer = null;
+        sendTaskFragmentParentInfoChangedIfNeeded();
+    }
+
+    @Nullable SurfaceControl getDecorSurface() {
+        return mDecorSurfaceContainer != null ? mDecorSurfaceContainer.mDecorSurface : null;
+    }
+
+    /**
+     * A decor surface that is requested by a {@code TaskFragmentOrganizer} which will be placed
+     * below children windows except for own Activities and TaskFragment in fully trusted mode.
+     */
+    @VisibleForTesting
+    class DecorSurfaceContainer {
+        @VisibleForTesting
+        @NonNull final SurfaceControl mContainerSurface;
+
+        @VisibleForTesting
+        @NonNull final SurfaceControl mDecorSurface;
+
+        // The TaskFragment that requested the decor surface. If it is destroyed, the decor surface
+        // is also released.
+        @VisibleForTesting
+        @NonNull TaskFragment mOwnerTaskFragment;
+
+        private DecorSurfaceContainer(@NonNull TaskFragment initialOwner) {
+            mOwnerTaskFragment = initialOwner;
+            mContainerSurface = makeSurface().setContainerLayer()
+                    .setParent(mSurfaceControl)
+                    .setName(mSurfaceControl + " - decor surface container")
+                    .setEffectLayer()
+                    .setHidden(false)
+                    .setCallsite("Task.DecorSurfaceContainer")
+                    .build();
+
+            mDecorSurface = makeSurface()
+                    .setParent(mContainerSurface)
+                    .setName(mSurfaceControl + " - decor surface")
+                    .setHidden(false)
+                    .setCallsite("Task.DecorSurfaceContainer")
+                    .build();
+        }
+
+        private void assignLayer(@NonNull SurfaceControl.Transaction t, int layer) {
+            t.setLayer(mContainerSurface, layer);
+            t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible());
+        }
+
+        private void release() {
+            mDecorSurface.release();
+            mContainerSurface.release();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 8bc461f..39b4480 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -316,7 +316,8 @@
     /** Organizer that organizing this TaskFragment. */
     @Nullable
     private ITaskFragmentOrganizer mTaskFragmentOrganizer;
-    private int mTaskFragmentOrganizerUid = INVALID_UID;
+    @VisibleForTesting
+    int mTaskFragmentOrganizerUid = INVALID_UID;
     private @Nullable String mTaskFragmentOrganizerProcessName;
 
     /** Client assigned unique token for this TaskFragment if this is created by an organizer. */
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index e7a1cf1..707f9fc 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -762,6 +762,8 @@
                     .setTask(task)
                     .build());
         }
+        // Make sure the parent info changed event will be dispatched if there are no other changes.
+        mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
     }
 
     boolean isSystemOrganizer(@NonNull IBinder organizerToken) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 3a711b2..27cc2d6 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -1182,22 +1182,6 @@
         }
     }
 
-    @Override
-    public void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled,
-            @Nullable int[] fromOrientations, @Nullable int[] toOrientations) {
-        enforceTaskPermission("setOrientationRequestPolicy()");
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mService.mWindowManager
-                        .setOrientationRequestPolicy(isIgnoreOrientationRequestDisabled,
-                                fromOrientations, toOrientations);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
     public boolean handleInterceptBackPressedOnTaskRoot(Task task) {
         if (!shouldInterceptBackPressedOnRootTask(task)) {
             return false;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 642d22f..e5604ec 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1756,6 +1756,27 @@
     }
 
     /**
+     * Checks if the transition contains order changes.
+     *
+     * This is a shallow check that doesn't account for collection in parallel, unlike
+     * {@code collectOrderChanges}
+     */
+    boolean hasOrderChanges() {
+        ArrayList<Task> onTopTasks = new ArrayList<>();
+        // Iterate over target displays to get up to date on top tasks.
+        // Cannot use `mOnTopTasksAtReady` as it's not populated before the `applyReady` is called.
+        for (DisplayContent dc : mTargetDisplays) {
+            addOnTopTasks(dc, onTopTasks);
+        }
+        for (Task task : onTopTasks) {
+            if (!mOnTopTasksStart.contains(task)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Collect tasks which moved-to-top as part of this transition. This also updates the
      * controller's latest-reported when relevant.
      *
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index bacfda5..e648d64 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -792,6 +792,12 @@
         mCollectingTransition.recordTaskOrder(wc);
     }
 
+    /** @see Transition#hasOrderChanges */
+    boolean hasOrderChanges() {
+        if (mCollectingTransition == null) return false;
+        return mCollectingTransition.hasOrderChanges();
+    }
+
     /**
      * Collects the window containers which need to be synced with the changing display area into
      * the current collecting transition.
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index ae171a0..808a11d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -993,4 +993,18 @@
      * @param displayId the id of display to check if there is a software navigation bar.
      */
     public abstract boolean hasNavigationBar(int displayId);
+
+    /**
+     * Controls whether the app-requested screen orientation is always respected.
+     *
+     * @param respected If {@code true}, the app requested orientation is always respected.
+     *                  Otherwise, the system might ignore the request due to
+     *                  {@link com.android.server.wm.DisplayArea#getIgnoreOrientationRequest}.
+     * @param fromOrientations The orientations we want to map to the correspondent orientations
+     *                         in toOrientation.
+     * @param toOrientations The orientations we map to the ones in fromOrientations at the same
+     *                       index
+     */
+    public abstract void setOrientationRequestPolicy(boolean respected,
+            int[] fromOrientations, int[] toOrientations);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dd2b48b..0d2c94d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8523,6 +8523,15 @@
                 mImeTargetChangeListener = listener;
             }
         }
+
+        @Override
+        public void setOrientationRequestPolicy(boolean respected,
+                int[] fromOrientations, int[] toOrientations) {
+            synchronized (mGlobalLock) {
+                WindowManagerService.this.setOrientationRequestPolicy(respected,
+                        fromOrientations, toOrientations);
+            }
+        }
     }
 
     private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 208df6c7..2af6569 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -23,7 +23,9 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE;
 import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
@@ -1468,6 +1470,16 @@
                 }
                 break;
             }
+            case OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE: {
+                final Task task = taskFragment.getTask();
+                task.moveOrCreateDecorSurfaceFor(taskFragment);
+                break;
+            }
+            case OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE: {
+                final Task task = taskFragment.getTask();
+                task.removeDecorSurface();
+                break;
+            }
         }
         return effects;
     }
@@ -1507,6 +1519,19 @@
             return false;
         }
 
+        // TODO (b/293654166) remove the decor surface checks once we clear security reviews
+        if ((opType == OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE
+                || opType == OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE)
+                && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+            final Throwable exception = new SecurityException(
+                    "Only a system organizer can perform OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE"
+                            + " or OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE."
+            );
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+                    opType, exception);
+            return false;
+        }
+
         final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken();
         return secondaryFragmentToken == null
                 || validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType,
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 cca4261..c625b1e 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -159,6 +159,12 @@
                 <xs:element type="usiVersion" name="usiVersion">
                     <xs:annotation name="final"/>
                 </xs:element>
+                <!-- Maximum screen brightness setting when screen brightness capped in
+                Wear Bedtime mode. This must be a non-negative decimal within the range defined by
+                the first and the last brightness value in screenBrightnessMap. -->
+                <xs:element type="nonNegativeDecimal" name="screenBrightnessCapForWearBedtimeMode">
+                    <xs:annotation name="final"/>
+                </xs:element>
             </xs:sequence>
         </xs:complexType>
     </xs:element>
@@ -586,42 +592,39 @@
                         minOccurs="0" maxOccurs="1">
                 <xs:annotation name="final"/>
             </xs:element>
-            <!-- Sets the brightness mapping of the desired screen brightness in nits to the
-             corresponding lux for the current display -->
-            <xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
+            <!-- Sets the brightness mapping of the desired screen brightness to the corresponding
+            lux for the current display -->
+            <xs:element name="luxToBrightnessMapping" type="luxToBrightnessMapping"
                         minOccurs="0" maxOccurs="1">
                 <xs:annotation name="final"/>
             </xs:element>
         </xs:sequence>
     </xs:complexType>
 
-    <!-- Represents the brightness mapping of the desired screen brightness in nits to the
-             corresponding lux for the current display -->
-    <xs:complexType name="displayBrightnessMapping">
-        <xs:sequence>
-            <!-- Sets the list of display brightness points, each representing the desired screen
-            brightness in nits to the corresponding lux for the current display
+    <!-- Sets the list of display brightness points, each representing the desired screen brightness
+    in a certain lux environment.
 
-            The N entries of this array define N + 1 control points as follows:
-            (1-based arrays)
+    The first value of each point is the lux value and the second value is the brightness value.
 
-            Point 1:            (0, nits[1]):             currentLux <= 0
-            Point 2:     (lux[1], nits[2]):       0 < currentLux <= lux[1]
-            Point 3:     (lux[2], nits[3]):  lux[2] < currentLux <= lux[3]
-            ...
-            Point N+1: (lux[N], nits[N+1]):            lux[N] < currentLux
+    The first lux value must be 0.
 
-            The control points must be strictly increasing. Each control point
-            corresponds to an entry in the brightness backlight values arrays.
-            For example, if currentLux == lux[1] (first element of the levels array)
-            then the brightness will be determined by nits[2] (second element
-            of the brightness values array).
-            -->
-            <xs:element name="displayBrightnessPoint" type="displayBrightnessPoint"
-                        minOccurs="1" maxOccurs="unbounded">
-                <xs:annotation name="final"/>
-            </xs:element>
-        </xs:sequence>
+    The control points must be strictly increasing.
+
+    Example: if currentLux == the second lux value in the mapping then the brightness will be
+    determined by the second brightness value in the mapping. Spline interpolation is used
+    to determine the auto-brightness values for lux levels between these control points.
+
+    The brightness values must be non-negative decimals within the range between the first and
+    the last brightness values in screenBrightnessMap.
+
+    This is used in place of config_autoBrightnessLevels and config_autoBrightnessLcdBacklightValues
+    defined in the config XML resource.
+    -->
+    <xs:complexType name="luxToBrightnessMapping">
+        <xs:element name="map" type="nonNegativeFloatToFloatMap">
+            <xs:annotation name="nonnull"/>
+            <xs:annotation name="final"/>
+        </xs:element>
     </xs:complexType>
 
     <!-- Represents a point in the display brightness mapping, representing the lux level from the
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index f767291..8c8c123 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -7,14 +7,14 @@
     method public final java.math.BigInteger getBrighteningLightDebounceMillis();
     method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
     method public final java.math.BigInteger getDarkeningLightDebounceMillis();
-    method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
     method public boolean getEnabled();
+    method public final com.android.server.display.config.LuxToBrightnessMapping getLuxToBrightnessMapping();
     method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
     method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
     method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
     method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
-    method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
     method public void setEnabled(boolean);
+    method public final void setLuxToBrightnessMapping(com.android.server.display.config.LuxToBrightnessMapping);
   }
 
   public class BlockingZoneConfig {
@@ -78,11 +78,6 @@
     method public java.util.List<com.android.server.display.config.Density> getDensity();
   }
 
-  public class DisplayBrightnessMapping {
-    ctor public DisplayBrightnessMapping();
-    method public final java.util.List<com.android.server.display.config.DisplayBrightnessPoint> getDisplayBrightnessPoint();
-  }
-
   public class DisplayBrightnessPoint {
     ctor public DisplayBrightnessPoint();
     method public final java.math.BigInteger getLux();
@@ -110,6 +105,7 @@
     method public final com.android.server.display.config.SensorDetails getProxSensor();
     method public com.android.server.display.config.DisplayQuirks getQuirks();
     method public com.android.server.display.config.RefreshRateConfigs getRefreshRate();
+    method public final java.math.BigDecimal getScreenBrightnessCapForWearBedtimeMode();
     method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault();
     method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap();
     method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxIdleMillis();
@@ -143,6 +139,7 @@
     method public final void setProxSensor(com.android.server.display.config.SensorDetails);
     method public void setQuirks(com.android.server.display.config.DisplayQuirks);
     method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs);
+    method public final void setScreenBrightnessCapForWearBedtimeMode(java.math.BigDecimal);
     method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal);
     method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap);
     method public final void setScreenBrightnessRampDecreaseMaxIdleMillis(java.math.BigInteger);
@@ -220,6 +217,12 @@
     method @NonNull public final java.util.List<com.android.server.display.config.BrightnessLimitMap> getBrightnessLimitMap();
   }
 
+  public class LuxToBrightnessMapping {
+    ctor public LuxToBrightnessMapping();
+    method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getMap();
+    method public final void setMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+  }
+
   public class NitsMap {
     ctor public NitsMap();
     method public String getInterpolation();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0a2e806..1919eb3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2112,6 +2112,15 @@
             networkPolicy.bindConnectivityManager();
             t.traceEnd();
 
+            t.traceBegin("StartSecurityStateManagerService");
+            try {
+                ServiceManager.addService(Context.SECURITY_STATE_SERVICE,
+                        new SecurityStateManagerService(context));
+            } catch (Throwable e) {
+                reportWtf("starting SecurityStateManagerService", e);
+            }
+            t.traceEnd();
+
             t.traceBegin("StartVpnManagerService");
             try {
                 vpnManager = VpnManagerService.create(context);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 97e5826..a2e80f0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -104,7 +104,7 @@
         468.5f,
     };
 
-    private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
+    private static final int[] DISPLAY_LEVELS_INT = {
         9,
         30,
         45,
@@ -118,6 +118,20 @@
         255
     };
 
+    private static final float[] DISPLAY_LEVELS = {
+        0.03f,
+        0.11f,
+        0.17f,
+        0.24f,
+        0.3f,
+        0.37f,
+        0.46f,
+        0.57f,
+        0.7f,
+        0.87f,
+        1
+    };
+
     private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
     private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
     private static final float[] DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT = { 0.03149606299f, 1.0f };
@@ -155,23 +169,23 @@
     DisplayWhiteBalanceController mMockDwbc;
 
     @Test
-    public void testSimpleStrategyMappingAtControlPoints() {
-        Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+    public void testSimpleStrategyMappingAtControlPoints_IntConfig() {
+        Resources res = createResources(DISPLAY_LEVELS_INT);
         DisplayDeviceConfig ddc = createDdc();
         BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
         assertNotNull("BrightnessMappingStrategy should not be null", simple);
         for (int i = 0; i < LUX_LEVELS.length; i++) {
             final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1,
                     PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
-                    PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_BACKLIGHT[i]);
+                    PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_INT[i]);
             assertEquals(expectedLevel,
                     simple.getBrightness(LUX_LEVELS[i]), 0.0001f /*tolerance*/);
         }
     }
 
     @Test
-    public void testSimpleStrategyMappingBetweenControlPoints() {
-        Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+    public void testSimpleStrategyMappingBetweenControlPoints_IntConfig() {
+        Resources res = createResources(DISPLAY_LEVELS_INT);
         DisplayDeviceConfig ddc = createDdc();
         BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
         assertNotNull("BrightnessMappingStrategy should not be null", simple);
@@ -179,14 +193,42 @@
             final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
             final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
             assertTrue("Desired brightness should be between adjacent control points.",
-                    backlight > DISPLAY_LEVELS_BACKLIGHT[i - 1]
-                            && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
+                    backlight > DISPLAY_LEVELS_INT[i - 1]
+                            && backlight < DISPLAY_LEVELS_INT[i]);
+        }
+    }
+
+    @Test
+    public void testSimpleStrategyMappingAtControlPoints_FloatConfig() {
+        Resources res = createResources(EMPTY_INT_ARRAY);
+        DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
+                EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
+        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
+        assertNotNull("BrightnessMappingStrategy should not be null", simple);
+        for (int i = 0; i < LUX_LEVELS.length; i++) {
+            assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]),
+                    /* tolerance= */ 0.0001f);
+        }
+    }
+
+    @Test
+    public void testSimpleStrategyMappingBetweenControlPoints_FloatConfig() {
+        Resources res = createResources(EMPTY_INT_ARRAY);
+        DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
+                EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
+        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
+        assertNotNull("BrightnessMappingStrategy should not be null", simple);
+        for (int i = 1; i < LUX_LEVELS.length; i++) {
+            final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
+            final float brightness = simple.getBrightness(lux);
+            assertTrue("Desired brightness should be between adjacent control points.",
+                    brightness > DISPLAY_LEVELS[i - 1] && brightness < DISPLAY_LEVELS[i]);
         }
     }
 
     @Test
     public void testSimpleStrategyIgnoresNewConfiguration() {
-        Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+        Resources res = createResources(DISPLAY_LEVELS_INT);
         DisplayDeviceConfig ddc = createDdc();
         BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
 
@@ -201,14 +243,14 @@
 
     @Test
     public void testSimpleStrategyIgnoresNullConfiguration() {
-        Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+        Resources res = createResources(DISPLAY_LEVELS_INT);
         DisplayDeviceConfig ddc = createDdc();
         BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
 
         strategy.setBrightnessConfiguration(null);
-        final int n = DISPLAY_LEVELS_BACKLIGHT.length;
+        final int n = DISPLAY_LEVELS_INT.length;
         final float expectedBrightness =
-                (float) DISPLAY_LEVELS_BACKLIGHT[n - 1] / PowerManager.BRIGHTNESS_ON;
+                (float) DISPLAY_LEVELS_INT[n - 1] / PowerManager.BRIGHTNESS_ON;
         assertEquals(expectedBrightness,
                 strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
     }
@@ -322,7 +364,7 @@
 
     @Test
     public void testDefaultStrategyIsPhysical() {
-        Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+        Resources res = createResources(DISPLAY_LEVELS_INT);
         DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
                 DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
         BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -363,13 +405,13 @@
         BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
         assertNull(strategy);
 
-        res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+        res = createResources(DISPLAY_LEVELS_INT);
         strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
         assertNull(strategy);
 
         // Extra backlight level
         final int[] backlight = Arrays.copyOf(
-                DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length + 1);
+                DISPLAY_LEVELS_INT, DISPLAY_LEVELS_INT.length + 1);
         backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
         res = createResources(backlight);
         ddc = createDdc(DISPLAY_RANGE_NITS,
@@ -410,7 +452,7 @@
                 LUX_LEVELS, DISPLAY_LEVELS_NITS);
         assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
         ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
-        res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+        res = createResources(DISPLAY_LEVELS_INT);
         assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
     }
 
@@ -546,16 +588,24 @@
         when(mockDdc.getBrightness()).thenReturn(backlightArray);
         when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(LUX_LEVELS);
         when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY);
+        when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(EMPTY_FLOAT_ARRAY);
         return mockDdc;
     }
 
     private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
             float[] luxLevelsFloat, float[] brightnessLevelsNits) {
+        return createDdc(nitsArray, backlightArray, luxLevelsFloat, brightnessLevelsNits,
+                EMPTY_FLOAT_ARRAY);
+    }
+
+    private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
+            float[] luxLevelsFloat, float[] brightnessLevelsNits, float[] brightnessLevels) {
         DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
         when(mockDdc.getNits()).thenReturn(nitsArray);
         when(mockDdc.getBrightness()).thenReturn(backlightArray);
         when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(luxLevelsFloat);
         when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(brightnessLevelsNits);
+        when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(brightnessLevels);
         return mockDdc;
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 0bcbeb9..31d7e88 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -47,8 +47,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
+import com.android.internal.display.BrightnessSynchronizer;
 import com.android.server.display.config.HdrBrightnessData;
 import com.android.server.display.config.ThermalStatus;
+import com.android.server.display.feature.DisplayManagerFlags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -92,10 +94,14 @@
     @Mock
     private Resources mResources;
 
+    @Mock
+    private DisplayManagerFlags mFlags;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getResources()).thenReturn(mResources);
+        when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
         mockDeviceConfigs();
     }
 
@@ -110,10 +116,6 @@
         assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
         assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
         assertArrayEquals(mDisplayDeviceConfig.getBacklight(), BRIGHTNESS, ZERO_DELTA);
-        assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
-                float[]{0.0f, 50.0f, 80.0f}, ZERO_DELTA);
-        assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
-                float[]{45.32f, 75.43f}, ZERO_DELTA);
 
         // Test thresholds
         assertEquals(10, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(),
@@ -606,7 +608,7 @@
         assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
                 float[]{2.0f, 200.0f, 600.0f}, ZERO_DELTA);
         assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
-                float[]{0.0f, 0.0f, 110.0f, 500.0f}, ZERO_DELTA);
+                float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA);
 
         // Test thresholds
         assertEquals(0, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), ZERO_DELTA);
@@ -671,6 +673,9 @@
 
         assertEquals("test_light_sensor", mDisplayDeviceConfig.getAmbientLightSensor().type);
         assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
+
+        assertEquals(BrightnessSynchronizer.brightnessIntToFloat(35),
+                mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
     }
 
     @Test
@@ -716,6 +721,38 @@
         assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(), 0.04f, ZERO_DELTA);
     }
 
+    @Test
+    public void testBrightnessCapForWearBedtimeMode() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+                getValidProxSensor(), /* includeIdleMode= */ false));
+        assertEquals(0.1f, mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
+    }
+
+    @Test
+    public void testAutoBrightnessBrighteningLevels() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+                getValidProxSensor(), /* includeIdleMode= */ false));
+
+        assertArrayEquals(new float[]{0.0f, 80},
+                mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+        assertArrayEquals(new float[]{0.2f, 0.3f},
+                mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(), SMALL_DELTA);
+    }
+
+    @Test
+    public void testAutoBrightnessBrighteningLevels_FeatureFlagOff() throws IOException {
+        when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(false);
+        setupDisplayDeviceConfigFromConfigResourceFile();
+        setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+                getValidProxSensor(), /* includeIdleMode= */ false));
+
+        assertNull(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels());
+        assertArrayEquals(new float[]{0, 110, 500},
+                mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+        assertArrayEquals(new float[]{2, 200, 600},
+                mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), SMALL_DELTA);
+    }
+
     private String getValidLuxThrottling() {
         return "<luxThrottling>\n"
                 + "    <brightnessLimitMap>\n"
@@ -1037,8 +1074,8 @@
                 +   "<screenBrightnessRampDecreaseMaxIdleMillis>"
                 +       "5000"
                 +   "</screenBrightnessRampDecreaseMaxIdleMillis>\n";
-
     }
+
     private String getContent() {
         return getContent(getValidLuxThrottling(), getValidProxSensor(),
                 /* includeIdleMode= */ true);
@@ -1089,16 +1126,18 @@
                 +       "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
                 +       "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
                 + (includeIdleMode ? getRampSpeedsIdle() : "")
-                +       "<displayBrightnessMapping>\n"
-                +            "<displayBrightnessPoint>\n"
-                +                "<lux>50</lux>\n"
-                +                "<nits>45.32</nits>\n"
-                +            "</displayBrightnessPoint>\n"
-                +            "<displayBrightnessPoint>\n"
-                +                "<lux>80</lux>\n"
-                +                "<nits>75.43</nits>\n"
-                +            "</displayBrightnessPoint>\n"
-                +       "</displayBrightnessMapping>\n"
+                +       "<luxToBrightnessMapping>\n"
+                +           "<map>\n"
+                +               "<point>\n"
+                +                   "<first>0</first>\n"
+                +                   "<second>0.2</second>\n"
+                +               "</point>\n"
+                +               "<point>\n"
+                +                   "<first>80</first>\n"
+                +                   "<second>0.3</second>\n"
+                +               "</point>\n"
+                +           "</map>\n"
+                +       "</luxToBrightnessMapping>\n"
                 +   "</autoBrightness>\n"
                 +  getPowerThrottlingConfig()
                 +   "<highBrightnessMode enabled=\"true\">\n"
@@ -1355,6 +1394,9 @@
                 +       "<majorVersion>2</majorVersion>\n"
                 +       "<minorVersion>0</minorVersion>\n"
                 +   "</usiVersion>\n"
+                +   "<screenBrightnessCapForWearBedtimeMode>"
+                +       "0.1"
+                +   "</screenBrightnessCapForWearBedtimeMode>"
                 + "</displayConfiguration>\n";
     }
 
@@ -1372,7 +1414,7 @@
     private void setupDisplayDeviceConfigFromDisplayConfigFile(String content) throws IOException {
         Path tempFile = Files.createTempFile("display_config", ".tmp");
         Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
-        mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
+        mDisplayDeviceConfig = new DisplayDeviceConfig(mContext, mFlags);
         mDisplayDeviceConfig.initFromFile(tempFile.toFile());
     }
 
@@ -1381,25 +1423,15 @@
         when(mResources.obtainTypedArray(
                 com.android.internal.R.array.config_screenBrightnessNits))
                 .thenReturn(screenBrightnessNits);
-        TypedArray screenBrightnessBacklight = createFloatTypedArray(new
-                float[]{0.0f, 120.0f, 255.0f});
-        when(mResources.obtainTypedArray(
-                com.android.internal.R.array.config_screenBrightnessBacklight))
-                .thenReturn(screenBrightnessBacklight);
         when(mResources.getIntArray(com.android.internal.R.array
                 .config_screenBrightnessBacklight)).thenReturn(new int[]{0, 120, 255});
 
-        when(mResources.getIntArray(com.android.internal.R.array
-                .config_autoBrightnessLevels)).thenReturn(new int[]{30, 80});
-        when(mResources.getIntArray(com.android.internal.R.array
-                .config_autoBrightnessDisplayValuesNits)).thenReturn(new int[]{25, 55});
-
         TypedArray screenBrightnessLevelNits = createFloatTypedArray(new
                 float[]{2.0f, 200.0f, 600.0f});
         when(mResources.obtainTypedArray(
                 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
                 .thenReturn(screenBrightnessLevelNits);
-        int[] screenBrightnessLevelLux = new int[]{0, 110, 500};
+        int[] screenBrightnessLevelLux = new int[]{110, 500};
         when(mResources.getIntArray(
                 com.android.internal.R.array.config_autoBrightnessLevels))
                 .thenReturn(screenBrightnessLevelLux);
@@ -1457,7 +1489,12 @@
                 R.integer.config_autoBrightnessDarkeningLightDebounce))
                 .thenReturn(4000);
 
-        mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
+        when(mResources.getInteger(
+                R.integer.config_screenBrightnessCapForWearBedtimeMode))
+                .thenReturn(35);
+
+        mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, /* useConfigXml= */ true,
+                mFlags);
     }
 
     private TypedArray createFloatTypedArray(float[] vals) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
index 4fd8f26..dc6abf1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
@@ -57,6 +57,9 @@
     @Mock
     private SurfaceControl.Transaction mMockTransaction;
 
+    @Mock
+    private DisplayAdapter mMockDisplayAdapter;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -67,34 +70,39 @@
 
     @Test
     public void testGetDisplaySurfaceDefaultSizeLocked_notRotated() {
-        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+                mMockDisplayAdapter);
         assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
     }
 
     @Test
     public void testGetDisplaySurfaceDefaultSizeLocked_rotation0() {
-        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+                mMockDisplayAdapter);
         displayDevice.setProjectionLocked(mMockTransaction, ROTATION_0, new Rect(), new Rect());
         assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
     }
 
     @Test
     public void testGetDisplaySurfaceDefaultSizeLocked_rotation90() {
-        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+                mMockDisplayAdapter);
         displayDevice.setProjectionLocked(mMockTransaction, ROTATION_90, new Rect(), new Rect());
         assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
     }
 
     @Test
     public void testGetDisplaySurfaceDefaultSizeLocked_rotation180() {
-        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+                mMockDisplayAdapter);
         displayDevice.setProjectionLocked(mMockTransaction, ROTATION_180, new Rect(), new Rect());
         assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
     }
 
     @Test
     public void testGetDisplaySurfaceDefaultSizeLocked_rotation270() {
-        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+                mMockDisplayAdapter);
         displayDevice.setProjectionLocked(mMockTransaction, ROTATION_270, new Rect(), new Rect());
         assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
     }
@@ -102,8 +110,9 @@
     private static class FakeDisplayDevice extends DisplayDevice {
         private final DisplayDeviceInfo mDisplayDeviceInfo;
 
-        FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo) {
-            super(null, null, "", InstrumentationRegistry.getInstrumentation().getContext());
+        FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo, DisplayAdapter displayAdapter) {
+            super(displayAdapter, /* displayToken= */ null, /* uniqueId= */ "",
+                    InstrumentationRegistry.getInstrumentation().getContext());
             mDisplayDeviceInfo = displayDeviceInfo;
         }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 55f56e9..02e3ef4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -212,7 +212,8 @@
             new DisplayManagerService.Injector() {
                 @Override
                 VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
-                        Context context, Handler handler, DisplayAdapter.Listener listener) {
+                        Context context, Handler handler, DisplayAdapter.Listener listener,
+                        DisplayManagerFlags flags) {
                     return mMockVirtualDisplayAdapter;
                 }
 
@@ -251,7 +252,8 @@
 
         @Override
         VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
-                Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+                Handler handler, DisplayAdapter.Listener displayAdapterListener,
+                DisplayManagerFlags flags) {
             return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
                     new VirtualDisplayAdapter.SurfaceControlDisplayFactory() {
                         @Override
@@ -263,7 +265,7 @@
                         @Override
                         public void destroyDisplay(IBinder displayToken) {
                         }
-                    });
+                    }, flags);
         }
 
         @Override
@@ -329,6 +331,7 @@
     @Mock SensorManager mSensorManager;
     @Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
     @Mock PackageManagerInternal mMockPackageManagerInternal;
+    @Mock DisplayAdapter mMockDisplayAdapter;
 
     @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
     @Mock DisplayManagerFlags mMockFlags;
@@ -788,7 +791,8 @@
                 new DisplayManagerService.Injector() {
                     @Override
                     VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
-                            Context context, Handler handler, DisplayAdapter.Listener listener) {
+                            Context context, Handler handler, DisplayAdapter.Listener listener,
+                            DisplayManagerFlags flags) {
                         return null;  // return null for the adapter.  This should cause a failure.
                     }
 
@@ -3194,7 +3198,7 @@
         private DisplayDeviceInfo mDisplayDeviceInfo;
 
         FakeDisplayDevice() {
-            super(null, null, "", mContext);
+            super(mMockDisplayAdapter, /* displayToken= */ null, /* uniqueId= */ "", mContext);
         }
 
         public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 8270845..f36854b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1455,8 +1455,8 @@
         // mMockContext and values will be loaded from mMockResources.
         @Override
         public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
-                long physicalDisplayId, boolean isFirstDisplay) {
-            return DisplayDeviceConfig.create(context, isFirstDisplay);
+                long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
+            return DisplayDeviceConfig.create(context, isFirstDisplay, flags);
         }
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 43d2b8f..28ec896 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -119,8 +119,8 @@
     @Mock IThermalService mIThermalServiceMock;
     @Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy =
             new DeviceStateToLayoutMap(mIdProducer, NON_EXISTING_FILE);
-    @Mock
-    DisplayManagerFlags mFlagsMock;
+    @Mock DisplayManagerFlags mFlagsMock;
+    @Mock DisplayAdapter mDisplayAdapterMock;
 
     @Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
     @Captor ArgumentCaptor<Integer> mDisplayEventCaptor;
@@ -1075,7 +1075,8 @@
         private int mState;
 
         TestDisplayDevice() {
-            super(null, null, "test_display_" + sUniqueTestDisplayId++, mContextMock);
+            super(mDisplayAdapterMock, /* displayToken= */ null,
+                    "test_display_" + sUniqueTestDisplayId++, mContextMock);
             mInfo = new DisplayDeviceInfo();
         }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
index 9f91916..5676a38 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -32,9 +32,12 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -51,8 +54,12 @@
     private TestInjector mInjector;
     private TestLooper mTestLooper;
 
+    @Mock
+    private DisplayAdapter mDisplayAdapter;
+
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mInjector = new TestInjector();
         mTestLooper = new TestLooper();
         Handler handler = new Handler(mTestLooper.getLooper());
@@ -62,8 +69,8 @@
     @Test
     public void testLoadBrightness() {
         final String uniqueDisplayId = "test:123";
-        final DisplayDevice testDisplayDevice = new DisplayDevice(
-                null, null, uniqueDisplayId, null) {
+        final DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+                /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
             @Override
             public boolean hasStableUniqueId() {
                 return true;
@@ -100,7 +107,8 @@
     public void testSetBrightness_brightnessTagWithNoUserId_updatesToBrightnessTagWithUserId() {
         final String uniqueDisplayId = "test:123";
         final DisplayDevice testDisplayDevice =
-                new DisplayDevice(null, null, uniqueDisplayId, null) {
+                new DisplayDevice(mDisplayAdapter, /* displayToken= */ null, uniqueDisplayId,
+                        /* context= */ null) {
             @Override
             public boolean hasStableUniqueId() {
                 return true;
@@ -273,7 +281,8 @@
         assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
                 userSerial));
 
-        DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+        DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+                /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
             @Override
             public boolean hasStableUniqueId() {
                 return true;
@@ -319,7 +328,8 @@
         assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
                 userSerial));
 
-        DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+        DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+                /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
             @Override
             public boolean hasStableUniqueId() {
                 return false;
@@ -386,7 +396,8 @@
     @Test
     public void testStoreAndRestoreResolution() {
         final String uniqueDisplayId = "test:123";
-        DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+        DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+                /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
             @Override
             public boolean hasStableUniqueId() {
                 return true;
@@ -422,7 +433,8 @@
     @Test
     public void testStoreAndRestoreRefreshRate() {
         final String uniqueDisplayId = "test:123";
-        DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+        DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+                /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
             @Override
             public boolean hasStableUniqueId() {
                 return true;
@@ -455,7 +467,8 @@
     @Test
     public void testBrightnessInitialisesWithInvalidFloat() {
         final String uniqueDisplayId = "test:123";
-        DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+        DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+                /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
             @Override
             public boolean hasStableUniqueId() {
                 return true;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
index b7cbac5..5c50acb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
@@ -72,27 +72,14 @@
 
     @Test
     public void testFindHighestRefreshRateForDefaultDisplay() {
-        when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
-        assertEquals(120,
-                RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
-                /* delta= */ 0);
-    }
-
-    @Test
-    public void testFindHighestRefreshRate() {
-        int displayId = 13;
-        when(mDisplayManagerMock.getDisplay(displayId)).thenReturn(mDisplayMock);
-        assertEquals(120,
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, displayId),
-                /* delta= */ 0);
-    }
-
-    @Test
-    public void testFindHighestRefreshRate_DisplayIsNull() {
         when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
         assertEquals(DEFAULT_REFRESH_RATE,
                 RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
                 /* delta= */ 0);
 
+        when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+        assertEquals(120,
+                RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+                /* delta= */ 0);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 73e7ba0..c01b15c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -28,6 +28,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.testutils.TestHandler;
 
 import org.junit.Before;
@@ -59,13 +60,17 @@
 
     private VirtualDisplayAdapter mVirtualDisplayAdapter;
 
+    @Mock
+    private DisplayManagerFlags mFeatureFlags;
+
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mHandler = new TestHandler(null);
         mVirtualDisplayAdapter = new VirtualDisplayAdapter(new DisplayManagerService.SyncRoot(),
-                mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory);
+                mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory,
+                mFeatureFlags);
 
         when(mMockCallback.asBinder()).thenReturn(mMockBinder);
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index ff2b1f4..6ba7368 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -262,7 +262,7 @@
                 Handler handler,
                 BrightnessClamperController.ClamperChangeListener clamperChangeListener,
                 BrightnessClamperController.DisplayDeviceData data,
-                DisplayManagerFlags flags) {
+                DisplayManagerFlags flags, Context context) {
             mCapturedChangeListener = clamperChangeListener;
             return mClampers;
         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
new file mode 100644
index 0000000..3458b08
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 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.brightness.clamper;
+
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_OFF;
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_ON;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class BrightnessWearBedtimeModeClamperTest {
+
+    private static final float BRIGHTNESS_CAP = 0.3f;
+
+    @Mock
+    private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
+
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getContext());
+
+    private final TestHandler mTestHandler = new TestHandler(null);
+    private final TestInjector mInjector = new TestInjector();
+    private BrightnessWearBedtimeModeClamper mClamper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mClamper = new BrightnessWearBedtimeModeClamper(mInjector, mTestHandler, mContext,
+                mMockClamperChangeListener, () -> BRIGHTNESS_CAP);
+        mTestHandler.flush();
+    }
+
+    @Test
+    public void testBrightnessCap() {
+        assertEquals(BRIGHTNESS_CAP, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
+    }
+
+    @Test
+    public void testBedtimeModeOn() {
+        setBedtimeModeEnabled(true);
+        assertTrue(mClamper.isActive());
+        verify(mMockClamperChangeListener).onChanged();
+    }
+
+    @Test
+    public void testBedtimeModeOff() {
+        setBedtimeModeEnabled(false);
+        assertFalse(mClamper.isActive());
+        verify(mMockClamperChangeListener).onChanged();
+    }
+
+    @Test
+    public void testType() {
+        assertEquals(BrightnessClamper.Type.BEDTIME_MODE, mClamper.getType());
+    }
+
+    @Test
+    public void testOnDisplayChanged() {
+        float newBrightnessCap = 0.61f;
+
+        mClamper.onDisplayChanged(() -> newBrightnessCap);
+        mTestHandler.flush();
+
+        assertEquals(newBrightnessCap, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
+        verify(mMockClamperChangeListener).onChanged();
+    }
+
+    private void setBedtimeModeEnabled(boolean enabled) {
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE,
+                enabled ? BEDTIME_MODE_ON : BEDTIME_MODE_OFF);
+        mInjector.notifyBedtimeModeChanged();
+        mTestHandler.flush();
+    }
+
+    private static class TestInjector extends BrightnessWearBedtimeModeClamper.Injector {
+
+        private ContentObserver mObserver;
+
+        @Override
+        void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            mObserver = observer;
+        }
+
+        private void notifyBedtimeModeChanged() {
+            if (mObserver != null) {
+                mObserver.dispatchChange(/* selfChange= */ false,
+                        Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE));
+            }
+        }
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index d085923..60a0c03 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -290,7 +290,6 @@
     };
 
     private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
-    private static final int DISPLAY_ID_2 = Display.DEFAULT_DISPLAY + 1;
     private static final int MODE_ID = 1;
     private static final float TRANSITION_POINT = 0.763f;
 
@@ -1551,12 +1550,9 @@
     public void testPeakRefreshRate_FlagEnabled() {
         when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
                 .thenReturn(true);
-        float highestRefreshRate1 = 130;
-        float highestRefreshRate2 = 132;
-        doReturn(highestRefreshRate1).when(() ->
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
-        doReturn(highestRefreshRate2).when(() ->
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID_2));
+        float highestRefreshRate = 130;
+        doReturn(highestRefreshRate).when(() ->
+                RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
         DisplayModeDirector director =
                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -1567,14 +1563,10 @@
 
         setPeakRefreshRate(Float.POSITIVE_INFINITY);
 
-        Vote vote1 = director.getVote(DISPLAY_ID,
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
                 Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
-        Vote vote2 = director.getVote(DISPLAY_ID_2,
-                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
-        assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0,
-                /* frameRateHigh= */ highestRefreshRate1);
-        assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0,
-                /* frameRateHigh= */ highestRefreshRate2);
+        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+                highestRefreshRate);
     }
 
     @Test
@@ -1592,54 +1584,19 @@
 
         setPeakRefreshRate(peakRefreshRate);
 
-        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
-        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
-                /* frameRateHigh= */ peakRefreshRate);
-    }
-
-    @Test
-    public void testPeakRefreshRate_DisplayChanged() {
-        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
-                .thenReturn(true);
-        float highestRefreshRate = 130;
-        doReturn(highestRefreshRate).when(() ->
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
-        DisplayModeDirector director =
-                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
-        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
-        Sensor lightSensor = createLightSensor();
-        SensorManager sensorManager = createMockSensorManager(lightSensor);
-        director.start(sensorManager);
-
-        setPeakRefreshRate(Float.POSITIVE_INFINITY);
-
-        Vote vote = director.getVote(DISPLAY_ID,
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
                 Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
-        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
-                /* frameRateHigh= */ highestRefreshRate);
-
-        // The highest refresh rate of the display changes
-        highestRefreshRate = 140;
-        doReturn(highestRefreshRate).when(() ->
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
-        director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
-        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
-                /* frameRateHigh= */ highestRefreshRate);
+        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+                peakRefreshRate);
     }
 
     @Test
     public void testMinRefreshRate_FlagEnabled() {
         when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
                 .thenReturn(true);
-        float highestRefreshRate1 = 130;
-        float highestRefreshRate2 = 132;
-        doReturn(highestRefreshRate1).when(() ->
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
-        doReturn(highestRefreshRate2).when(() ->
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID_2));
+        float highestRefreshRate = 130;
+        doReturn(highestRefreshRate).when(() ->
+                RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
         DisplayModeDirector director =
                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
         director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -1650,12 +1607,9 @@
 
         setMinRefreshRate(Float.POSITIVE_INFINITY);
 
-        Vote vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
-        Vote vote2 = director.getVote(DISPLAY_ID_2,
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
                 Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
-        assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ highestRefreshRate1,
-                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
-        assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ highestRefreshRate2,
+        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
                 /* frameRateHigh= */ Float.POSITIVE_INFINITY);
     }
 
@@ -1674,44 +1628,13 @@
 
         setMinRefreshRate(minRefreshRate);
 
-        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+                Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
         assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate,
                 /* frameRateHigh= */ Float.POSITIVE_INFINITY);
     }
 
     @Test
-    public void testMinRefreshRate_DisplayChanged() {
-        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
-                .thenReturn(true);
-        float highestRefreshRate = 130;
-        doReturn(highestRefreshRate).when(() ->
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
-        DisplayModeDirector director =
-                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
-        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
-        Sensor lightSensor = createLightSensor();
-        SensorManager sensorManager = createMockSensorManager(lightSensor);
-        director.start(sensorManager);
-
-        setMinRefreshRate(Float.POSITIVE_INFINITY);
-
-        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
-        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
-                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
-
-        // The highest refresh rate of the display changes
-        highestRefreshRate = 140;
-        doReturn(highestRefreshRate).when(() ->
-                RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
-        director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);
-
-        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
-        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
-                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
-    }
-
-    @Test
     public void testSensorRegistration() {
         // First, configure brightness zones or DMD won't register for sensor data.
         final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -3406,7 +3329,7 @@
     public static class FakesInjector implements DisplayModeDirector.Injector {
         private final FakeDeviceConfig mDeviceConfig;
         private final DisplayInfo mDisplayInfo;
-        private final Map<Integer, Display> mDisplays;
+        private final Display mDisplay;
         private boolean mDisplayInfoValid = true;
         private final DisplayManagerInternal mDisplayManagerInternal;
         private final StatusBarManagerInternal mStatusBarManagerInternal;
@@ -3427,8 +3350,7 @@
             mDisplayInfo.defaultModeId = MODE_ID;
             mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
                     800, 600, /* refreshRate= */ 60)};
-            mDisplays = Map.of(DISPLAY_ID, createDisplay(DISPLAY_ID),
-                    DISPLAY_ID_2, createDisplay(DISPLAY_ID_2));
+            mDisplay = createDisplay(DISPLAY_ID);
             mDisplayManagerInternal = displayManagerInternal;
             mStatusBarManagerInternal = statusBarManagerInternal;
             mSensorManagerInternal = sensorManagerInternal;
@@ -3459,12 +3381,12 @@
 
         @Override
         public Display getDisplay(int displayId) {
-            return mDisplays.get(displayId);
+            return mDisplay;
         }
 
         @Override
         public Display[] getDisplays() {
-            return mDisplays.values().toArray(new Display[0]);
+            return new Display[] { mDisplay };
         }
 
         @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index be29163..cbc8538 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -46,6 +46,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -61,6 +62,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
 import android.app.IActivityManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.Sensor;
@@ -128,6 +130,8 @@
     @Mock
     private ConnectivityManager mConnectivityManager;
     @Mock
+    private ContentResolver mContentResolver;
+    @Mock
     private IActivityManager mIActivityManager;
     @Mock
     private LocationManager mLocationManager;
@@ -332,7 +336,7 @@
                         anyString(), any(Executor.class),
                         any(DeviceConfig.OnPropertiesChangedListener.class)));
         doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock
-                -> mock(DeviceConfig.Properties.class))
+                -> new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_DEVICE_IDLE).build())
                 .when(() -> DeviceConfig.getProperties(
                         anyString(), ArgumentMatchers.<String>any()));
         when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
@@ -347,6 +351,7 @@
         mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
         mAnyMotionDetector = new AnyMotionDetectorForTest();
         mInjector = new InjectorForTest(getContext());
+        doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
 
         doReturn(mWearModeManagerInternal)
                 .when(() -> LocalServices.getService(WearModeManagerInternal.class));
@@ -366,7 +371,8 @@
         mDeviceIdleController.setLightEnabledForTest(true);
 
         // Get the same Constants object that mDeviceIdleController got.
-        mConstants = mInjector.getConstants(mDeviceIdleController);
+        mConstants = mInjector.getConstants(mDeviceIdleController,
+                mInjector.getHandler(mDeviceIdleController), mContentResolver);
 
         final ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
                 ArgumentCaptor.forClass(TelephonyCallback.class);
@@ -2164,9 +2170,8 @@
     public void testStationaryDetection_QuickDozeOff() {
         setQuickDozeEnabled(false);
         enterDeepState(STATE_IDLE);
-        // Regular progression through states, so time should have increased appropriately.
-        mInjector.nowElapsed += mConstants.IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.SENSING_TIMEOUT
-                + mConstants.LOCATING_TIMEOUT;
+        // Indicate that enough time has passed for the device to be considered stationary.
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT;
 
         StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
new file mode 100644
index 0000000..c5e6824
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2023 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.location.gnss;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.location.GnssCapabilities;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.flags.Flags;
+import android.location.provider.ProviderRequest;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.location.gnss.hal.FakeGnssHal;
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.injector.Injector;
+import com.android.server.location.injector.TestInjector;
+import com.android.server.timedetector.TimeDetectorInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+@Presubmit
+@androidx.test.filters.SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GnssLocationProviderTest {
+
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .setStrictness(Strictness.WARN)
+            .mockStatic(Settings.Global.class)
+            .build();
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    private @Mock Context mContext;
+    private @Mock LocationManagerInternal mLocationManagerInternal;
+    private @Mock LocationManager mLocationManager;
+    private @Mock TimeDetectorInternal mTimeDetectorInternal;
+    private @Mock GnssConfiguration mMockConfiguration;
+    private @Mock GnssMetrics mGnssMetrics;
+    private @Mock PowerManager mPowerManager;
+    private @Mock TelephonyManager mTelephonyManager;
+    private @Mock AppOpsManager mAppOpsManager;
+    private @Mock AlarmManager mAlarmManager;
+    private @Mock PowerManager.WakeLock mWakeLock;
+    private @Mock ContentResolver mContentResolver;
+    private @Mock UserManager mUserManager;
+    private @Mock UserHandle mUserHandle;
+    private Set<UserHandle> mUserHandleSet = new HashSet<>();
+
+    private GnssNative mGnssNative;
+
+    private GnssLocationProvider mTestProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn("mypackage").when(mContext).getPackageName();
+        doReturn("attribution").when(mContext).getAttributionTag();
+        doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
+        doReturn(mPowerManager).when(mContext).getSystemService("power");
+        doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+        doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+        doReturn(mAlarmManager).when(mContext).getSystemService(Context.ALARM_SERVICE);
+        doReturn(mLocationManager).when(mContext).getSystemService(LocationManager.class);
+        doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
+        mUserHandleSet.add(mUserHandle);
+        doReturn(true).when(mLocationManager).isLocationEnabledForUser(eq(mUserHandle));
+        doReturn(mUserHandleSet).when(mUserManager).getVisibleUsers();
+        doReturn(mContentResolver).when(mContext).getContentResolver();
+        doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
+        LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
+        LocalServices.addService(TimeDetectorInternal.class, mTimeDetectorInternal);
+        FakeGnssHal fakeGnssHal = new FakeGnssHal();
+        GnssNative.setGnssHalForTest(fakeGnssHal);
+        Injector injector = new TestInjector(mContext);
+        mGnssNative = spy(Objects.requireNonNull(
+                GnssNative.create(injector, mMockConfiguration)));
+        doReturn(true).when(mGnssNative).init();
+        GnssCapabilities gnssCapabilities = new GnssCapabilities.Builder().setHasScheduling(
+                true).build();
+        doReturn(gnssCapabilities).when(mGnssNative).getCapabilities();
+
+        mTestProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
+        mGnssNative.register();
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(LocationManagerInternal.class);
+        LocalServices.removeServiceForTest(TimeDetectorInternal.class);
+    }
+
+    @Test
+    public void testStartNavigating() {
+        ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+                0).build();
+
+        mTestProvider.onSetRequest(providerRequest);
+        verify(mGnssNative).start();
+    }
+
+    @Test
+    public void testUpdateRequirements_sameRequest() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
+        ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+                0).build();
+
+        mTestProvider.onSetRequest(providerRequest);
+        verify(mGnssNative).start();
+
+        // set the same request
+        mTestProvider.onSetRequest(providerRequest);
+        verify(mGnssNative, never()).stop();
+        verify(mGnssNative, times(1)).start();
+    }
+
+    @Test
+    public void testUpdateRequirements_differentRequest() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
+        ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+                0).build();
+
+        mTestProvider.onSetRequest(providerRequest);
+        verify(mGnssNative).start();
+
+        // set a different request
+        providerRequest = new ProviderRequest.Builder().setIntervalMillis(2000).build();
+        mTestProvider.onSetRequest(providerRequest);
+        verify(mGnssNative).stop();
+        verify(mGnssNative, times(2)).start();
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index 931b38d..f8fe97e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -22,12 +22,14 @@
 import android.content.pm.PackageManager.PERMISSION_GRANTED
 import android.content.pm.UserInfo
 import android.os.Build
+import android.os.UserHandle
 import android.os.UserHandle.USER_SYSTEM
 import android.util.Log
 import com.android.server.testutils.any
 import com.android.server.testutils.spy
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertFalse
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -177,4 +179,13 @@
 
         assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_INTERNAL_ERROR)
     }
+
+    @Test
+    fun deletePackageLIFWithNonExistantPackage_isFalse() {
+        val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+                                      mock(BroadcastHelper::class.java))
+        val result = dph.deletePackageLIF("a.nonexistent.package", UserHandle.of(USER_SYSTEM), true,
+                                          intArrayOf(0), 0, PackageRemovedInfo(), true)
+        assertFalse(result)
+    }
 }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 7148b16..2e0ba00 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -82,6 +82,15 @@
                 .isEqualTo(20 * MINUTE_IN_MS);
         assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
                 .isEqualTo(40 * MINUTE_IN_MS);
+        assertThat(uidBatteryConsumer
+                .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND))
+                .isEqualTo(20 * MINUTE_IN_MS);
+        assertThat(uidBatteryConsumer
+                .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND))
+                .isEqualTo(20 * MINUTE_IN_MS);
+        assertThat(uidBatteryConsumer
+                .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+                .isEqualTo(20 * MINUTE_IN_MS);
         assertThat(uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO))
                 .isWithin(PRECISION).of(2.0);
         assertThat(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 07c486c..079ea2c7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -193,14 +193,14 @@
         for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
             if (uidBatteryConsumer.getUid() == APP_UID1) {
                 assertUidBatteryConsumer(uidBatteryConsumer, 2124, null,
-                        5321, 7432, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 745,
+                        5321, 6900, 532, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 745,
                         POWER_MODEL_UNDEFINED,
                         956, 1167, 1478,
                         true, 3554, 3776, 3998, 444, 3554, 15542, 3776, 17762, 3998, 19982,
                         444, 1110);
             } else if (uidBatteryConsumer.getUid() == APP_UID2) {
                 assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar",
-                        1111, 2222, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
+                        1111, 2220, 2, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
                         BatteryConsumer.POWER_MODEL_POWER_PROFILE,
                         555, 666, 777,
                         true, 1777, 1888, 1999, 321, 1777, 7771, 1888, 8881, 1999, 9991,
@@ -269,7 +269,7 @@
                         .setStatsEndTimestamp(3000);
 
         addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo",
-                1000, 2000,
+                1000, 1500, 500,
                 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
                 BatteryConsumer.POWER_MODEL_POWER_PROFILE, 500, 600, 800,
                 1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
@@ -312,13 +312,13 @@
                 .setStatsEndTimestamp(5000);
 
         addUidBatteryConsumer(builder, batteryStats, APP_UID1, null,
-                4321, 5432,
+                4321, 5400, 32,
                 123, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_ENERGY_CONSUMPTION,
                 456, 567, 678,
                 1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
 
         addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar",
-                1111, 2222,
+                1111, 2220, 2,
                 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
                 BatteryConsumer.POWER_MODEL_POWER_PROFILE, 555, 666, 777,
                 1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
@@ -338,7 +338,8 @@
 
     private void addUidBatteryConsumer(BatteryUsageStats.Builder builder,
             MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain,
-            int timeInStateForeground, int timeInStateBackground, double screenPower,
+            int timeInProcessStateForeground, int timeInProcessStateBackground,
+            int timeInProcessStateForegroundService, double screenPower,
             int screenPowerModel, double cpuPower, int cpuPowerModel, double customComponentPower,
             int cpuDuration, int customComponentDuration, double cpuPowerForeground,
             int cpuDurationForeground, double cpuPowerBackground, int cpuDurationBackground,
@@ -348,8 +349,10 @@
                 builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
         uidBuilder
                 .setPackageWithHighestDrain(packageWithHighestDrain)
-                .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, timeInStateForeground)
-                .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, timeInStateBackground)
+                .setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND, timeInProcessStateForeground)
+                .setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, timeInProcessStateBackground)
+                .setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
+                        timeInProcessStateForegroundService)
                 .setConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
                 .setConsumedPower(
@@ -446,7 +449,7 @@
         for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
             if (uidBatteryConsumer.getUid() == APP_UID1) {
                 assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo",
-                        1000, 2000, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
+                        1000, 1500, 500, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
                         BatteryConsumer.POWER_MODEL_POWER_PROFILE,
                         500, 600, 800,
                         true, 1777, 1888, 1999, 123, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
@@ -496,8 +499,9 @@
     }
 
     private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer,
-            double consumedPower, String packageWithHighestDrain, int timeInStateForeground,
-            int timeInStateBackground, int screenPower, int screenPowerModel, double cpuPower,
+            double consumedPower, String packageWithHighestDrain, int timeInProcessStateForeground,
+            int timeInProcessStateBackground, int timeInProcessStateForegroundService,
+            int screenPower, int screenPowerModel, double cpuPower,
             int cpuPowerModel, double customComponentPower, int cpuDuration,
             int customComponentDuration, boolean processStateDataIncluded,
             double totalPowerForeground, double totalPowerBackground, double totalPowerFgs,
@@ -509,9 +513,16 @@
         assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo(
                 packageWithHighestDrain);
         assertThat(uidBatteryConsumer.getTimeInStateMs(
-                UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInStateForeground);
+                UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInProcessStateForeground);
         assertThat(uidBatteryConsumer.getTimeInStateMs(
-                UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(timeInStateBackground);
+                UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(
+                        timeInProcessStateBackground + timeInProcessStateForegroundService);
+        assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+                PROCESS_STATE_FOREGROUND)).isEqualTo(timeInProcessStateForeground);
+        assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+                PROCESS_STATE_BACKGROUND)).isEqualTo(timeInProcessStateBackground);
+        assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+                PROCESS_STATE_FOREGROUND_SERVICE)).isEqualTo(timeInProcessStateForegroundService);
         assertThat(uidBatteryConsumer.getConsumedPower(
                 BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower);
         assertThat(uidBatteryConsumer.getPowerModel(
diff --git a/services/tests/servicestests/src/com/android/server/SecurityStateTest.java b/services/tests/servicestests/src/com/android/server/SecurityStateTest.java
new file mode 100644
index 0000000..fc91e47
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/SecurityStateTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 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;
+
+import static android.os.SecurityStateManager.KEY_KERNEL_VERSION;
+import static android.os.SecurityStateManager.KEY_SYSTEM_SPL;
+import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
+
+import static com.android.server.SecurityStateManagerService.KERNEL_RELEASE_PATTERN;
+import static com.android.server.SecurityStateManagerService.VENDOR_SECURITY_PATCH_PROPERTY_KEY;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.regex.Matcher;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SecurityStateTest {
+    @Rule
+    public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    private Context mMockContext;
+
+    @Mock
+    private PackageManager mMockPackageManager;
+
+    @Mock
+    private Resources mMockResources;
+
+    private static final String DEFAULT_MODULE_METADATA_PROVIDER = "com.android.modulemetadata";
+    private static final String DEFAULT_MODULE_METADATA_PROVIDER_VERSION = "2023-12-01";
+    private static final String DEFAULT_SECURITY_STATE_PACKAGE = "com.google.android.gms";
+    private static final String DEFAULT_SECURITY_STATE_PACKAGE_VERSION = "2023-12-05";
+    private static final String[] SECURITY_STATE_PACKAGES =
+            new String[]{DEFAULT_SECURITY_STATE_PACKAGE};
+
+    @Before
+    public void setUp() throws Exception {
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockContext.getString(R.string.config_defaultModuleMetadataProvider))
+                .thenReturn(DEFAULT_MODULE_METADATA_PROVIDER);
+        when(mMockPackageManager.getPackageInfo(anyString(), anyInt()))
+                .thenReturn(new PackageInfo());
+        PackageInfo moduleMetadataPackageInfo = new PackageInfo();
+        moduleMetadataPackageInfo.versionName = DEFAULT_MODULE_METADATA_PROVIDER_VERSION;
+        when(mMockPackageManager.getPackageInfo(DEFAULT_MODULE_METADATA_PROVIDER, 0))
+                .thenReturn(moduleMetadataPackageInfo);
+        PackageInfo securityStatePackageInfo = new PackageInfo();
+        securityStatePackageInfo.versionName = DEFAULT_SECURITY_STATE_PACKAGE_VERSION;
+        when(mMockPackageManager.getPackageInfo(DEFAULT_SECURITY_STATE_PACKAGE, 0))
+                .thenReturn(securityStatePackageInfo);
+        when(mMockResources.getStringArray(R.array.config_securityStatePackages))
+                .thenReturn(SECURITY_STATE_PACKAGES);
+    }
+
+    @Test
+    public void testGetGlobalSecurityState_returnsBundle() {
+        SecurityStateManagerService securityState = new SecurityStateManagerService(mMockContext);
+
+        Bundle bundle = securityState.getGlobalSecurityState();
+
+        assertEquals(bundle.getString(KEY_SYSTEM_SPL), Build.VERSION.SECURITY_PATCH);
+        assertEquals(bundle.getString(KEY_VENDOR_SPL),
+                SystemProperties.get(VENDOR_SECURITY_PATCH_PROPERTY_KEY, ""));
+        Matcher matcher = KERNEL_RELEASE_PATTERN.matcher(VintfRuntimeInfo.getKernelVersion());
+        String kernelVersion = "";
+        if (matcher.matches()) {
+            kernelVersion = matcher.group(1);
+        }
+        assertEquals(bundle.getString(KEY_KERNEL_VERSION), kernelVersion);
+        assertEquals(bundle.getString(DEFAULT_MODULE_METADATA_PROVIDER),
+                DEFAULT_MODULE_METADATA_PROVIDER_VERSION);
+        assertEquals(bundle.getString(DEFAULT_SECURITY_STATE_PACKAGE),
+                DEFAULT_SECURITY_STATE_PACKAGE_VERSION);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/WatchdogTest.java b/services/tests/servicestests/src/com/android/server/WatchdogTest.java
new file mode 100644
index 0000000..34ac47e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/WatchdogTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2009 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.Handler;
+import android.os.SimpleClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.Watchdog.HandlerChecker;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.ZoneOffset;
+
+/** Test class for {@link Watchdog}. */
+@RunWith(AndroidJUnit4.class)
+public class WatchdogTest {
+    private static final int TIMEOUT_MS = 10;
+
+    private TestClock mClock;
+    private Handler mHandler;
+    private HandlerChecker mChecker;
+
+    @Before
+    public void setUp() {
+        mClock = new TestClock();
+        mHandler = mock(Handler.class);
+        mChecker =
+                new HandlerChecker(mHandler, "monitor thread", new Object(), mClock) {
+                    @Override
+                    public boolean isHandlerPolling() {
+                        return false;
+                    }
+                };
+    }
+
+    @Test
+    public void checkerPausedUntilResume() {
+        Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+        mChecker.addMonitorLocked(monitor);
+
+        mChecker.pauseLocked("pausing");
+        mChecker.scheduleCheckLocked(TIMEOUT_MS);
+        verifyNoMoreInteractions(mHandler);
+        assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+        mChecker.resumeLocked("resuming");
+        mChecker.scheduleCheckLocked(10);
+        assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+    }
+
+    @Test
+    public void checkerPausedUntilDeadline() {
+        Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+        mChecker.addMonitorLocked(monitor);
+
+        mChecker.pauseForLocked(10, "pausing");
+        mChecker.scheduleCheckLocked(TIMEOUT_MS);
+        verifyNoMoreInteractions(mHandler);
+        assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+        mClock.advanceBy(5);
+        verifyNoMoreInteractions(mHandler);
+        assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+        // Above the 10s timeout. Watchdog should not be paused anymore.
+        mClock.advanceBy(6);
+        mChecker.scheduleCheckLocked(TIMEOUT_MS);
+        assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+    }
+
+    @Test
+    public void checkerPausedDuringScheduledRun() {
+        Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+        mChecker.addMonitorLocked(monitor);
+
+        mChecker.scheduleCheckLocked(TIMEOUT_MS);
+        mClock.advanceBy(5);
+        mChecker.pauseForLocked(10, "pausing");
+        verifyNoMoreInteractions(mHandler);
+        assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+        // Above the 10s timeout. Watchdog should not be paused anymore.
+        mClock.advanceBy(11);
+        mChecker.scheduleCheckLocked(TIMEOUT_MS);
+        assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+    }
+
+    @Test
+    public void blockedThread() {
+        mChecker.scheduleCheckLocked(TIMEOUT_MS);
+        assertEquals(mChecker.getCompletionStateLocked(), Watchdog.WAITING);
+
+        mClock.advanceBy(6);
+        assertEquals(Watchdog.WAITED_UNTIL_PRE_WATCHDOG, mChecker.getCompletionStateLocked());
+
+        // Above the 10s timeout.
+        mClock.advanceBy(6);
+        assertEquals(Watchdog.OVERDUE, mChecker.getCompletionStateLocked());
+    }
+
+    @Test
+    public void checkNothingBlocked() {
+        Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+        mChecker.addMonitorLocked(monitor);
+
+        mChecker.scheduleCheckLocked(TIMEOUT_MS);
+        // scheduleCheckLocked calls #postAtFrontOfQueue which will call mChecker.run().
+        mChecker.run();
+        assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+        verify(monitor).monitor();
+    }
+
+    private static class TestClock extends SimpleClock {
+        long mNowMillis = 1;
+
+        TestClock() {
+            super(ZoneOffset.UTC);
+        }
+
+        @Override
+        public long millis() {
+            return mNowMillis;
+        }
+
+        public void advanceBy(long millis) {
+            mNowMillis += millis;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 82efdd3..800350a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -85,9 +85,9 @@
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
 import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
 import com.android.server.accessibility.magnification.MagnificationController;
 import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
 import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -156,7 +156,7 @@
     @Mock private UserManagerInternal mMockUserManagerInternal;
     @Mock private IBinder mMockBinder;
     @Mock private IAccessibilityServiceClient mMockServiceClient;
-    @Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
+    @Mock private MagnificationConnectionManager mMockMagnificationConnectionManager;
     @Mock private MagnificationController mMockMagnificationController;
     @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
     @Mock private ProxyManager mProxyManager;
@@ -180,8 +180,8 @@
                 UserManagerInternal.class, mMockUserManagerInternal);
         mInputFilter = Mockito.mock(FakeInputFilter.class);
 
-        when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
-                mMockWindowMagnificationMgr);
+        when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
+                mMockMagnificationConnectionManager);
         when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
                 mMockFullScreenMagnificationController);
         when(mMockMagnificationController.supportWindowMagnification()).thenReturn(true);
@@ -530,7 +530,7 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr).requestConnection(true);
+        verify(mMockMagnificationConnectionManager).requestConnection(true);
     }
 
     @SmallTest
@@ -547,7 +547,7 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr).requestConnection(true);
+        verify(mMockMagnificationConnectionManager).requestConnection(true);
     }
 
     @SmallTest
@@ -565,7 +565,7 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr).requestConnection(false);
+        verify(mMockMagnificationConnectionManager).requestConnection(false);
     }
 
     @SmallTest
@@ -583,7 +583,7 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr).requestConnection(true);
+        verify(mMockMagnificationConnectionManager).requestConnection(true);
     }
 
     @SmallTest
@@ -602,7 +602,7 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr).requestConnection(false);
+        verify(mMockMagnificationConnectionManager).requestConnection(false);
     }
 
     @SmallTest
@@ -616,7 +616,7 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr).requestConnection(true);
+        verify(mMockMagnificationConnectionManager).requestConnection(true);
     }
 
     @SmallTest
@@ -630,7 +630,8 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+        verify(mMockMagnificationConnectionManager, atLeastOnce())
+                .removeMagnificationButton(anyInt());
     }
 
     @SmallTest
@@ -644,7 +645,7 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+        verify(mMockMagnificationConnectionManager, never()).removeMagnificationButton(anyInt());
     }
 
     @SmallTest
@@ -659,7 +660,8 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+        verify(mMockMagnificationConnectionManager, atLeastOnce())
+                .removeMagnificationButton(anyInt());
     }
 
     @SmallTest
@@ -674,7 +676,7 @@
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
 
-        verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+        verify(mMockMagnificationConnectionManager, never()).removeMagnificationButton(anyInt());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index a02807f..7829fcc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -39,9 +39,9 @@
 import android.graphics.Region;
 
 import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
 import com.android.server.accessibility.magnification.MagnificationController;
 import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -66,21 +66,21 @@
     @Mock
     private FullScreenMagnificationController mMockFullScreenMagnificationController;
     @Mock
-    private WindowMagnificationManager mMockWindowMagnificationManager;
+    private MagnificationConnectionManager mMockMagnificationConnectionManager;
     FullScreenMagnificationControllerStub mFullScreenMagnificationControllerStub;
-    WindowMagnificationManagerStub mWindowMagnificationManagerStub;
+    MagnificationManagerStub mMagnificationManagerStub;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mFullScreenMagnificationControllerStub = new FullScreenMagnificationControllerStub(
                 mMockFullScreenMagnificationController);
-        mWindowMagnificationManagerStub = new WindowMagnificationManagerStub(
-                mMockWindowMagnificationManager);
+        mMagnificationManagerStub = new MagnificationManagerStub(
+                mMockMagnificationConnectionManager);
         when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
                 mMockFullScreenMagnificationController);
-        when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
-                mMockWindowMagnificationManager);
+        when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
+                mMockMagnificationConnectionManager);
         mMagnificationProcessor = new MagnificationProcessor(mMockMagnificationController);
     }
 
@@ -194,7 +194,7 @@
         doAnswer((invocation) -> {
             ((Region) invocation.getArguments()[1]).set(region);
             return null;
-        }).when(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+        }).when(mMockMagnificationConnectionManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
                 any());
 
         final Region result = new Region();
@@ -286,7 +286,7 @@
 
         mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
 
-        verify(mMockWindowMagnificationManager).disableWindowMagnification(TEST_DISPLAY, false,
+        verify(mMockMagnificationConnectionManager).disableWindowMagnification(TEST_DISPLAY, false,
                 null);
     }
 
@@ -296,7 +296,7 @@
         mMagnificationProcessor.resetAllIfNeeded(connectionId);
 
         verify(mMockFullScreenMagnificationController).resetAllIfNeeded(eq(connectionId));
-        verify(mMockWindowMagnificationManager).resetAllIfNeeded(eq(connectionId));
+        verify(mMockMagnificationConnectionManager).resetAllIfNeeded(eq(connectionId));
     }
 
     @Test
@@ -450,7 +450,7 @@
                 .setActivated(false).build();
         mMagnificationProcessor.setMagnificationConfig(TEST_DISPLAY, config, false, SERVICE_ID);
 
-        verify(mMockWindowMagnificationManager)
+        verify(mMockMagnificationConnectionManager)
                 .disableWindowMagnification(eq(TEST_DISPLAY), anyBoolean());
     }
 
@@ -481,11 +481,11 @@
             mFullScreenMagnificationControllerStub.resetAndStubMethods();
             mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(),
                     config.getCenterX(), config.getCenterY(), false, SERVICE_ID);
-            mWindowMagnificationManagerStub.deactivateIfNeed();
+            mMagnificationManagerStub.deactivateIfNeed();
         } else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) {
-            mWindowMagnificationManagerStub.resetAndStubMethods();
-            mMockWindowMagnificationManager.enableWindowMagnification(displayId, config.getScale(),
-                    config.getCenterX(), config.getCenterY());
+            mMagnificationManagerStub.resetAndStubMethods();
+            mMockMagnificationConnectionManager.enableWindowMagnification(
+                    displayId, config.getScale(), config.getCenterX(), config.getCenterY());
             mFullScreenMagnificationControllerStub.deactivateIfNeed();
         }
     }
@@ -568,26 +568,26 @@
         }
     }
 
-    private static class WindowMagnificationManagerStub {
-        private final WindowMagnificationManager mWindowMagnificationManager;
+    private static class MagnificationManagerStub {
+        private final MagnificationConnectionManager mMagnificationConnectionManager;
         private float mScale = 1.0f;
         private float mCenterX = 0;
         private float mCenterY = 0;
         private boolean mIsEnabled = false;
 
-        WindowMagnificationManagerStub(
-                WindowMagnificationManager windowMagnificationManager) {
-            mWindowMagnificationManager = windowMagnificationManager;
+        MagnificationManagerStub(
+                MagnificationConnectionManager magnificationConnectionManager) {
+            mMagnificationConnectionManager = magnificationConnectionManager;
         }
 
         private void stubMethods() {
-            doAnswer(invocation -> mScale).when(mWindowMagnificationManager).getScale(
+            doAnswer(invocation -> mScale).when(mMagnificationConnectionManager).getScale(
                     TEST_DISPLAY);
-            doAnswer(invocation -> mCenterX).when(mWindowMagnificationManager).getCenterX(
+            doAnswer(invocation -> mCenterX).when(mMagnificationConnectionManager).getCenterX(
                     TEST_DISPLAY);
-            doAnswer(invocation -> mCenterY).when(mWindowMagnificationManager).getCenterY(
+            doAnswer(invocation -> mCenterY).when(mMagnificationConnectionManager).getCenterY(
                     TEST_DISPLAY);
-            doAnswer(invocation -> mIsEnabled).when(mWindowMagnificationManager)
+            doAnswer(invocation -> mIsEnabled).when(mMagnificationConnectionManager)
                     .isWindowMagnifierEnabled(TEST_DISPLAY);
 
             Answer enableWindowMagnificationStubAnswer = invocation -> {
@@ -598,10 +598,10 @@
                 return true;
             };
             doAnswer(enableWindowMagnificationStubAnswer).when(
-                    mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+                    mMagnificationConnectionManager).enableWindowMagnification(eq(TEST_DISPLAY),
                     anyFloat(), anyFloat(), anyFloat());
             doAnswer(enableWindowMagnificationStubAnswer).when(
-                    mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+                    mMagnificationConnectionManager).enableWindowMagnification(eq(TEST_DISPLAY),
                     anyFloat(), anyFloat(), anyFloat(), any(), anyInt());
 
             Answer disableWindowMagnificationStubAnswer = invocation -> {
@@ -609,15 +609,15 @@
                 return true;
             };
             doAnswer(disableWindowMagnificationStubAnswer).when(
-                    mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY),
+                    mMagnificationConnectionManager).disableWindowMagnification(eq(TEST_DISPLAY),
                     anyBoolean());
             doAnswer(disableWindowMagnificationStubAnswer).when(
-                    mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY),
+                    mMagnificationConnectionManager).disableWindowMagnification(eq(TEST_DISPLAY),
                     anyBoolean(), any());
         }
 
         public void resetAndStubMethods() {
-            Mockito.reset(mWindowMagnificationManager);
+            Mockito.reset(mMagnificationConnectionManager);
             stubMethods();
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
new file mode 100644
index 0000000..3843e25
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2019 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.accessibility.magnification;
+
+import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
+import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY_2;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import static java.lang.Float.NaN;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.FlakyTest;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+/**
+ * Tests for WindowMagnificationManager.
+ */
+public class MagnificationConnectionManagerTest {
+
+    private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
+    private static final int SERVICE_ID = 1;
+
+    private MockWindowMagnificationConnection mMockConnection;
+    @Mock
+    private Context mContext;
+    @Mock
+    private AccessibilityTraceManager mMockTrace;
+    @Mock
+    private StatusBarManagerInternal mMockStatusBarManagerInternal;
+    @Mock
+    private MagnificationAnimationCallback mAnimationCallback;
+    @Mock
+    private MagnificationConnectionManager.Callback mMockCallback;
+    private MockContentResolver mResolver;
+    private MagnificationConnectionManager mMagnificationConnectionManager;
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+        LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+        mResolver = new MockContentResolver();
+        mMockConnection = new MockWindowMagnificationConnection();
+        mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
+                mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
+
+        when(mContext.getContentResolver()).thenReturn(mResolver);
+        stubSetConnection(false);
+
+        mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        Settings.Secure.putFloatForUser(mResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f,
+                CURRENT_USER_ID);
+    }
+
+    private void stubSetConnection(boolean needDelay) {
+        doAnswer((InvocationOnMock invocation) -> {
+            final boolean connect = (Boolean) invocation.getArguments()[0];
+            // Simulates setConnection() called by another process.
+            if (needDelay) {
+                final Context context = ApplicationProvider.getApplicationContext();
+                context.getMainThreadHandler().postDelayed(
+                        () -> {
+                            mMagnificationConnectionManager.setConnection(
+                                    connect ? mMockConnection.getConnection() : null);
+                        }, 10);
+            } else {
+                mMagnificationConnectionManager.setConnection(
+                        connect ? mMockConnection.getConnection() : null);
+            }
+            return true;
+        }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
+    }
+
+    @Test
+    public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        assertTrue(mMagnificationConnectionManager.isConnected());
+        verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+    }
+
+    @Test
+    public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        assertTrue(mMagnificationConnectionManager.isConnected());
+        verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+        verify(mMockConnection.getConnection()).setConnectionCallback(
+                any(IWindowMagnificationConnectionCallback.class));
+    }
+
+    @Test
+    public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        mMockConnection.getDeathRecipient().binderDied();
+
+        assertFalse(mMagnificationConnectionManager.isConnected());
+        verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
+                0);
+    }
+
+    /**
+     * This test simulates {@link MagnificationConnectionManager#setConnection} is called by thread
+     * A and then the former connection is called by thread B. In this situation we should keep the
+     * new connection.
+     */
+    @Test
+    public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        MockWindowMagnificationConnection secondConnection =
+                new MockWindowMagnificationConnection();
+
+        mMagnificationConnectionManager.setConnection(secondConnection.getConnection());
+        mMockConnection.getDeathRecipient().binderDied();
+
+        assertTrue(mMagnificationConnectionManager.isConnected());
+        verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
+        verify(secondConnection.asBinder(), never()).unlinkToDeath(
+                secondConnection.getDeathRecipient(), 0);
+    }
+
+    @Test
+    public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        mMagnificationConnectionManager.setConnection(null);
+
+        assertFalse(mMagnificationConnectionManager.isConnected());
+        verify(mMockConnection.getConnection()).setConnectionCallback(null);
+    }
+
+    @Test
+    public void enableWithAnimation_hasConnection_enableWindowMagnification()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
+
+        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+                eq(200f), eq(300f), eq(0f), eq(0f), notNull());
+    }
+
+    @Test
+    public void enableWithCallback_hasConnection_enableWindowMagnification()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
+                mAnimationCallback, SERVICE_ID);
+
+        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+                eq(200f), eq(300f), eq(0f), eq(0f),
+                any(IRemoteMagnificationAnimationCallback.class));
+        verify(mAnimationCallback).onResult(true);
+    }
+
+    @Test
+    public void disable_hasConnectionAndEnabled_disableWindowMagnification()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+        verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+                notNull());
+    }
+
+    @Test
+    public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false,
+                mAnimationCallback);
+
+        verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+                any(IRemoteMagnificationAnimationCallback.class));
+        verify(mAnimationCallback).onResult(true);
+    }
+
+    @Test
+    public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
+
+        assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+    }
+
+    @Test
+    public void getPersistedScale() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        assertEquals(mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY), 2.5f);
+    }
+
+    @Test
+    public void persistScale_setValue_expectedValueInProvider() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
+        mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f);
+
+        mMagnificationConnectionManager.persistScale(TEST_DISPLAY);
+
+        assertEquals(Settings.Secure.getFloatForUser(mResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
+                CURRENT_USER_ID), 2.5f);
+    }
+
+    @Test
+    public void persistScale_setValueWhenScaleIsOne_nothingChanged() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        final float persistedScale =
+                mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY);
+
+        mMagnificationConnectionManager.setScale(TEST_DISPLAY, 1.0f);
+        mMagnificationConnectionManager.persistScale(TEST_DISPLAY);
+
+        assertEquals(Settings.Secure.getFloatForUser(mResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
+                CURRENT_USER_ID), persistedScale);
+    }
+
+    @Test
+    public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
+
+        mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f);
+
+        assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY), 2.5f);
+    }
+
+    @Test
+    public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+
+        mMagnificationConnectionManager.setScale(TEST_DISPLAY, 10.0f);
+
+        assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY),
+                MagnificationScaleProvider.MAX_SCALE);
+    }
+
+    @FlakyTest(bugId = 297879435)
+    @Test
+    public void logTrackingTypingFocus_processScroll_logDuration() {
+        MagnificationConnectionManager spyMagnificationConnectionManager = spy(
+                mMagnificationConnectionManager);
+        spyMagnificationConnectionManager.enableWindowMagnification(
+                TEST_DISPLAY, 3.0f, 50f, 50f);
+        spyMagnificationConnectionManager.onImeWindowVisibilityChanged(
+                TEST_DISPLAY, /* shown */ true);
+
+        spyMagnificationConnectionManager.processScroll(TEST_DISPLAY, 10f, 10f);
+
+        verify(spyMagnificationConnectionManager).logTrackingTypingFocus(anyLong());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        final Region outRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+        mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection(), never())
+                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+    }
+
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier()
+            throws RemoteException {
+        final float distanceX = 10f;
+        final float distanceY = 10f;
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        final Region outRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+        mMagnificationConnectionManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection(), never())
+                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        final Region outRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.inset(-10, -10);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection(), never())
+                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+    }
+    @Test
+    public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        final Region outRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection(), never())
+                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        final Region outRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+                eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+                any(IRemoteMagnificationAnimationCallback.class));
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        final Region outRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection(), never())
+                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        final Region outRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+        final Rect requestedRect = outRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+                eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+                any(IRemoteMagnificationAnimationCallback.class));
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        final Region beforeRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+        final Rect requestedRect = beforeRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+        mMagnificationConnectionManager.setMagnificationFollowTypingEnabled(false);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        final Region afterRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+        assertEquals(afterRegion, beforeRegion);
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+        final Region beforeRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+        final Rect requestedRect = beforeRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        final Region afterRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+        assertEquals(afterRegion, beforeRegion);
+    }
+
+    @Test
+    public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+        mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+        mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+        final Region beforeRegion = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+        final Rect requestedRect = beforeRegion.getBounds();
+        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+        // Enabling a window magnifier again will turn on the tracking typing focus functionality.
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
+
+        mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+        verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+                eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+                any(IRemoteMagnificationAnimationCallback.class));
+    }
+
+    @Test
+    public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
+
+        mMagnificationConnectionManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
+        verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
+    }
+
+    @Test
+    public void showMagnificationButton_hasConnection_invokeConnectionMethod()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        mMagnificationConnectionManager.showMagnificationButton(TEST_DISPLAY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        verify(mMockConnection.getConnection()).showMagnificationButton(TEST_DISPLAY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+        mMagnificationConnectionManager.removeMagnificationButton(TEST_DISPLAY);
+        verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
+    }
+
+    @Test
+    public void removeMagnificationSettingsPanel_hasConnection_invokeConnectionMethod()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        mMagnificationConnectionManager.removeMagnificationSettingsPanel(TEST_DISPLAY);
+        verify(mMockConnection.getConnection()).removeMagnificationSettingsPanel(TEST_DISPLAY);
+    }
+
+    @Test
+    public void onUserMagnificationScaleChanged_hasConnection_invokeConnectionMethod()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+        final float testScale = 3f;
+        mMagnificationConnectionManager.onUserMagnificationScaleChanged(
+                CURRENT_USER_ID, TEST_DISPLAY, testScale);
+        verify(mMockConnection.getConnection()).onUserMagnificationScaleChanged(
+                eq(CURRENT_USER_ID), eq(TEST_DISPLAY), eq(testScale));
+    }
+
+    @Test
+    public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+        mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
+                new Rect(0, 0, 500, 500));
+        PointF[] pointersLocation = new PointF[2];
+        pointersLocation[0] = new PointF(600, 700);
+        pointersLocation[1] = new PointF(300, 400);
+        MotionEvent event = generatePointersDownEvent(pointersLocation);
+
+        assertEquals(mMagnificationConnectionManager.pointersInWindow(TEST_DISPLAY, event), 1);
+    }
+
+    @Test
+    public void onPerformScaleAction_magnifierEnabled_notifyAction() throws RemoteException {
+        final float newScale = 4.0f;
+        final boolean updatePersistence = true;
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+        mMockConnection.getConnectionCallback().onPerformScaleAction(
+                TEST_DISPLAY, newScale, updatePersistence);
+
+        verify(mMockCallback).onPerformScaleAction(
+                eq(TEST_DISPLAY), eq(newScale), eq(updatePersistence));
+    }
+
+    @Test
+    public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
+            throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+        mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
+
+        verify(mMockCallback).onAccessibilityActionPerformed(eq(TEST_DISPLAY));
+    }
+
+    @Test
+    public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+        mMockConnection.getDeathRecipient().binderDied();
+
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+    }
+
+    @Test
+    public void
+            requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
+            throws RemoteException {
+        assertTrue(mMagnificationConnectionManager.requestConnection(true));
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+        assertTrue(mMagnificationConnectionManager.requestConnection(false));
+
+        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+        verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
+    }
+
+    @Test
+    public void requestConnection_requestWindowMagnificationConnection() throws RemoteException {
+        assertTrue(mMagnificationConnectionManager.requestConnection(true));
+        verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true);
+    }
+
+    @Test
+    public void isConnected_requestConnection_expectedValue() throws RemoteException {
+        mMagnificationConnectionManager.requestConnection(true);
+        assertTrue(mMagnificationConnectionManager.isConnected());
+
+        mMagnificationConnectionManager.requestConnection(false);
+        assertFalse(mMagnificationConnectionManager.isConnected());
+    }
+
+    @Test
+    public void requestConnection_registerAndUnregisterBroadcastReceiver() {
+        assertTrue(mMagnificationConnectionManager.requestConnection(true));
+        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
+
+        assertTrue(mMagnificationConnectionManager.requestConnection(false));
+        verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+    }
+
+    @Test
+    public void requestConnectionToNull_expectedGetterResults() {
+        mMagnificationConnectionManager.requestConnection(true);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
+
+        mMagnificationConnectionManager.requestConnection(false);
+
+        assertEquals(1f, mMagnificationConnectionManager.getScale(TEST_DISPLAY), 0);
+        assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY)));
+        assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY)));
+        final Region bounds = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+        assertTrue(bounds.isEmpty());
+    }
+
+    @Test
+    public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected()
+            throws RemoteException {
+        stubSetConnection(true);
+        mMagnificationConnectionManager.requestConnection(true);
+
+        assertTrue(mMagnificationConnectionManager.enableWindowMagnification(
+                TEST_DISPLAY, 3f, 1, 1));
+
+        // Invoke enableWindowMagnification if the connection is connected.
+        verify(mMockConnection.getConnection()).enableWindowMagnification(
+                eq(TEST_DISPLAY), eq(3f),
+                eq(1f), eq(1f), eq(0f), eq(0f), notNull());
+    }
+
+    @Test
+    public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+                100f, 200f, null,
+                MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+                100f, 200f, null,
+                MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+        assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+
+        mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID);
+
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+    }
+
+    @Test
+    public void resetAllMagnification_enabledByDifferentId_windowMagnifierDisabled() {
+        final int serviceId2 = SERVICE_ID + 1;
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+                100f, 200f, null,
+                MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+                100f, 200f, null,
+                MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, serviceId2);
+        assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+
+        mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID);
+
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+    }
+
+    @Test
+    public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
+            throws RemoteException {
+        mMagnificationConnectionManager.requestConnection(true);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+
+        mMagnificationConnectionManager.mScreenStateReceiver.onReceive(mContext,
+                new Intent(Intent.ACTION_SCREEN_OFF));
+
+        verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
+        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+    }
+
+    @Test
+    public void centerGetter_enabledOnTestDisplay_expectedValues() {
+        mMagnificationConnectionManager.requestConnection(true);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+        assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+        assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+    }
+
+    @Test
+    public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
+            throws RemoteException {
+        mMagnificationConnectionManager.requestConnection(true);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+                100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
+
+        assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+        assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+
+        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+                eq(100f), eq(200f), eq(0f), eq(0f), notNull());
+    }
+
+    @Test
+    public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
+            throws RemoteException {
+        mMagnificationConnectionManager.requestConnection(true);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+                100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
+
+        assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+        assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+
+        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+                eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
+    }
+
+    @Test
+    public void magnifierGetters_disabled_expectedValues() {
+        mMagnificationConnectionManager.requestConnection(true);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+                100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
+
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+        assertEquals(1f, mMagnificationConnectionManager.getScale(TEST_DISPLAY), 0);
+        assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY)));
+        assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY)));
+        final Region bounds = new Region();
+        mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+        assertTrue(bounds.isEmpty());
+    }
+
+    @Test
+    public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
+        mMagnificationConnectionManager.requestConnection(true);
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+        mMagnificationConnectionManager.onDisplayRemoved(TEST_DISPLAY);
+
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+    }
+
+    @Test
+    public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+        verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, true);
+    }
+
+    @Test
+    public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+        verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, false);
+
+        Mockito.reset(mMockCallback);
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+        verify(mMockCallback, never()).onWindowMagnificationActivationState(eq(TEST_DISPLAY),
+                anyBoolean());
+    }
+
+    private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
+        final int len = pointersLocation.length;
+
+        final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[len];
+        for (int i = 0; i < len; i++) {
+            MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
+            pointerProperty.id = i;
+            pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER;
+            pp[i] = pointerProperty;
+        }
+
+        final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[len];
+        for (int i = 0; i < len; i++) {
+            MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
+            pointerCoord.x = pointersLocation[i].x;
+            pointerCoord.y = pointersLocation[i].y;
+            pc[i] = pointerCoord;
+        }
+
+        return MotionEvent.obtain(
+                /* downTime */ SystemClock.uptimeMillis(),
+                /* eventTime */ SystemClock.uptimeMillis(),
+                /* action */ MotionEvent.ACTION_POINTER_DOWN,
+                /* pointerCount */ pc.length,
+                /* pointerProperties */ pp,
+                /* pointerCoords */ pc,
+                /* metaState */ 0,
+                /* buttonState */ 0,
+                /* xPrecision */ 1.0f,
+                /* yPrecision */ 1.0f,
+                /* deviceId */ 0,
+                /* edgeFlags */ 0,
+                /* source */ InputDevice.SOURCE_TOUCHSCREEN,
+                /* flags */ 0);
+    }
+
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
index cfd0289..8f85f11 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
@@ -39,7 +39,7 @@
 /**
  * Tests for MagnificationConnectionWrapper. We don't test {@code
  * MagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
- * {@link WindowMagnificationManagerTest}.
+ * {@link MagnificationConnectionManagerTest}.
  */
 public class MagnificationConnectionWrapperTest {
 
@@ -73,9 +73,9 @@
     }
 
     @Test
-    public void setScale() throws RemoteException {
-        mConnectionWrapper.setScale(TEST_DISPLAY, 3.0f);
-        verify(mConnection).setScale(TEST_DISPLAY, 3.0f);
+    public void setScaleForWindowMagnification() throws RemoteException {
+        mConnectionWrapper.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
+        verify(mConnection).setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index d4c6fad..e8cdf35 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -131,7 +131,7 @@
     private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
 
     private MockWindowMagnificationConnection mMockConnection;
-    private WindowMagnificationManager mWindowMagnificationManager;
+    private MagnificationConnectionManager mMagnificationConnectionManager;
     private MockContentResolver mMockResolver;
     private MagnificationController mMagnificationController;
     private final WindowMagnificationMgrCallbackDelegate
@@ -205,13 +205,14 @@
         ));
         mScreenMagnificationController.register(TEST_DISPLAY);
 
-        mWindowMagnificationManager = spy(new WindowMagnificationManager(mContext, globalLock,
-                mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
+        mMagnificationConnectionManager = spy(
+                new MagnificationConnectionManager(mContext, globalLock,
+                        mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
         mMockConnection = new MockWindowMagnificationConnection(true);
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
 
         mMagnificationController = spy(new MagnificationController(mService, globalLock, mContext,
-                mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider,
+                mScreenMagnificationController, mMagnificationConnectionManager, mScaleProvider,
                 ConcurrentUtils.DIRECT_EXECUTOR));
         mMagnificationController.setMagnificationCapabilities(
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -254,8 +255,10 @@
         mCallbackArgumentCaptor.getValue().onResult(true);
         mMockConnection.invokeCallbacks();
         verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
-        assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
-        assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+        assertEquals(MAGNIFIED_CENTER_X,
+                mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+        assertEquals(MAGNIFIED_CENTER_Y,
+                mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
     }
 
     @Test
@@ -297,8 +300,10 @@
 
         mMockConnection.invokeCallbacks();
         verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
-        assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
-        assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+        assertEquals(MAGNIFIED_CENTER_X,
+                mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+        assertEquals(MAGNIFIED_CENTER_Y,
+                mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
     }
 
     @Test
@@ -316,7 +321,7 @@
         // The first time is triggered when window mode is activated.
         // The second time is triggered when activating the window mode again.
         // The third time is triggered when the transition is completed.
-        verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_WINDOW));
     }
 
@@ -330,7 +335,7 @@
                 mTransitionCallBack);
         mMockConnection.invokeCallbacks();
 
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
         verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
                 DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
                 true, MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -350,7 +355,7 @@
                 mTransitionCallBack);
         mMockConnection.invokeCallbacks();
 
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
         verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
                 magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true,
                 MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -386,11 +391,11 @@
                 mTransitionCallBack);
 
         // Enable window magnification while animating.
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+        mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
                 Float.NaN, Float.NaN, null, TEST_SERVICE_ID);
         mMockConnection.invokeCallbacks();
 
-        assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
         verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
                 DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
                 true, MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -408,8 +413,10 @@
 
         verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), eq(false));
         mMockConnection.invokeCallbacks();
-        assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
-        assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+        assertEquals(MAGNIFIED_CENTER_X,
+                mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+        assertEquals(MAGNIFIED_CENTER_Y,
+                mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
     }
 
     @Test
@@ -438,7 +445,7 @@
                 animate, TEST_SERVICE_ID);
         mMockConnection.invokeCallbacks();
 
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
         verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
                 eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y),
                 any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID));
@@ -564,7 +571,7 @@
         mMagnificationController.onDisplayRemoved(TEST_DISPLAY);
 
         verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY);
-        verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY);
+        verify(mMagnificationConnectionManager).onDisplayRemoved(TEST_DISPLAY);
         verify(mScaleProvider).onDisplayRemoved(TEST_DISPLAY);
     }
 
@@ -573,7 +580,7 @@
         mMagnificationController.updateUserIdIfNeeded(SECOND_USER_ID);
 
         verify(mScreenMagnificationController).resetAllIfNeeded(false);
-        verify(mWindowMagnificationManager).disableAllWindowMagnifiers();
+        verify(mMagnificationConnectionManager).disableAllWindowMagnifiers();
         verify(mScaleProvider).onUserChanged(SECOND_USER_ID);
     }
 
@@ -584,7 +591,7 @@
         mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, TEST_SERVICE_ID);
         mMockConnection.invokeCallbacks();
 
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+        assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
     }
 
     @Test
@@ -594,11 +601,11 @@
 
         // The first time is trigger when fullscreen mode is activated.
         // The second time is triggered when magnification spec is changed.
-        verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_FULLSCREEN));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -625,8 +632,8 @@
 
         mMagnificationController.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence);
 
-        verify(mWindowMagnificationManager).setScale(eq(TEST_DISPLAY), eq(newScale));
-        verify(mWindowMagnificationManager, never()).persistScale(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), eq(newScale));
+        verify(mMagnificationConnectionManager, never()).persistScale(eq(TEST_DISPLAY));
     }
 
     @Test
@@ -669,7 +676,7 @@
         assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0);
         assertEquals(config.getScale(), actualConfig.getScale(), 0);
 
-        verify(mWindowMagnificationManager).onUserMagnificationScaleChanged(
+        verify(mMagnificationConnectionManager).onUserMagnificationScaleChanged(
                 /* userId= */ anyInt(), eq(TEST_DISPLAY), eq(config.getScale()));
     }
 
@@ -677,11 +684,11 @@
     public void onSourceBoundChanged_windowEnabled_notifyMagnificationChanged()
             throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
-        reset(mWindowMagnificationManager);
+        reset(mMagnificationConnectionManager);
 
         mMagnificationController.onSourceBoundsChanged(TEST_DISPLAY, TEST_RECT);
 
-        verify(mWindowMagnificationManager).onUserMagnificationScaleChanged(
+        verify(mMagnificationConnectionManager).onUserMagnificationScaleChanged(
                 /* userId= */ anyInt(), eq(TEST_DISPLAY), eq(DEFAULT_SCALE));
     }
 
@@ -780,11 +787,11 @@
 
         // The first time is triggered when window mode is activated.
         // The second time is triggered when accessibility action performed.
-        verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_WINDOW));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -799,10 +806,11 @@
 
         // The first time is triggered when window mode is activated.
         // The second time is triggered when accessibility action performed.
-        verify(mWindowMagnificationManager, times(2)).removeMagnificationButton(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager, times(2))
+                .removeMagnificationButton(eq(TEST_DISPLAY));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -816,7 +824,7 @@
     public void deactivateWindowMagnification_windowActivated_triggerCallbackAndLogUsage()
             throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, /* clear= */ true);
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, /* clear= */ true);
 
         verify(mMagnificationController).onWindowMagnificationActivationState(
                 eq(TEST_DISPLAY), eq(false));
@@ -828,7 +836,7 @@
     public void setPreferenceMagnificationFollowTypingEnabled_setPrefDisabled_disableAll() {
         mMagnificationController.setMagnificationFollowTypingEnabled(false);
 
-        verify(mWindowMagnificationManager).setMagnificationFollowTypingEnabled(eq(false));
+        verify(mMagnificationConnectionManager).setMagnificationFollowTypingEnabled(eq(false));
         verify(mScreenMagnificationController).setMagnificationFollowTypingEnabled(eq(false));
     }
 
@@ -850,7 +858,7 @@
 
         verify(mScreenMagnificationController).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
                 eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom));
-        verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+        verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
                 anyInt(), anyInt(), anyInt(), anyInt());
     }
 
@@ -867,7 +875,7 @@
 
         verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(anyInt(),
                 anyInt(), anyInt(), anyInt(), anyInt());
-        verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+        verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
                 anyInt(), anyInt(), anyInt(), anyInt());
     }
 
@@ -880,7 +888,7 @@
 
         verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
                 eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
-        verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+        verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
                 anyInt(), anyInt(), anyInt(), anyInt());
     }
 
@@ -895,7 +903,7 @@
 
         verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
                 eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
-        verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+        verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
                 anyInt(), anyInt(), anyInt(), anyInt());
     }
 
@@ -970,7 +978,8 @@
 
         mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
 
-        verify(mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
+        verify(mMagnificationConnectionManager)
+                .disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
     }
 
     @Test
@@ -983,11 +992,11 @@
         // The first time is triggered when fullscreen mode is activated.
         // The second time is triggered when magnification spec is changed.
         // The third time is triggered when user interaction changed.
-        verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_FULLSCREEN));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -1001,11 +1010,11 @@
         // The first time is triggered when fullscreen mode is activated.
         // The second time is triggered when magnification spec is changed.
         // The third time is triggered when user interaction changed.
-        verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_FULLSCREEN));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -1018,11 +1027,11 @@
 
         // The first time is triggered when the window mode is activated.
         // The second time is triggered when user interaction changed.
-        verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_WINDOW));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -1035,11 +1044,11 @@
 
         // The first time is triggered when the window mode is activated.
         // The second time is triggered when user interaction changed.
-        verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_WINDOW));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -1053,11 +1062,11 @@
         mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
         mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
 
-        verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_FULLSCREEN));
         // The first time is triggered when fullscreen mode is activated.
         // The second time is triggered when magnification spec is changed.
-        verify(mWindowMagnificationManager, times(2)).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, times(2)).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -1071,9 +1080,9 @@
         mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
         mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
 
-        verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_FULLSCREEN));
-        verify(mWindowMagnificationManager, times(2)).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, times(2)).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -1082,11 +1091,11 @@
             throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
 
-        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_WINDOW));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -1100,11 +1109,11 @@
         // The first time is triggered when fullscreen mode is activated.
         // The second time is triggered when magnification spec is changed.
         // The third time is triggered when fullscreen mode activation state is updated.
-        verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_FULLSCREEN));
         // Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
         // in current capability and mode, and the magnification is activated.
-        verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+        verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
                 eq(TEST_DISPLAY));
     }
 
@@ -1113,10 +1122,10 @@
             throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
 
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
 
-        verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
-        verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).removeMagnificationButton(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
     }
 
     @Test
@@ -1126,8 +1135,8 @@
         setMagnificationEnabled(MODE_FULLSCREEN);
         mScreenMagnificationController.reset(TEST_DISPLAY, /* animate= */ true);
 
-        verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
-        verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).removeMagnificationButton(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
     }
 
     @Test
@@ -1142,10 +1151,10 @@
         // The first time is triggered when fullscreen mode is activated.
         // The second time is triggered when magnification spec is changed.
         // The third time is triggered when the disable-magnification callback is triggered.
-        verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_FULLSCREEN));
         // It is triggered when the disable-magnification callback is triggered.
-        verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
     }
 
     @Test
@@ -1163,10 +1172,10 @@
 
         // The first time is triggered when window mode is activated.
         // The second time is triggered when the disable-magnification callback is triggered.
-        verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+        verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
                 eq(MODE_WINDOW));
         // It is triggered when the disable-magnification callback is triggered.
-        verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
     }
 
     @Test
@@ -1174,9 +1183,9 @@
             throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
 
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+        mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
 
-        verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
     }
 
     @Test
@@ -1185,7 +1194,7 @@
         setMagnificationEnabled(MODE_FULLSCREEN);
         mScreenMagnificationController.reset(TEST_DISPLAY, /* animate= */ true);
 
-        verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+        verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
     }
 
     @Test
@@ -1260,17 +1269,17 @@
 
     private void activateMagnifier(int displayId, int mode, float centerX, float centerY)
             throws RemoteException {
-        final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
+        final boolean windowMagnifying = mMagnificationConnectionManager.isWindowMagnifierEnabled(
                 displayId);
         if (windowMagnifying) {
-            mWindowMagnificationManager.disableWindowMagnification(displayId, false);
+            mMagnificationConnectionManager.disableWindowMagnification(displayId, false);
             mMockConnection.invokeCallbacks();
         }
         if (mode == MODE_FULLSCREEN) {
             mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX,
                     centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
         } else {
-            mWindowMagnificationManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
+            mMagnificationConnectionManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
                     centerX, centerY, null, TEST_SERVICE_ID);
             mMockConnection.invokeCallbacks();
         }
@@ -1304,10 +1313,10 @@
     }
 
     private static class WindowMagnificationMgrCallbackDelegate implements
-            WindowMagnificationManager.Callback {
-        private WindowMagnificationManager.Callback mCallback;
+            MagnificationConnectionManager.Callback {
+        private MagnificationConnectionManager.Callback mCallback;
 
-        public void setDelegate(WindowMagnificationManager.Callback callback) {
+        public void setDelegate(MagnificationConnectionManager.Callback callback) {
             mCallback = callback;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 612a091..c4be51f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -92,7 +92,7 @@
     public final TestableContext mContext = new TestableContext(
             InstrumentationRegistry.getInstrumentation().getContext());
 
-    private WindowMagnificationManager mWindowMagnificationManager;
+    private MagnificationConnectionManager mMagnificationConnectionManager;
     private MockWindowMagnificationConnection mMockConnection;
     private SpyWindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
     private WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
@@ -104,23 +104,23 @@
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
-        mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
-                mock(WindowMagnificationManager.Callback.class), mMockTrace,
+        mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
+                mock(MagnificationConnectionManager.Callback.class), mMockTrace,
                 new MagnificationScaleProvider(mContext));
         mMockConnection = new MockWindowMagnificationConnection();
         mWindowMagnificationGestureHandler = new SpyWindowMagnificationGestureHandler(
-                mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
+                mContext, mMagnificationConnectionManager, mMockTrace, mMockCallback,
                 /** detectSingleFingerTripleTap= */ true, /** detectTwoFingerTripleTap= */ true,
                 /** detectShortcutTrigger= */ true, DISPLAY_0);
         mMockWindowMagnificationGestureHandler =
                 mWindowMagnificationGestureHandler.getMockGestureHandler();
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
         mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
     }
 
     @After
     public void tearDown() {
-        mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, true);
+        mMagnificationConnectionManager.disableWindowMagnification(DISPLAY_0, true);
     }
 
     @Test
@@ -378,7 +378,7 @@
             }
             break;
             case STATE_SHOW_MAGNIFIER_SHORTCUT: {
-                mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false);
+                mMagnificationConnectionManager.disableWindowMagnification(DISPLAY_0, false);
             }
             break;
             case STATE_TWO_FINGERS_DOWN: {
@@ -423,7 +423,7 @@
     }
 
     private boolean isWindowMagnifierEnabled(int displayId) {
-        return mWindowMagnificationManager.isWindowMagnifierEnabled(displayId);
+        return mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
     }
 
     private static String stateToString(int state) {
@@ -495,13 +495,14 @@
         private final WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
 
         SpyWindowMagnificationGestureHandler(@UiContext Context context,
-                WindowMagnificationManager windowMagnificationMgr,
+                MagnificationConnectionManager magnificationConnectionManager,
                 AccessibilityTraceManager trace,
                 Callback callback,
                 boolean detectSingleFingerTripleTap, boolean detectTwoFingerTripleTap,
                 boolean detectShortcutTrigger, int displayId) {
-            super(context, windowMagnificationMgr, trace, callback, detectSingleFingerTripleTap,
-                    detectTwoFingerTripleTap, detectShortcutTrigger, displayId);
+            super(context, magnificationConnectionManager, trace, callback,
+                    detectSingleFingerTripleTap, detectTwoFingerTripleTap,
+                    detectShortcutTrigger, displayId);
             mMockWindowMagnificationGestureHandler = mock(WindowMagnificationGestureHandler.class);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
deleted file mode 100644
index 24ad976..0000000
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ /dev/null
@@ -1,847 +0,0 @@
-/*
- * Copyright (C) 2019 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.accessibility.magnification;
-
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY_2;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import static java.lang.Float.NaN;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnectionCallback;
-import android.view.accessibility.MagnificationAnimationCallback;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.FlakyTest;
-
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
-import com.android.server.accessibility.AccessibilityTraceManager;
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-
-/**
- * Tests for WindowMagnificationManager.
- */
-public class WindowMagnificationManagerTest {
-
-    private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
-    private static final int SERVICE_ID = 1;
-
-    private MockWindowMagnificationConnection mMockConnection;
-    @Mock
-    private Context mContext;
-    @Mock
-    private AccessibilityTraceManager mMockTrace;
-    @Mock
-    private StatusBarManagerInternal mMockStatusBarManagerInternal;
-    @Mock
-    private MagnificationAnimationCallback mAnimationCallback;
-    @Mock
-    private WindowMagnificationManager.Callback mMockCallback;
-    private MockContentResolver mResolver;
-    private WindowMagnificationManager mWindowMagnificationManager;
-
-    @Before
-    public void setUp() throws RemoteException {
-        MockitoAnnotations.initMocks(this);
-        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
-        LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
-        mResolver = new MockContentResolver();
-        mMockConnection = new MockWindowMagnificationConnection();
-        mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
-                mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
-
-        when(mContext.getContentResolver()).thenReturn(mResolver);
-        stubSetConnection(false);
-
-        mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        Settings.Secure.putFloatForUser(mResolver,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f,
-                CURRENT_USER_ID);
-    }
-
-    private void stubSetConnection(boolean needDelay) {
-        doAnswer((InvocationOnMock invocation) -> {
-            final boolean connect = (Boolean) invocation.getArguments()[0];
-            // Simulates setConnection() called by another process.
-            if (needDelay) {
-                final Context context = ApplicationProvider.getApplicationContext();
-                context.getMainThreadHandler().postDelayed(
-                        () -> {
-                            mWindowMagnificationManager.setConnection(
-                                    connect ? mMockConnection.getConnection() : null);
-                        }, 10);
-            } else {
-                mWindowMagnificationManager.setConnection(
-                        connect ? mMockConnection.getConnection() : null);
-            }
-            return true;
-        }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
-    }
-
-    @Test
-    public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
-        verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
-    }
-
-    @Test
-    public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
-        verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
-        verify(mMockConnection.getConnection()).setConnectionCallback(
-                any(IWindowMagnificationConnectionCallback.class));
-    }
-
-    @Test
-    public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        mMockConnection.getDeathRecipient().binderDied();
-
-        assertNull(mWindowMagnificationManager.mConnectionWrapper);
-        verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
-                0);
-    }
-
-    /**
-     * This test simulates {@link WindowMagnificationManager#setConnection} is called by thread A
-     * and then the former connection is called by thread B. In this situation we should keep the
-     * new connection.
-     */
-    @Test
-    public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        MockWindowMagnificationConnection secondConnection =
-                new MockWindowMagnificationConnection();
-
-        mWindowMagnificationManager.setConnection(secondConnection.getConnection());
-        mMockConnection.getDeathRecipient().binderDied();
-
-        assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
-        verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
-        verify(secondConnection.asBinder(), never()).unlinkToDeath(
-                secondConnection.getDeathRecipient(), 0);
-    }
-
-    @Test
-    public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        mWindowMagnificationManager.setConnection(null);
-
-        assertNull(mWindowMagnificationManager.mConnectionWrapper);
-        verify(mMockConnection.getConnection()).setConnectionCallback(null);
-    }
-
-    @Test
-    public void enableWithAnimation_hasConnection_enableWindowMagnification()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
-
-        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
-                eq(200f), eq(300f), eq(0f), eq(0f), notNull());
-    }
-
-    @Test
-    public void enableWithCallback_hasConnection_enableWindowMagnification()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
-                mAnimationCallback, SERVICE_ID);
-
-        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
-                eq(200f), eq(300f), eq(0f), eq(0f),
-                any(IRemoteMagnificationAnimationCallback.class));
-        verify(mAnimationCallback).onResult(true);
-    }
-
-    @Test
-    public void disable_hasConnectionAndEnabled_disableWindowMagnification()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
-        verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
-                notNull());
-    }
-
-    @Test
-    public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false,
-                mAnimationCallback);
-
-        verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
-                any(IRemoteMagnificationAnimationCallback.class));
-        verify(mAnimationCallback).onResult(true);
-    }
-
-    @Test
-    public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
-
-        assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-    }
-
-    @Test
-    public void getPersistedScale() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        assertEquals(mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY), 2.5f);
-    }
-
-    @Test
-    public void persistScale_setValue_expectedValueInProvider() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
-        mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
-
-        mWindowMagnificationManager.persistScale(TEST_DISPLAY);
-
-        assertEquals(Settings.Secure.getFloatForUser(mResolver,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
-                CURRENT_USER_ID), 2.5f);
-    }
-
-    @Test
-    public void persistScale_setValueWhenScaleIsOne_nothingChanged() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        final float persistedScale = mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY);
-
-        mWindowMagnificationManager.setScale(TEST_DISPLAY, 1.0f);
-        mWindowMagnificationManager.persistScale(TEST_DISPLAY);
-
-        assertEquals(Settings.Secure.getFloatForUser(mResolver,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
-                CURRENT_USER_ID), persistedScale);
-    }
-
-    @Test
-    public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
-
-        mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
-
-        assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY), 2.5f);
-    }
-
-    @Test
-    public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
-
-        mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f);
-
-        assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY),
-                MagnificationScaleProvider.MAX_SCALE);
-    }
-
-    @FlakyTest(bugId = 297879435)
-    @Test
-    public void logTrackingTypingFocus_processScroll_logDuration() {
-        WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
-        spyWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        spyWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, /* shown */ true);
-
-        spyWindowMagnificationManager.processScroll(TEST_DISPLAY, 10f, 10f);
-
-        verify(spyWindowMagnificationManager).logTrackingTypingFocus(anyLong());
-    }
-
-    @Test
-    public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        final Region outRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
-        final Rect requestedRect = outRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-        mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        verify(mMockConnection.getConnection(), never())
-                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
-    }
-
-
-    @Test
-    public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier()
-            throws RemoteException {
-        final float distanceX = 10f;
-        final float distanceY = 10f;
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        final Region outRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
-        final Rect requestedRect = outRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-        mWindowMagnificationManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        verify(mMockConnection.getConnection(), never())
-                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
-    }
-
-    @Test
-    public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        final Region outRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
-        final Rect requestedRect = outRegion.getBounds();
-        requestedRect.inset(-10, -10);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        verify(mMockConnection.getConnection(), never())
-                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
-    }
-    @Test
-    public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        final Region outRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
-        final Rect requestedRect = outRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        verify(mMockConnection.getConnection(), never())
-                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
-    }
-
-    @Test
-    public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        final Region outRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
-        final Rect requestedRect = outRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
-                eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
-                any(IRemoteMagnificationAnimationCallback.class));
-    }
-
-    @Test
-    public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        final Region outRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
-        final Rect requestedRect = outRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        verify(mMockConnection.getConnection(), never())
-                .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
-    }
-
-    @Test
-    public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        final Region outRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
-        final Rect requestedRect = outRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
-                eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
-                any(IRemoteMagnificationAnimationCallback.class));
-    }
-
-    @Test
-    public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        final Region beforeRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
-        final Rect requestedRect = beforeRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-        mWindowMagnificationManager.setMagnificationFollowTypingEnabled(false);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        final Region afterRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
-        assertEquals(afterRegion, beforeRegion);
-    }
-
-    @Test
-    public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
-        final Region beforeRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
-        final Rect requestedRect = beforeRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        final Region afterRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
-        assertEquals(afterRegion, beforeRegion);
-    }
-
-    @Test
-    public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
-        mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
-        mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
-        final Region beforeRegion = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
-        final Rect requestedRect = beforeRegion.getBounds();
-        requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-        // Enabling a window magnifier again will turn on the tracking typing focus functionality.
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
-
-        mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
-                requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
-        verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
-                eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
-                any(IRemoteMagnificationAnimationCallback.class));
-    }
-
-    @Test
-    public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
-
-        mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
-        verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
-    }
-
-    @Test
-    public void showMagnificationButton_hasConnection_invokeConnectionMethod()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        mWindowMagnificationManager.showMagnificationButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-        verify(mMockConnection.getConnection()).showMagnificationButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-
-        mWindowMagnificationManager.removeMagnificationButton(TEST_DISPLAY);
-        verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
-    }
-
-    @Test
-    public void removeMagnificationSettingsPanel_hasConnection_invokeConnectionMethod()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        mWindowMagnificationManager.removeMagnificationSettingsPanel(TEST_DISPLAY);
-        verify(mMockConnection.getConnection()).removeMagnificationSettingsPanel(TEST_DISPLAY);
-    }
-
-    @Test
-    public void onUserMagnificationScaleChanged_hasConnection_invokeConnectionMethod()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
-        final float testScale = 3f;
-        mWindowMagnificationManager.onUserMagnificationScaleChanged(
-                CURRENT_USER_ID, TEST_DISPLAY, testScale);
-        verify(mMockConnection.getConnection()).onUserMagnificationScaleChanged(
-                eq(CURRENT_USER_ID), eq(TEST_DISPLAY), eq(testScale));
-    }
-
-    @Test
-    public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-        mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
-                new Rect(0, 0, 500, 500));
-        PointF[] pointersLocation = new PointF[2];
-        pointersLocation[0] = new PointF(600, 700);
-        pointersLocation[1] = new PointF(300, 400);
-        MotionEvent event = generatePointersDownEvent(pointersLocation);
-
-        assertEquals(mWindowMagnificationManager.pointersInWindow(TEST_DISPLAY, event), 1);
-    }
-
-    @Test
-    public void onPerformScaleAction_magnifierEnabled_notifyAction() throws RemoteException {
-        final float newScale = 4.0f;
-        final boolean updatePersistence = true;
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
-        mMockConnection.getConnectionCallback().onPerformScaleAction(
-                TEST_DISPLAY, newScale, updatePersistence);
-
-        verify(mMockCallback).onPerformScaleAction(
-                eq(TEST_DISPLAY), eq(newScale), eq(updatePersistence));
-    }
-
-    @Test
-    public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
-            throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
-        mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
-
-        verify(mMockCallback).onAccessibilityActionPerformed(eq(TEST_DISPLAY));
-    }
-
-    @Test
-    public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
-        mMockConnection.getDeathRecipient().binderDied();
-
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-    }
-
-    @Test
-    public void
-            requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
-            throws RemoteException {
-        assertTrue(mWindowMagnificationManager.requestConnection(true));
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
-        assertTrue(mWindowMagnificationManager.requestConnection(false));
-
-        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
-        verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
-    }
-
-    @Test
-    public void requestConnection_requestWindowMagnificationConnection() throws RemoteException {
-        assertTrue(mWindowMagnificationManager.requestConnection(true));
-        verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true);
-    }
-
-    @Test
-    public void isConnected_requestConnection_expectedValue() throws RemoteException {
-        mWindowMagnificationManager.requestConnection(true);
-        assertTrue(mWindowMagnificationManager.isConnected());
-
-        mWindowMagnificationManager.requestConnection(false);
-        assertFalse(mWindowMagnificationManager.isConnected());
-    }
-
-    @Test
-    public void requestConnection_registerAndUnregisterBroadcastReceiver() {
-        assertTrue(mWindowMagnificationManager.requestConnection(true));
-        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
-
-        assertTrue(mWindowMagnificationManager.requestConnection(false));
-        verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
-    }
-
-    @Test
-    public void requestConnectionToNull_expectedGetterResults() {
-        mWindowMagnificationManager.requestConnection(true);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
-
-        mWindowMagnificationManager.requestConnection(false);
-
-        assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
-        assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
-        assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
-        final Region bounds = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
-        assertTrue(bounds.isEmpty());
-    }
-
-    @Test
-    public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected()
-            throws RemoteException {
-        stubSetConnection(true);
-        mWindowMagnificationManager.requestConnection(true);
-
-        assertTrue(mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1));
-
-        // Invoke enableWindowMagnification if the connection is connected.
-        verify(mMockConnection.getConnection()).enableWindowMagnification(
-                eq(TEST_DISPLAY), eq(3f),
-                eq(1f), eq(1f), eq(0f), eq(0f), notNull());
-    }
-
-    @Test
-    public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
-                100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
-                100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
-        assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-        assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
-
-        mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
-
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
-    }
-
-    @Test
-    public void resetAllMagnification_enabledByDifferentId_windowMagnifierDisabled() {
-        final int serviceId2 = SERVICE_ID + 1;
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
-                100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
-                100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, serviceId2);
-        assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-        assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
-
-        mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
-
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-        assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
-    }
-
-    @Test
-    public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
-            throws RemoteException {
-        mWindowMagnificationManager.requestConnection(true);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
-
-        mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext,
-                new Intent(Intent.ACTION_SCREEN_OFF));
-
-        verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
-        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-    }
-
-    @Test
-    public void centerGetter_enabledOnTestDisplay_expectedValues() {
-        mWindowMagnificationManager.requestConnection(true);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
-
-        assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
-        assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
-    }
-
-    @Test
-    public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
-            throws RemoteException {
-        mWindowMagnificationManager.requestConnection(true);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
-                100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
-
-        assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
-        assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
-
-        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
-                eq(100f), eq(200f), eq(0f), eq(0f), notNull());
-    }
-
-    @Test
-    public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
-            throws RemoteException {
-        mWindowMagnificationManager.requestConnection(true);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
-                100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
-
-        assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
-        assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
-
-        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
-                eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
-    }
-
-    @Test
-    public void magnifierGetters_disabled_expectedValues() {
-        mWindowMagnificationManager.requestConnection(true);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
-                100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
-
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
-        assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
-        assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
-        assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
-        final Region bounds = new Region();
-        mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
-        assertTrue(bounds.isEmpty());
-    }
-
-    @Test
-    public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
-        mWindowMagnificationManager.requestConnection(true);
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
-
-        mWindowMagnificationManager.onDisplayRemoved(TEST_DISPLAY);
-
-        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-    }
-
-    @Test
-    public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
-        verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, true);
-    }
-
-    @Test
-    public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
-        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
-        verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, false);
-
-        Mockito.reset(mMockCallback);
-        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
-        verify(mMockCallback, never()).onWindowMagnificationActivationState(eq(TEST_DISPLAY),
-                anyBoolean());
-    }
-
-    private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
-        final int len = pointersLocation.length;
-
-        final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[len];
-        for (int i = 0; i < len; i++) {
-            MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
-            pointerProperty.id = i;
-            pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER;
-            pp[i] = pointerProperty;
-        }
-
-        final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[len];
-        for (int i = 0; i < len; i++) {
-            MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
-            pointerCoord.x = pointersLocation[i].x;
-            pointerCoord.y = pointersLocation[i].y;
-            pc[i] = pointerCoord;
-        }
-
-        return MotionEvent.obtain(
-                /* downTime */ SystemClock.uptimeMillis(),
-                /* eventTime */ SystemClock.uptimeMillis(),
-                /* action */ MotionEvent.ACTION_POINTER_DOWN,
-                /* pointerCount */ pc.length,
-                /* pointerProperties */ pp,
-                /* pointerCoords */ pc,
-                /* metaState */ 0,
-                /* buttonState */ 0,
-                /* xPrecision */ 1.0f,
-                /* yPrecision */ 1.0f,
-                /* deviceId */ 0,
-                /* edgeFlags */ 0,
-                /* source */ InputDevice.SOURCE_TOUCHSCREEN,
-                /* flags */ 0);
-    }
-
-
-}
diff --git a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
index 749b07d..9c8276a 100644
--- a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
@@ -19,6 +19,16 @@
 import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID;
 import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
 import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
+
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_LARGE;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_MEDIUM;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_SMALL;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_UNKNOWN;
+
+import static junit.framework.Assert.assertEquals;
 
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -34,11 +44,15 @@
 import android.media.LoudnessCodecInfo;
 import android.media.PlayerBase;
 import android.os.IBinder;
+import android.os.PersistableBundle;
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.server.audio.LoudnessCodecHelper.DeviceSplRange;
+import com.android.server.audio.LoudnessCodecHelper.LoudnessCodecInputProperties;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -84,8 +98,7 @@
         mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
 
         mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
-                List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
-                        CODEC_METADATA_TYPE_MPEG_4)));
+                List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
 
         verify(mDispatcher).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid), any());
     }
@@ -96,8 +109,7 @@
         mLoudnessHelper.unregisterLoudnessCodecUpdatesDispatcher(mDispatcher);
 
         mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
-                List.of(getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/false,
-                        CODEC_METADATA_TYPE_MPEG_D)));
+                List.of(getLoudnessInfo(/*isDownmixing=*/false, CODEC_METADATA_TYPE_MPEG_D)));
 
         verify(mDispatcher, times(0)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
                 any());
@@ -108,11 +120,9 @@
         mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
 
         mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
-                List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
-                        CODEC_METADATA_TYPE_MPEG_4)));
-        mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid,
-                getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
-                        CODEC_METADATA_TYPE_MPEG_D));
+                List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
+        mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid, /*mediaCodecHash=*/222,
+                getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
 
         verify(mDispatcher, times(2)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
                 any());
@@ -124,11 +134,10 @@
         mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
 
         mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
-                List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
+                List.of(getLoudnessInfo(/*isDownmixing=*/true,
                         CODEC_METADATA_TYPE_MPEG_4)));
-        mLoudnessHelper.addLoudnessCodecInfo(newPiid,
-                getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
-                        CODEC_METADATA_TYPE_MPEG_D));
+        mLoudnessHelper.addLoudnessCodecInfo(newPiid, /*mediaCodecHash=*/222,
+                getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
 
         verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
                 any());
@@ -140,12 +149,10 @@
         mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
 
         mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
-                List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
-                        CODEC_METADATA_TYPE_MPEG_4)));
+                List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
         //does not trigger dispatch since active apc list does not contain newPiid
         mLoudnessHelper.startLoudnessCodecUpdates(newPiid,
-                List.of(getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
-                        CODEC_METADATA_TYPE_MPEG_D)));
+                List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D)));
         verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
                 any());
 
@@ -157,9 +164,8 @@
     @Test
     public void updateCodecParameters_noStartedPiids_noDispatch() throws Exception {
         mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
-        mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid,
-                getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
-                        CODEC_METADATA_TYPE_MPEG_D));
+        mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid, /*mediaCodecHash=*/222,
+                getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
 
         mLoudnessHelper.updateCodecParameters(getApcListForPiids(mInitialApcPiid));
 
@@ -170,8 +176,8 @@
 
     @Test
     public void updateCodecParameters_removedCodecInfo_noDispatch() throws Exception {
-        final LoudnessCodecInfo info = getLoudnessInfo(/*mediaCodecHash=*/111,
-                /*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4);
+        final LoudnessCodecInfo info = getLoudnessInfo(/*isDownmixing=*/true,
+                CODEC_METADATA_TYPE_MPEG_4);
         mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
 
         mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid, List.of(info));
@@ -186,8 +192,8 @@
 
     @Test
     public void updateCodecParameters_stoppedPiids_noDispatch() throws Exception {
-        final LoudnessCodecInfo info = getLoudnessInfo(/*mediaCodecHash=*/111,
-                /*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4);
+        final LoudnessCodecInfo info = getLoudnessInfo(/*isDownmixing=*/true,
+                CODEC_METADATA_TYPE_MPEG_4);
         mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
 
         mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid, List.of(info));
@@ -200,6 +206,108 @@
                 any());
     }
 
+    @Test
+    public void checkParcelableBundle_forMpeg4CodecInputProperties() {
+        PersistableBundle loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+                SPL_RANGE_SMALL).createLoudnessParameters();
+        assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+                SPL_RANGE_SMALL).createLoudnessParameters();
+        assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+                SPL_RANGE_MEDIUM).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+                SPL_RANGE_MEDIUM).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+                SPL_RANGE_LARGE).createLoudnessParameters();
+        assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+                SPL_RANGE_LARGE).createLoudnessParameters();
+        assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+                SPL_RANGE_UNKNOWN).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+                SPL_RANGE_UNKNOWN).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+    }
+
+    @Test
+    public void checkParcelableBundle_forMpegDCodecInputProperties() {
+        PersistableBundle loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+                SPL_RANGE_SMALL).createLoudnessParameters();
+        assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+                SPL_RANGE_SMALL).createLoudnessParameters();
+        assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+                SPL_RANGE_MEDIUM).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+                SPL_RANGE_MEDIUM).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+                SPL_RANGE_LARGE).createLoudnessParameters();
+        assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+                SPL_RANGE_LARGE).createLoudnessParameters();
+        assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+                SPL_RANGE_UNKNOWN).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+        loudnessParameters = createInputProperties(
+                CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+                SPL_RANGE_UNKNOWN).createLoudnessParameters();
+        assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+        assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+    }
+
     private List<AudioPlaybackConfiguration> getApcListForPiids(int... piids) {
         final ArrayList<AudioPlaybackConfiguration> apcList = new ArrayList<>();
 
@@ -220,11 +328,15 @@
         return apcList;
     }
 
-    private static LoudnessCodecInfo getLoudnessInfo(int mediaCodecHash, boolean isDownmixing,
-            int metadataType) {
+    private static LoudnessCodecInputProperties createInputProperties(
+            int metadataType, boolean isDownmixing, @DeviceSplRange int splRange) {
+        return new LoudnessCodecInputProperties.Builder().setMetadataType(
+                metadataType).setIsDownmixing(isDownmixing).setDeviceSplRange(splRange).build();
+    }
+
+    private static LoudnessCodecInfo getLoudnessInfo(boolean isDownmixing, int metadataType) {
         LoudnessCodecInfo info = new LoudnessCodecInfo();
         info.isDownmixing = isDownmixing;
-        info.mediaCodecHashCode = mediaCodecHash;
         info.metadataType = metadataType;
 
         return info;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index b5ba322..9213601a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -413,18 +413,24 @@
     public void getDeviceIdForDisplayId_invalidDisplayId_returnsDefault() {
         assertThat(mVdm.getDeviceIdForDisplayId(Display.INVALID_DISPLAY))
                 .isEqualTo(DEVICE_ID_DEFAULT);
+        assertThat(mLocalService.getDeviceIdForDisplayId(Display.INVALID_DISPLAY))
+                .isEqualTo(DEVICE_ID_DEFAULT);
     }
 
     @Test
     public void getDeviceIdForDisplayId_defaultDisplayId_returnsDefault() {
         assertThat(mVdm.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY))
                 .isEqualTo(DEVICE_ID_DEFAULT);
+        assertThat(mLocalService.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY))
+                .isEqualTo(DEVICE_ID_DEFAULT);
     }
 
     @Test
     public void getDeviceIdForDisplayId_nonExistentDisplayId_returnsDefault() {
         assertThat(mVdm.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
                 .isEqualTo(DEVICE_ID_DEFAULT);
+        assertThat(mLocalService.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
+                .isEqualTo(DEVICE_ID_DEFAULT);
     }
 
     @Test
@@ -433,6 +439,8 @@
 
         assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
                 .isEqualTo(mDeviceImpl.getDeviceId());
+        assertThat(mLocalService.getDeviceIdForDisplayId(DISPLAY_ID_1))
+                .isEqualTo(mDeviceImpl.getDeviceId());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 01922e0..edfe1b4 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -40,6 +40,7 @@
 import android.testing.TestableLooper;
 import android.view.Surface;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -77,6 +78,11 @@
         when(mVirtualCameraServiceMock.registerCamera(any(), any())).thenReturn(true);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        mVirtualCameraController.close();
+    }
+
     @Test
     public void registerCamera_registersCamera() throws Exception {
         mVirtualCameraController.registerCamera(createVirtualCameraConfig(
@@ -95,6 +101,8 @@
     public void unregisterCamera_unregistersCamera() throws Exception {
         VirtualCameraConfig config = createVirtualCameraConfig(
                 CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1);
+        mVirtualCameraController.registerCamera(config);
+
         mVirtualCameraController.unregisterCamera(config);
 
         verify(mVirtualCameraServiceMock).unregisterCamera(any());
@@ -107,9 +115,10 @@
         mVirtualCameraController.registerCamera(createVirtualCameraConfig(
                 CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_2));
 
+        mVirtualCameraController.close();
+
         ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
                 ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
-        mVirtualCameraController.close();
         verify(mVirtualCameraServiceMock, times(2)).registerCamera(any(),
                 configurationCaptor.capture());
         List<VirtualCameraConfiguration> virtualCameraConfigurations =
diff --git a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
index 2be3f1e8..517f483 100644
--- a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
@@ -21,8 +21,11 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import android.app.usage.Flags;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
 import android.content.res.Configuration;
+import android.os.PersistableBundle;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -99,6 +102,17 @@
                 case UsageEvents.Event.LOCUS_ID_SET:
                     event.mLocusId = "locus" + (i % 7); //"random" locus
                     break;
+                case UsageEvents.Event.USER_INTERACTION:
+                    if (Flags.userInteractionTypeApi()) {
+                        // "random" user interaction extras.
+                        PersistableBundle extras = new PersistableBundle();
+                        extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+                                "fake.namespace.category" + (i % 13));
+                        extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION,
+                                "fakeaction" + (i % 13));
+                        event.mExtras = extras;
+                    }
+                    break;
             }
 
             intervalStats.addEvent(event);
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 6ae2658..cd29c80 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -24,11 +24,13 @@
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
+import android.app.usage.Flags;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.os.PersistableBundle;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.AtomicFile;
 import android.util.LongSparseArray;
@@ -183,6 +185,17 @@
                 case Event.LOCUS_ID_SET:
                     event.mLocusId = "locus" + (i % 7); //"random" locus
                     break;
+                case Event.USER_INTERACTION:
+                    if (Flags.userInteractionTypeApi()) {
+                        // "random" user interaction extras.
+                        PersistableBundle extras = new PersistableBundle();
+                        extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+                                "fake.namespace.category" + (i % 13));
+                        extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION,
+                                "fakeaction" + (i % 13));
+                        event.mExtras = extras;
+                    }
+                    break;
             }
 
             mIntervalStats.addEvent(event);
@@ -295,6 +308,18 @@
                         assertEquals(e1.mLocusIdToken, e2.mLocusIdToken,
                                 "Usage event " + debugId);
                         break;
+                    case Event.USER_INTERACTION:
+                        if (Flags.userInteractionTypeApi()) {
+                            PersistableBundle extras1 = e1.getExtras();
+                            PersistableBundle extras2 = e2.getExtras();
+                            assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+                                    extras2.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+                                    "Usage event " + debugId);
+                            assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+                                    extras2.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+                                    "Usage event " + debugId);
+                        }
+                        break;
                 }
                 // fallthrough
             case 4: // test fields added in version 4
diff --git a/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java
new file mode 100644
index 0000000..377e4c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2023 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.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.provider.DeviceConfig;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link UserSettingDeviceConfigMediator}
+ */
+@RunWith(AndroidJUnit4.class)
+public class UserSettingDeviceConfigMediatorTest {
+    @Test
+    public void testDeviceConfigOnly() {
+        UserSettingDeviceConfigMediator mediator =
+                new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+        DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+                .setInt("int", 1)
+                .setFloat("float", .5f)
+                .setBoolean("boolean", true)
+                .setLong("long", 123456789)
+                .setString("string", "abc123")
+                .build();
+
+        mediator.setDeviceConfigProperties(properties);
+
+        assertEquals(1, mediator.getInt("int", 123));
+        assertEquals(123, mediator.getInt("invalidKey", 123));
+        assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+        assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+        assertEquals(true, mediator.getBoolean("boolean", false));
+        assertEquals(true, mediator.getBoolean("invalidKey", true));
+        assertEquals(123456789, mediator.getLong("long", 987654321));
+        assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+        assertEquals("abc123", mediator.getString("string", "xyz987"));
+        assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+
+        // Clear the properties
+        mediator.setDeviceConfigProperties(null);
+
+        assertEquals(123, mediator.getInt("int", 123));
+        assertEquals(123, mediator.getInt("invalidKey", 123));
+        assertEquals(.8f, mediator.getFloat("float", .8f), 0.001);
+        assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+        assertEquals(false, mediator.getBoolean("boolean", false));
+        assertEquals(true, mediator.getBoolean("invalidKey", true));
+        assertEquals(987654321, mediator.getLong("long", 987654321));
+        assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+        assertEquals("xyz987", mediator.getString("string", "xyz987"));
+        assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+    }
+
+    @Test
+    public void testSettingsOnly() {
+        UserSettingDeviceConfigMediator mediator =
+                new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+        String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123";
+
+        mediator.setSettingsString(settings);
+
+        assertEquals(1, mediator.getInt("int", 123));
+        assertEquals(123, mediator.getInt("invalidKey", 123));
+        assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+        assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+        assertEquals(true, mediator.getBoolean("boolean", false));
+        assertEquals(true, mediator.getBoolean("invalidKey", true));
+        assertEquals(123456789, mediator.getLong("long", 987654321));
+        assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+        assertEquals("abc123", mediator.getString("string", "xyz987"));
+        assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+
+        // Clear the settings
+        mediator.setSettingsString(null);
+
+        assertEquals(123, mediator.getInt("int", 123));
+        assertEquals(123, mediator.getInt("invalidKey", 123));
+        assertEquals(.8f, mediator.getFloat("float", .8f), 0.001);
+        assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+        assertEquals(false, mediator.getBoolean("boolean", false));
+        assertEquals(true, mediator.getBoolean("invalidKey", true));
+        assertEquals(987654321, mediator.getLong("long", 987654321));
+        assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+        assertEquals("xyz987", mediator.getString("string", "xyz987"));
+        assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+    }
+
+    @Test
+    public void testSettingsOverridesAll() {
+        UserSettingDeviceConfigMediator mediator =
+                new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(',');
+
+        String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123,"
+                + "intOnlyInSettings=9,floatOnlyInSettings=.25f,booleanOnlyInSettings=true,"
+                + "longOnlyInSettings=53771465,stringOnlyInSettings=settingsString";
+        DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+                .setInt("int", 10)
+                .setInt("intOnlyInDeviceConfig", 9001)
+                .setFloat("float", .7f)
+                .setFloat("floatOnlyInDeviceConfig", .9f)
+                .setBoolean("boolean", false)
+                .setBoolean("booleanOnlyInDeviceConfig", true)
+                .setLong("long", 60000001)
+                .setLong("longOnlyInDeviceConfig", 7357)
+                .setString("string", "xyz987")
+                .setString("stringOnlyInDeviceConfig", "deviceConfigString")
+                .build();
+
+        mediator.setSettingsString(settings);
+        mediator.setDeviceConfigProperties(properties);
+
+        // Since settings overrides all, anything in DeviceConfig should be ignored,
+        // even if settings doesn't have a value for it.
+        assertEquals(1, mediator.getInt("int", 123));
+        assertEquals(9, mediator.getInt("intOnlyInSettings", 123));
+        assertEquals(123, mediator.getInt("intOnlyInDeviceConfig", 123));
+        assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+        assertEquals(.25f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+        assertEquals(.8f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+        assertEquals(true, mediator.getBoolean("boolean", false));
+        assertEquals(true, mediator.getBoolean("booleanOnlyInSettings", false));
+        assertEquals(false, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+        assertEquals(123456789, mediator.getLong("long", 987654321));
+        assertEquals(53771465, mediator.getLong("longOnlyInSettings", 987654321));
+        assertEquals(987654321, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+        assertEquals("abc123", mediator.getString("string", "default"));
+        assertEquals("settingsString", mediator.getString("stringOnlyInSettings", "default"));
+        assertEquals("default", mediator.getString("stringOnlyInDeviceConfig", "default"));
+
+        // Nothing in settings, do DeviceConfig can be used.
+        mediator.setSettingsString("");
+
+        assertEquals(10, mediator.getInt("int", 123));
+        assertEquals(123, mediator.getInt("intOnlyInSettings", 123));
+        assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+        assertEquals(.7f, mediator.getFloat("float", .8f), 0.001);
+        assertEquals(.8f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+        assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+        assertEquals(false, mediator.getBoolean("boolean", false));
+        assertEquals(false, mediator.getBoolean("booleanOnlyInSettings", false));
+        assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+        assertEquals(60000001, mediator.getLong("long", 987654321));
+        assertEquals(987654321, mediator.getLong("longOnlyInSettings", 987654321));
+        assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+        assertEquals("xyz987", mediator.getString("string", "default"));
+        assertEquals("default", mediator.getString("stringOnlyInSettings", "default"));
+        assertEquals("deviceConfigString",
+                mediator.getString("stringOnlyInDeviceConfig", "default"));
+
+        // Nothing in settings, do DeviceConfig can be used.
+        mediator.setSettingsString(null);
+
+        assertEquals(10, mediator.getInt("int", 123));
+        assertEquals(123, mediator.getInt("intOnlyInSettings", 123));
+        assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+        assertEquals(.7f, mediator.getFloat("float", .8f), 0.001);
+        assertEquals(.8f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+        assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+        assertEquals(false, mediator.getBoolean("boolean", false));
+        assertEquals(false, mediator.getBoolean("booleanOnlyInSettings", false));
+        assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+        assertEquals(60000001, mediator.getLong("long", 987654321));
+        assertEquals(987654321, mediator.getLong("longOnlyInSettings", 987654321));
+        assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+        assertEquals("xyz987", mediator.getString("string", "default"));
+        assertEquals("default", mediator.getString("stringOnlyInSettings", "default"));
+        assertEquals("deviceConfigString",
+                mediator.getString("stringOnlyInDeviceConfig", "default"));
+    }
+
+    @Test
+    public void testSettingsOverridesIndividual() {
+        UserSettingDeviceConfigMediator mediator =
+                new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+        String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123,"
+                + "intOnlyInSettings=9,floatOnlyInSettings=.25f,booleanOnlyInSettings=true,"
+                + "longOnlyInSettings=53771465,stringOnlyInSettings=settingsString";
+        DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+                .setInt("int", 10)
+                .setInt("intOnlyInDeviceConfig", 9001)
+                .setFloat("float", .7f)
+                .setFloat("floatOnlyInDeviceConfig", .9f)
+                .setBoolean("boolean", false)
+                .setBoolean("booleanOnlyInDeviceConfig", true)
+                .setLong("long", 60000001)
+                .setLong("longOnlyInDeviceConfig", 7357)
+                .setString("string", "xyz987")
+                .setString("stringOnlyInDeviceConfig", "deviceConfigString")
+                .build();
+
+        mediator.setSettingsString(settings);
+        mediator.setDeviceConfigProperties(properties);
+
+        // Since settings overrides individual, anything in DeviceConfig that doesn't exist in
+        // settings should be used.
+        assertEquals(1, mediator.getInt("int", 123));
+        assertEquals(9, mediator.getInt("intOnlyInSettings", 123));
+        assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+        assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+        assertEquals(.25f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+        assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+        assertEquals(true, mediator.getBoolean("boolean", false));
+        assertEquals(true, mediator.getBoolean("booleanOnlyInSettings", false));
+        assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+        assertEquals(123456789, mediator.getLong("long", 987654321));
+        assertEquals(53771465, mediator.getLong("longOnlyInSettings", 987654321));
+        assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+        assertEquals("abc123", mediator.getString("string", "default"));
+        assertEquals("settingsString", mediator.getString("stringOnlyInSettings", "default"));
+        assertEquals("deviceConfigString",
+                mediator.getString("stringOnlyInDeviceConfig", "default"));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index d000605..32082e3 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -28,6 +28,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.test.suitebuilder.annotation.MediumTest;
@@ -1449,4 +1450,21 @@
         checkPreparationPhasesForPackage(currentSdkPackage.packageName,
                 1 /* first preparation phase */);
     }
+
+    @Test
+    @RequiresFlagsEnabled("android.webkit.update_service_v2")
+    public void testDefaultWebViewPackageIsTheFirstAvailableByDefault() {
+        String nonDefaultPackage = "nonDefaultPackage";
+        String defaultPackage1 = "defaultPackage1";
+        String defaultPackage2 = "defaultPackage2";
+        WebViewProviderInfo[] packages =
+                new WebViewProviderInfo[] {
+                    new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
+                    new WebViewProviderInfo(defaultPackage1, "", true, false, null),
+                    new WebViewProviderInfo(defaultPackage2, "", true, false, null)
+                };
+        setupWithPackages(packages);
+        assertEquals(
+                defaultPackage1, mWebViewUpdateServiceImpl.getDefaultWebViewPackage().packageName);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 09ffe71..ee08fd2 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -28,6 +28,7 @@
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_CAN_COLORIZE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
 import static android.app.Notification.FLAG_NO_CLEAR;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
 import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
@@ -320,6 +321,11 @@
     private static final int SECONDARY_DISPLAY_ID = 42;
     private static final int TEST_PROFILE_USERHANDLE = 12;
 
+    private static final String ACTION_NOTIFICATION_TIMEOUT =
+            NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
+    private static final String EXTRA_KEY = "key";
+    private static final String SCHEME_TIMEOUT = "timeout";
+
     private final int mUid = Binder.getCallingUid();
     private final @UserIdInt int mUserId = UserHandle.getUserId(mUid);
 
@@ -442,6 +448,7 @@
     MultiRateLimiter mToastRateLimiter;
     BroadcastReceiver mPackageIntentReceiver;
     BroadcastReceiver mUserSwitchIntentReceiver;
+    BroadcastReceiver mNotificationTimeoutReceiver;
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
     TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker;
 
@@ -677,6 +684,8 @@
         verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(),
                 any(), intentFilterCaptor.capture(), any(), any());
         verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(),
+                intentFilterCaptor.capture(), anyInt());
+        verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(),
                 intentFilterCaptor.capture());
         List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues();
         List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues();
@@ -695,9 +704,14 @@
                     mUserSwitchIntentReceiver = broadcastReceivers.get(i);
                 }
             }
+            if (filter.hasAction(ACTION_NOTIFICATION_TIMEOUT)
+                    && filter.hasDataScheme(SCHEME_TIMEOUT)) {
+                mNotificationTimeoutReceiver = broadcastReceivers.get(i);
+            }
         }
         assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
         assertNotNull("User-switch receiver should exist", mUserSwitchIntentReceiver);
+        assertNotNull("Notification timeout receiver should exist", mNotificationTimeoutReceiver);
 
         // Pretend the shortcut exists
         List<ShortcutInfo> shortcutInfos = new ArrayList<>();
@@ -2430,6 +2444,59 @@
     }
 
     @Test
+    public void testCancelWithTagDoesNotCancelLifetimeExtended() throws Exception {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+        final NotificationRecord notif = generateNotificationRecord(null);
+        notif.getSbn().getNotification().flags =
+                Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        mService.addNotification(notif);
+        final StatusBarNotification sbn = notif.getSbn();
+
+        assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(1);
+        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+
+        mBinderService.cancelNotificationWithTag(PKG, PKG, sbn.getTag(), sbn.getId(),
+                sbn.getUserId());
+        waitForIdle();
+
+        assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(1);
+        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+
+        mSetFlagsRule.disableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+        mBinderService.cancelNotificationWithTag(PKG, PKG, sbn.getTag(), sbn.getId(),
+                sbn.getUserId());
+        waitForIdle();
+
+        assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(0);
+        assertThat(mService.getNotificationRecordCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void testCancelAllDoesNotCancelLifetimeExtended() throws Exception {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+        // Adds a lifetime extended notification.
+        final NotificationRecord notif = generateNotificationRecord(mTestNotificationChannel, 1,
+                null, false);
+        notif.getSbn().getNotification().flags =
+                Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        mService.addNotification(notif);
+        // Adds a second, non-lifetime extended notification.
+        final NotificationRecord notifCancelable = generateNotificationRecord(
+                mTestNotificationChannel, 2, null, false);
+        mService.addNotification(notifCancelable);
+        // Verify that both notifications have been posted and are active.
+        assertThat(mBinderService.getActiveNotifications(PKG).length).isEqualTo(2);
+
+        mBinderService.cancelAllNotifications(PKG, notif.getSbn().getUserId());
+        waitForIdle();
+
+        // The non-lifetime extended notification, with id = 2, has been cancelled.
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+        assertThat(notifs.length).isEqualTo(1);
+        assertThat(notifs[0].getId()).isEqualTo(1);
+    }
+
+    @Test
     public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
             throws Exception {
         when(mAmi.applyForegroundServiceNotification(
@@ -2832,6 +2899,24 @@
     }
 
     @Test
+    public void testCancelNotificationsFromListener_clearAll_NoClearLifetimeExt()
+            throws Exception {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+
+        final NotificationRecord notif = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, false);
+        notif.getNotification().flags = FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        mService.addNotification(notif);
+
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
+        waitForIdle();
+
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
+        assertThat(notifs.length).isEqualTo(1);
+    }
+
+    @Test
     public void testCancelNotificationsFromListener_byKey_GroupWithOngoingParent()
             throws Exception {
         final NotificationRecord parent = generateNotificationRecord(
@@ -3036,6 +3121,22 @@
     }
 
     @Test
+    public void testCancelNotificationsFromListener_byKey_NoClearLifetimeExt()
+            throws Exception {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+        final NotificationRecord notif = generateNotificationRecord(
+                mTestNotificationChannel, 3, null, false);
+        notif.getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        mService.addNotification(notif);
+        String[] keys = {notif.getSbn().getKey()};
+        mService.getBinderService().cancelNotificationsFromListener(null, keys);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
     public void testGroupInstanceIds() throws Exception {
         final NotificationRecord group1 = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group1", true);
@@ -5298,6 +5399,79 @@
                 anyInt());
     }
 
+    private void simulateNotificationTimeoutBroadcast(String notificationKey) {
+        final Bundle extras = new Bundle();
+        extras.putString(EXTRA_KEY, notificationKey);
+        final Intent intent = new Intent(ACTION_NOTIFICATION_TIMEOUT);
+        intent.putExtras(extras);
+        mNotificationTimeoutReceiver.onReceive(getContext(), intent);
+    }
+
+    @Test
+    public void testTimeout_CancelsNotification() throws Exception {
+        final NotificationRecord notif = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, false);
+        mService.addNotification(notif);
+
+        simulateNotificationTimeoutBroadcast(notif.getKey());
+        waitForIdle();
+
+        // Check that the notification was cancelled.
+        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+        assertThat(notifsAfter.length).isEqualTo(0);
+        assertThat(mService.getNotificationRecord(notif.getKey())).isNull();
+    }
+
+    @Test
+    public void testTimeout_NoCancelForegroundServiceNotification() throws Exception {
+        // Creates a notification with FLAG_FOREGROUND_SERVICE
+        final NotificationRecord notif = generateNotificationRecord(null);
+        notif.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+        mService.addNotification(notif);
+
+        simulateNotificationTimeoutBroadcast(notif.getKey());
+        waitForIdle();
+
+        // Check that the notification was not cancelled.
+        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+        assertThat(notifsAfter.length).isEqualTo(1);
+        assertThat(mService.getNotificationRecord(notif.getKey())).isEqualTo(notif);
+    }
+
+    @Test
+    public void testTimeout_NoCancelUserInitJobNotification() throws Exception {
+        // Create a notification with FLAG_USER_INITIATED_JOB
+        final NotificationRecord notif = generateNotificationRecord(null);
+        notif.getSbn().getNotification().flags = Notification.FLAG_USER_INITIATED_JOB;
+        mService.addNotification(notif);
+
+        simulateNotificationTimeoutBroadcast(notif.getKey());
+        waitForIdle();
+
+        // Check that the notification was not cancelled.
+        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+        assertThat(notifsAfter.length).isEqualTo(1);
+        assertThat(mService.getNotificationRecord(notif.getKey())).isEqualTo(notif);
+    }
+
+    @Test
+    public void testTimeout_NoCancelLifetimeExtensionNotification() throws Exception {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+        // Create a notification with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY
+        final NotificationRecord notif = generateNotificationRecord(null);
+        notif.getSbn().getNotification().flags =
+                Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        mService.addNotification(notif);
+
+        simulateNotificationTimeoutBroadcast(notif.getKey());
+        waitForIdle();
+
+        // Check that the notification was not cancelled.
+        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+        assertThat(notifsAfter.length).isEqualTo(1);
+        assertThat(mService.getNotificationRecord(notif.getKey())).isEqualTo(notif);
+    }
+
     @Test
     public void testBumpFGImportance_channelChangePreOApp() throws Exception {
         String preOPkg = PKG_N_MR1;
@@ -7913,6 +8087,7 @@
 
     @Test
     public void testOnNotificationSmartReplySent() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
         final int replyIndex = 2;
         final String reply = "Hello";
         final boolean modifiedBeforeSending = true;
@@ -7930,6 +8105,10 @@
         assertEquals(1, mNotificationRecordLogger.numCalls());
         assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
                 mNotificationRecordLogger.event(0));
+        // Check that r.recordSmartReplied was called.
+        assertThat(r.getSbn().getNotification().flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY)
+                .isGreaterThan(0);
+        assertThat(r.getStats().hasSmartReplied()).isTrue();
     }
 
     @Test
@@ -13116,6 +13295,20 @@
                 eq("package"), anyString(), anyInt(), anyBoolean());
     }
 
+    @Test
+    public void testFixNotification_clearsLifetimeExtendedFlag() throws Exception {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+        Notification n = new Notification.Builder(mContext, "test")
+                .setFlag(FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true)
+                .build();
+
+        assertThat(n.flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isGreaterThan(0);
+
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true);
+
+        assertThat(n.flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
+    }
+
     private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
             throws RemoteException {
         StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index f83a1df..670d097 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -29,6 +29,8 @@
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -44,6 +46,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.Flags;
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.app.NotificationChannel;
@@ -64,6 +67,7 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.Vibrator;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
@@ -80,6 +84,7 @@
 import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -122,6 +127,9 @@
     private static final NotificationRecord.Light CUSTOM_LIGHT =
             new NotificationRecord.Light(1, 2, 3);
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -651,6 +659,7 @@
 
     @Test
     public void testNotificationStats() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
         StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, groupId /* group */);
@@ -690,6 +699,37 @@
 
         record.recordDirectReplied();
         assertTrue(record.getStats().hasDirectReplied());
+
+        record.recordSmartReplied();
+        assertThat(record.getStats().hasSmartReplied()).isTrue();
+    }
+
+    @Test
+    public void testDirectRepliedAddsLifetimeExtensionFlag() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        record.recordDirectReplied();
+        assertThat(record.getSbn().getNotification().flags
+                & Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isGreaterThan(0);
+    }
+
+    @Test
+    public void testSmartRepliedAddsLifetimeExtensionFlag() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        record.recordSmartReplied();
+        assertThat(record.getSbn().getNotification().flags
+                & Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isGreaterThan(0);
     }
 
     @Test
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f2a1fe8..c3074bb 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -93,8 +93,6 @@
                   android:showWhenLocked="true"/>
         <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
 
-        <activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" />
-
         <activity android:name="com.android.server.wm.SurfaceSyncGroupTests$TestActivity"
             android:screenOrientation="locked"
             android:turnScreenOn="true"
@@ -122,6 +120,13 @@
         <activity android:name="com.android.server.wm.ActivityRecordInputSinkTests$TestActivity"
                   android:exported="true">
         </activity>
+
+        <activity android:name="com.android.server.wm.utils.TestActivity"
+            android:screenOrientation="locked"
+            android:turnScreenOn="true"
+            android:showWhenLocked="true"
+            android:theme="@style/WhiteBackgroundTheme"
+            android:exported="true" />
     </application>
 
     <instrumentation
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
index f8ebead..46e87dc 100644
--- a/services/tests/wmtests/AndroidTest.xml
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -30,4 +30,8 @@
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false" />
     </test>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" />
+    </target_preparer>
 </configuration>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 2c35cf0..8d236ed 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -110,24 +110,6 @@
     }
 
     /**
-     * META + SPACE to switch keyboard layout.
-     */
-    @Test
-    public void testMetaSpace() {
-        sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SPACE}, 0);
-        mPhoneWindowManager.assertSwitchKeyboardLayout(1);
-    }
-
-    /**
-     * META + SHIFT + SPACE to switch keyboard layout backwards.
-     */
-    @Test
-    public void testMetaShiftSpace() {
-        sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, 0);
-        mPhoneWindowManager.assertSwitchKeyboardLayout(-1);
-    }
-
-    /**
      * CTRL + ALT + Z to enable accessibility service.
      */
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 71098aa..2d7f044 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -30,13 +30,13 @@
 import com.android.internal.annotations.Keep;
 import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
 @Presubmit
 @MediumTest
 @RunWith(JUnitParamsRunner.class)
@@ -71,8 +71,8 @@
                         KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, ALT_ON},
                 {"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK}, KeyboardLogEvent.BACK,
                         KeyEvent.KEYCODE_BACK, 0},
-                {"Meta + `(grave) -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_GRAVE},
-                        KeyboardLogEvent.BACK, KeyEvent.KEYCODE_GRAVE, META_ON},
+                {"Meta + Escape -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_ESCAPE},
+                        KeyboardLogEvent.BACK, KeyEvent.KEYCODE_ESCAPE, META_ON},
                 {"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
                         KeyboardLogEvent.BACK, KeyEvent.KEYCODE_DPAD_LEFT, META_ON},
                 {"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
@@ -132,13 +132,6 @@
                 {"LANGUAGE_SWITCH key -> Switch Keyboard Language",
                         new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
                         KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
-                {"Meta + Space -> Switch Keyboard Language",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_SPACE},
-                        KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE, META_ON},
-                {"Meta + Shift + Space -> Switch Keyboard Language",
-                        new int[]{META_KEY, SHIFT_KEY, KeyEvent.KEYCODE_SPACE},
-                        KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE,
-                        META_ON | SHIFT_ON},
                 {"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
                         KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, META_KEY, META_ON},
                 {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index a831f6a..9efbe35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -25,8 +25,6 @@
 import static com.android.server.wm.utils.LastCallVerifier.lastCall;
 
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,11 +34,14 @@
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
 import com.android.server.testutils.StubTransaction;
 import com.android.server.wm.utils.MockAnimationAdapter;
+import com.android.window.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -176,8 +177,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() {
-        assumeTrue(Dimmer.DIMMER_REFACTOR);
         final float alpha = 0.7f;
         final int blur = 50;
         mHost.addChild(mChild, 0);
@@ -196,8 +197,8 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Legacy() {
-        assumeFalse(Dimmer.DIMMER_REFACTOR);
         final float alpha = 0.7f;
         mHost.addChild(mChild, 0);
         mDimmer.adjustAppearance(mChild, alpha, 20);
@@ -211,8 +212,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testDimBelowWithChildSurfaceDestroyedWhenReset_Smooth() {
-        assumeTrue(Dimmer.DIMMER_REFACTOR);
         mHost.addChild(mChild, 0);
 
         final float alpha = 0.8f;
@@ -231,8 +232,8 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testDimBelowWithChildSurfaceDestroyedWhenReset_Legacy() {
-        assumeFalse(Dimmer.DIMMER_REFACTOR);
         mHost.addChild(mChild, 0);
 
         final float alpha = 0.8f;
@@ -291,8 +292,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testRemoveDimImmediately_Smooth() {
-        assumeTrue(Dimmer.DIMMER_REFACTOR);
         mHost.addChild(mChild, 0);
         mDimmer.adjustAppearance(mChild, 1, 2);
         mDimmer.adjustRelativeLayer(mChild, -1);
@@ -311,8 +312,8 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testRemoveDimImmediately_Legacy() {
-        assumeFalse(Dimmer.DIMMER_REFACTOR);
         mHost.addChild(mChild, 0);
         mDimmer.adjustAppearance(mChild, 1, 0);
         mDimmer.adjustRelativeLayer(mChild, -1);
@@ -331,8 +332,8 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testDimmerWithBlurUpdatesTransaction_Legacy() {
-        assumeFalse(Dimmer.DIMMER_REFACTOR);
         mHost.addChild(mChild, 0);
 
         final int blurRadius = 50;
@@ -345,4 +346,120 @@
         verify(mHost.getPendingTransaction()).setBackgroundBlurRadius(dimLayer, blurRadius);
         verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
     }
+
+    /**
+     * mChild is requesting the dim values to be set directly. In this case, dim won't play the
+     * standard animation, but directly apply mChild's requests to the dim surface
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+    public void testContainerDimsOpeningAnimationByItself() {
+        mHost.addChild(mChild, 0);
+
+        mDimmer.resetDimStates();
+        mDimmer.adjustAppearance(mChild, 0.1f, 0);
+        mDimmer.adjustRelativeLayer(mChild, -1);
+        SurfaceControl dimLayer = mDimmer.getDimLayer();
+        mDimmer.updateDims(mTransaction);
+
+        mDimmer.resetDimStates();
+        mDimmer.adjustAppearance(mChild, 0.2f, 0);
+        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.updateDims(mTransaction);
+
+        mDimmer.resetDimStates();
+        mDimmer.adjustAppearance(mChild, 0.3f, 0);
+        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.updateDims(mTransaction);
+
+        verify(mTransaction).setAlpha(dimLayer, 0.2f);
+        verify(mTransaction).setAlpha(dimLayer, 0.3f);
+        verify(sTestAnimation, times(1)).startAnimation(
+                any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+                anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+    }
+
+    /**
+     * Same as testContainerDimsOpeningAnimationByItself, but this is a more specific case in which
+     * alpha is animated to 0. This corner case is needed to verify that the layer is removed anyway
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+    public void testContainerDimsClosingAnimationByItself() {
+        mHost.addChild(mChild, 0);
+
+        mDimmer.resetDimStates();
+        mDimmer.adjustAppearance(mChild, 0.2f, 0);
+        mDimmer.adjustRelativeLayer(mChild, -1);
+        SurfaceControl dimLayer = mDimmer.getDimLayer();
+        mDimmer.updateDims(mTransaction);
+
+        mDimmer.resetDimStates();
+        mDimmer.adjustAppearance(mChild, 0.1f, 0);
+        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.updateDims(mTransaction);
+
+        mDimmer.resetDimStates();
+        mDimmer.adjustAppearance(mChild, 0f, 0);
+        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.updateDims(mTransaction);
+
+        mDimmer.resetDimStates();
+        mDimmer.updateDims(mTransaction);
+        verify(mTransaction).remove(dimLayer);
+    }
+
+    /**
+     * Check the handover of the dim between two windows and the consequent dim animation in between
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+    public void testMultipleContainersDimmingConsecutively() {
+        TestWindowContainer first = mChild;
+        TestWindowContainer second = new TestWindowContainer(mWm);
+        mHost.addChild(first, 0);
+        mHost.addChild(second, 1);
+
+        mDimmer.adjustAppearance(first, 0.5f, 0);
+        mDimmer.adjustRelativeLayer(first, -1);
+        SurfaceControl dimLayer = mDimmer.getDimLayer();
+        mDimmer.updateDims(mTransaction);
+
+        mDimmer.resetDimStates();
+        mDimmer.adjustAppearance(second, 0.9f, 0);
+        mDimmer.adjustRelativeLayer(second, -1);
+        mDimmer.updateDims(mTransaction);
+
+        verify(sTestAnimation, times(2)).startAnimation(
+                any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+                anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+        verify(mTransaction).setAlpha(dimLayer, 0.5f);
+        verify(mTransaction).setAlpha(dimLayer, 0.9f);
+    }
+
+    /**
+     * Two windows are trying to modify the dim at the same time, but only the last request before
+     * updateDims will be satisfied
+     */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+    public void testMultipleContainersDimmingAtTheSameTime() {
+        TestWindowContainer first = mChild;
+        TestWindowContainer second = new TestWindowContainer(mWm);
+        mHost.addChild(first, 0);
+        mHost.addChild(second, 1);
+
+        mDimmer.adjustAppearance(first, 0.5f, 0);
+        mDimmer.adjustRelativeLayer(first, -1);
+        SurfaceControl dimLayer = mDimmer.getDimLayer();
+        mDimmer.adjustAppearance(second, 0.9f, 0);
+        mDimmer.adjustRelativeLayer(second, -1);
+        mDimmer.updateDims(mTransaction);
+
+        verify(sTestAnimation, times(1)).startAnimation(
+                any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+                anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+        verify(mTransaction, never()).setAlpha(dimLayer, 0.5f);
+        verify(mTransaction).setAlpha(dimLayer, 0.9f);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index 8119fd4..3b9ed26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -23,15 +23,14 @@
 
 import static org.junit.Assert.assertTrue;
 
-import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
-import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
 import android.view.Gravity;
 import android.view.IWindow;
 import android.view.SurfaceControl;
@@ -46,12 +45,12 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.server.wm.utils.CommonUtils;
+import com.android.server.wm.utils.TestActivity;
 
 import org.junit.After;
 import org.junit.Before;
@@ -59,11 +58,14 @@
 import org.junit.runner.RunWith;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 @Presubmit
 @SmallTest
 @RunWith(WindowTestRunner.class)
 public class SurfaceControlViewHostTests {
+    private static final long WAIT_TIME_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
+
     private static final String TAG = "SurfaceControlViewHostTests";
 
     private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
@@ -76,6 +78,8 @@
     private SurfaceControlViewHost mScvh1;
     private SurfaceControlViewHost mScvh2;
 
+    private SurfaceView mSurfaceView;
+
     @Before
     public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -96,15 +100,17 @@
         mView1 = new Button(mActivity);
         mView2 = new Button(mActivity);
 
-        mInstrumentation.runOnMainSync(() -> {
-            try {
-                mActivity.attachToSurfaceView(sc);
-            } catch (InterruptedException e) {
-            }
+        CountDownLatch svReadyLatch = new CountDownLatch(1);
+        mActivity.runOnUiThread(() -> addSurfaceView(svReadyLatch));
+        assertTrue("Failed to wait for SV to get created",
+                svReadyLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
+        new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
+                .show(sc).apply();
 
+        mInstrumentation.runOnMainSync(() -> {
             TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
                     mActivity.getResources().getConfiguration(), sc,
-                    mActivity.mSurfaceView.getHostToken());
+                    mSurfaceView.getHostToken());
 
             mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
                     wwm, "requestFocusWithMultipleWindows");
@@ -135,7 +141,7 @@
         }
         assertTrue("Failed to wait for view2", wasVisible);
 
-        IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken());
+        IWindow window = IWindow.Stub.asInterface(mSurfaceView.getWindowToken());
 
         WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
                 mScvh1.getInputTransferToken(), true);
@@ -162,43 +168,30 @@
         }
     }
 
-    public static class TestActivity extends Activity implements SurfaceHolder.Callback {
-        private SurfaceView mSurfaceView;
-        private final CountDownLatch mSvReadyLatch = new CountDownLatch(1);
+    private void addSurfaceView(CountDownLatch svReadyLatch) {
+        final FrameLayout content = mActivity.getParentLayout();
+        mSurfaceView = new SurfaceView(mActivity);
+        mSurfaceView.setZOrderOnTop(true);
+        final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
+                Gravity.LEFT | Gravity.TOP);
+        mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceCreated(@NonNull SurfaceHolder holder) {
+                svReadyLatch.countDown();
+            }
 
-        @Override
-        protected void onCreate(@Nullable Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            final FrameLayout content = new FrameLayout(this);
-            mSurfaceView = new SurfaceView(this);
-            mSurfaceView.setBackgroundColor(Color.BLACK);
-            mSurfaceView.setZOrderOnTop(true);
-            final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
-                    Gravity.LEFT | Gravity.TOP);
-            content.addView(mSurfaceView, lp);
-            setContentView(content);
-            mSurfaceView.getHolder().addCallback(this);
-        }
+            @Override
+            public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+                    int height) {
 
-        @Override
-        public void surfaceCreated(@NonNull SurfaceHolder holder) {
-            mSvReadyLatch.countDown();
-        }
+            }
 
-        @Override
-        public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
-                int height) {
-        }
+            @Override
+            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
 
-        @Override
-        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
-        }
-
-        public void attachToSurfaceView(SurfaceControl sc) throws InterruptedException {
-            mSvReadyLatch.await();
-            new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
-                    .show(sc).apply();
-        }
+            }
+        });
+        content.addView(mSurfaceView, lp);
     }
 }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index 3cb4a1d..e65a9fe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -19,6 +19,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
 import android.os.Handler;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.DexmakerShareClassLoaderRule;
 
@@ -39,6 +41,9 @@
     public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule(
             this::onBeforeSystemServicesCreated);
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @WindowTestRunner.MethodWrapperRule
     public final WindowManagerGlobalLockRule mLockRule =
             new WindowManagerGlobalLockRule(mSystemServicesTestRule);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 8a90f12..06f29c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -25,7 +25,9 @@
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE;
 import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
@@ -1759,6 +1761,40 @@
     }
 
     @Test
+    public void testApplyTransaction_createTaskFragmentDecorSurface() {
+        // TODO(b/293654166) remove system organizer requirement once security review is cleared.
+        mController.unregisterOrganizer(mIOrganizer);
+        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        final Task task = createTask(mDisplayContent);
+
+        final TaskFragment tf = createTaskFragment(task);
+        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+                OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE).build();
+        mTransaction.addTaskFragmentOperation(tf.getFragmentToken(), operation);
+
+        assertApplyTransactionAllowed(mTransaction);
+
+        verify(task).moveOrCreateDecorSurfaceFor(tf);
+    }
+
+    @Test
+    public void testApplyTransaction_removeTaskFragmentDecorSurface() {
+        // TODO(b/293654166) remove system organizer requirement once security review is cleared.
+        mController.unregisterOrganizer(mIOrganizer);
+        mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+        final Task task = createTask(mDisplayContent);
+        final TaskFragment tf = createTaskFragment(task);
+
+        final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+                OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE).build();
+        mTransaction.addTaskFragmentOperation(tf.getFragmentToken(), operation);
+
+        assertApplyTransactionAllowed(mTransaction);
+
+        verify(task).removeDecorSurface();
+    }
+
+    @Test
     public void testApplyTransaction_reorderToBottomOfTask_failsIfNotSystemOrganizer() {
         testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
                 OP_TYPE_REORDER_TO_BOTTOM_OF_TASK);
@@ -1966,7 +2002,8 @@
     /** Setups the mock Task as the parent of the given TaskFragment. */
     private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
         doReturn(mockParent).when(taskFragment).getTask();
-        doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true, true))
+        doReturn(new TaskFragmentParentInfo(
+                new Configuration(), DEFAULT_DISPLAY, true, true, null /* decorSurface */))
                 .when(mockParent).getTaskFragmentParentInfo();
 
         // Task needs to be visible
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 5e531b4..da7612b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -63,6 +63,7 @@
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 
 import android.app.ActivityManager;
@@ -82,6 +83,7 @@
 import android.util.Xml;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.SurfaceControl;
 import android.window.TaskFragmentOrganizer;
 
 import androidx.test.filters.MediumTest;
@@ -1619,6 +1621,185 @@
         assertFalse(task.isDragResizing());
     }
 
+    @Test
+    public void testMoveOrCreateDecorSurface() {
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final Task task =  new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+        final ActivityRecord activity = task.getTopMostActivity();
+        final TaskFragment fragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+
+        // Decor surface should not be present initially.
+        assertNull(task.mDecorSurfaceContainer);
+        assertNull(task.getDecorSurface());
+        assertNull(task.getTaskFragmentParentInfo().getDecorSurface());
+
+        // Decor surface should be created.
+        clearInvocations(task);
+        task.moveOrCreateDecorSurfaceFor(fragment);
+
+        assertNotNull(task.mDecorSurfaceContainer);
+        assertNotNull(task.getDecorSurface());
+        verify(task).sendTaskFragmentParentInfoChangedIfNeeded();
+        assertNotNull(task.getTaskFragmentParentInfo().getDecorSurface());
+        assertEquals(fragment, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+        // Decor surface should be removed.
+        clearInvocations(task);
+        task.removeDecorSurface();
+
+        assertNull(task.mDecorSurfaceContainer);
+        assertNull(task.getDecorSurface());
+        verify(task).sendTaskFragmentParentInfoChangedIfNeeded();
+        assertNull(task.getTaskFragmentParentInfo().getDecorSurface());
+    }
+
+    @Test
+    public void testMoveOrCreateDecorSurface_whenOwnerTaskFragmentRemoved() {
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final Task task =  new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+        final ActivityRecord activity = task.getTopMostActivity();
+        final TaskFragment fragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+
+        task.moveOrCreateDecorSurfaceFor(fragment1);
+
+        assertNotNull(task.mDecorSurfaceContainer);
+        assertNotNull(task.getDecorSurface());
+        assertEquals(fragment1, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+        // Transfer ownership
+        task.moveOrCreateDecorSurfaceFor(fragment2);
+
+        assertNotNull(task.mDecorSurfaceContainer);
+        assertNotNull(task.getDecorSurface());
+        assertEquals(fragment2, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+        // Safe surface should be removed when the owner TaskFragment is removed.
+        task.removeChild(fragment2);
+
+        verify(task).removeDecorSurface();
+        assertNull(task.mDecorSurfaceContainer);
+        assertNull(task.getDecorSurface());
+    }
+
+    @Test
+    public void testAssignChildLayers_decorSurfacePlacement() {
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final Task task =  new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+        final ActivityRecord unembeddedActivity = task.getTopMostActivity();
+
+        final TaskFragment fragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+
+        doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+        spyOn(unembeddedActivity);
+        spyOn(fragment1);
+        spyOn(fragment2);
+
+        // Initially, the decor surface should not be placed.
+        task.assignChildLayers(t);
+        verify(unembeddedActivity).assignLayer(t, 0);
+        verify(fragment1).assignLayer(t, 1);
+        verify(fragment2).assignLayer(t, 2);
+
+        clearInvocations(t);
+        clearInvocations(unembeddedActivity);
+        clearInvocations(fragment1);
+        clearInvocations(fragment2);
+
+        // The decor surface should be placed just above the owner TaskFragment.
+        doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+        doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+        doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+        doReturn(true).when(fragment1).isVisible();
+
+        task.moveOrCreateDecorSurfaceFor(fragment1);
+        task.assignChildLayers(t);
+
+        verify(unembeddedActivity).assignLayer(t, 0);
+        verify(fragment1).assignLayer(t, 1);
+        verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+        verify(fragment2).assignLayer(t, 3);
+        verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+        verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+        clearInvocations(t);
+        clearInvocations(unembeddedActivity);
+        clearInvocations(fragment1);
+        clearInvocations(fragment2);
+
+        // The decor surface should be invisible if the owner TaskFragment is invisible.
+        doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+        doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+        doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+        doReturn(false).when(fragment1).isVisible();
+
+        task.assignChildLayers(t);
+
+        verify(unembeddedActivity).assignLayer(t, 0);
+        verify(fragment1).assignLayer(t, 1);
+        verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+        verify(fragment2).assignLayer(t, 3);
+        verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, false);
+        verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+        clearInvocations(t);
+        clearInvocations(unembeddedActivity);
+        clearInvocations(fragment1);
+        clearInvocations(fragment2);
+
+        // The decor surface should be placed on below activity from a different UID.
+        doReturn(false).when(unembeddedActivity).isUid(task.effectiveUid);
+        doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+        doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+        doReturn(true).when(fragment1).isVisible();
+
+        task.assignChildLayers(t);
+
+        verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 0);
+        verify(unembeddedActivity).assignLayer(t, 1);
+        verify(fragment1).assignLayer(t, 2);
+        verify(fragment2).assignLayer(t, 3);
+        verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+        verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+        clearInvocations(t);
+        clearInvocations(unembeddedActivity);
+        clearInvocations(fragment1);
+        clearInvocations(fragment2);
+
+        // The decor surface should be placed below untrusted embedded TaskFragment.
+        doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+        doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+        doReturn(false).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+        doReturn(true).when(fragment1).isVisible();
+
+        task.assignChildLayers(t);
+
+        verify(unembeddedActivity).assignLayer(t, 0);
+        verify(fragment1).assignLayer(t, 1);
+        verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+        verify(fragment2).assignLayer(t, 3);
+        verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+        verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+        clearInvocations(t);
+        clearInvocations(unembeddedActivity);
+        clearInvocations(fragment1);
+        clearInvocations(fragment2);
+
+        // The decor surface should not be placed after removal.
+        task.removeDecorSurface();
+        task.assignChildLayers(t);
+
+        verify(unembeddedActivity).assignLayer(t, 0);
+        verify(fragment1).assignLayer(t, 1);
+        verify(fragment2).assignLayer(t, 2);
+    }
+
     private Task getTestTask() {
         return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index dade3b9..71447e7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2015,6 +2015,9 @@
         transition.collect(leafTaskA);
         rootTaskA.moveToFront("test", leafTaskA);
 
+        // Test has order changes, a shallow check of order changes
+        assertTrue(transition.hasOrderChanges());
+
         // All the tasks were already visible, so there shouldn't be any changes
         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
                 participants, changes);
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java b/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java
new file mode 100644
index 0000000..c12dcdd
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 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.utils;
+
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.os.Bundle;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * TestActivity that will ensure it dismisses keyguard and shows as a fullscreen activity.
+ */
+public class TestActivity extends Activity {
+    private static final int sTypeMask = systemBars() | displayCutout();
+    private FrameLayout mParentLayout;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mParentLayout = new FrameLayout(this);
+        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT);
+        setContentView(mParentLayout, layoutParams);
+
+        WindowInsetsController windowInsetsController = getWindow().getInsetsController();
+        windowInsetsController.hide(sTypeMask);
+        WindowManager.LayoutParams params = getWindow().getAttributes();
+        params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        getWindow().setAttributes(params);
+        getWindow().setDecorFitsSystemWindows(false);
+
+        final KeyguardManager keyguardManager = getInstrumentation().getContext().getSystemService(
+                KeyguardManager.class);
+        if (keyguardManager != null && keyguardManager.isKeyguardLocked()) {
+            keyguardManager.requestDismissKeyguard(this, null);
+        }
+    }
+
+    public FrameLayout getParentLayout() {
+        return mParentLayout;
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index bfb159f..dce4818 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -31,6 +31,7 @@
 import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET;
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
 import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
 import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
 import static android.app.usage.UsageEvents.Event.SCREEN_NON_INTERACTIVE;
 import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
@@ -42,7 +43,9 @@
 import android.app.usage.EventStats;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
 import android.content.res.Configuration;
+import android.os.PersistableBundle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -575,6 +578,23 @@
                         continue;
                     }
                     break;
+                case USER_INTERACTION:
+                    if (event.mUserInteractionExtrasToken != null) {
+                        String category = packagesTokenData.getString(packageToken,
+                                event.mUserInteractionExtrasToken.mCategoryToken);
+                        String action = packagesTokenData.getString(packageToken,
+                                event.mUserInteractionExtrasToken.mActionToken);
+                        if (TextUtils.isEmpty(category) || TextUtils.isEmpty(action)) {
+                            this.events.remove(i);
+                            dataOmitted = true;
+                            continue;
+                        }
+                        event.mExtras = new PersistableBundle();
+                        event.mExtras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, category);
+                        event.mExtras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, action);
+                        event.mUserInteractionExtrasToken = null;
+                    }
+                    break;
             }
         }
         if (dataOmitted) {
@@ -692,13 +712,30 @@
                                 event.mPackage, event.mLocusId);
                     }
                     break;
+                case USER_INTERACTION:
+                    if (event.mExtras != null && event.mExtras.size() != 0) {
+                        final String category = event.mExtras.getString(
+                                UsageStatsManager.EXTRA_EVENT_CATEGORY);
+                        final String action = event.mExtras.getString(
+                                UsageStatsManager.EXTRA_EVENT_ACTION);
+                        if (!TextUtils.isEmpty(category) && !TextUtils.isEmpty(action)) {
+                            event.mUserInteractionExtrasToken =
+                                    new Event.UserInteractionEventExtrasToken();
+                            event.mUserInteractionExtrasToken.mCategoryToken =
+                                    packagesTokenData.getTokenOrAdd(packageToken, event.mPackage,
+                                            category);
+                            event.mUserInteractionExtrasToken.mActionToken =
+                                    packagesTokenData.getTokenOrAdd(packageToken, event.mPackage,
+                                            action);
+                        }
+                    }
+                    break;
             }
         }
     }
 
     /**
      * Obfuscates the data in this instance of interval stats.
-     *
      * @hide
      */
     public void obfuscateData(PackagesTokenData packagesTokenData) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index 8138747..d865345 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -17,8 +17,10 @@
 
 import android.app.usage.ConfigurationStats;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event.UserInteractionEventExtrasToken;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
+import android.os.PersistableBundle;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -26,6 +28,8 @@
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -282,6 +286,16 @@
                     event.mLocusIdToken = proto.readInt(
                             EventObfuscatedProto.LOCUS_ID_TOKEN) - 1;
                     break;
+                case (int) EventObfuscatedProto.INTERACTION_EXTRAS:
+                    try {
+                        final long interactionExtrasToken = proto.start(
+                                EventObfuscatedProto.INTERACTION_EXTRAS);
+                        event.mUserInteractionExtrasToken = parseUserInteractionEventExtras(proto);
+                        proto.end(interactionExtrasToken);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Unable to read some user interaction extras from proto.", e);
+                    }
+                    break;
                 case ProtoInputStream.NO_MORE_FIELDS:
                     return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event;
             }
@@ -386,7 +400,7 @@
     }
 
     private static void writeEvent(ProtoOutputStream proto, final long statsBeginTime,
-            final UsageEvents.Event event) throws IllegalArgumentException {
+            final UsageEvents.Event event) throws IOException, IllegalArgumentException {
         proto.write(EventObfuscatedProto.PACKAGE_TOKEN, event.mPackageToken + 1);
         if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
             proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
@@ -429,6 +443,12 @@
                             event.mNotificationChannelIdToken + 1);
                 }
                 break;
+            case UsageEvents.Event.USER_INTERACTION:
+                if (event.mUserInteractionExtrasToken != null) {
+                    writeUserInteractionEventExtras(proto, EventObfuscatedProto.INTERACTION_EXTRAS,
+                            event.mUserInteractionExtrasToken);
+                }
+                break;
         }
     }
 
@@ -703,6 +723,9 @@
                 case (int) PendingEventProto.TASK_ROOT_CLASS:
                     event.mTaskRootClass = proto.readString(PendingEventProto.TASK_ROOT_CLASS);
                     break;
+                case (int) PendingEventProto.EXTRAS:
+                    event.mExtras = parsePendingEventExtras(proto, PendingEventProto.EXTRAS);
+                    break;
                 case ProtoInputStream.NO_MORE_FIELDS:
                     // Handle default values for certain events types
                     switch (event.mEventType) {
@@ -757,7 +780,7 @@
     }
 
     private static void writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event)
-            throws IllegalArgumentException {
+            throws IOException, IllegalArgumentException {
         proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage);
         if (event.mClass != null) {
             proto.write(PendingEventProto.CLASS_NAME, event.mClass);
@@ -794,6 +817,11 @@
                             event.mNotificationChannelId);
                 }
                 break;
+            case UsageEvents.Event.USER_INTERACTION:
+                if (event.mExtras != null && event.mExtras.size() != 0) {
+                    writePendingEventExtras(proto, PendingEventProto.EXTRAS, event.mExtras);
+                }
+                break;
         }
     }
 
@@ -888,4 +916,52 @@
             proto.end(token);
         }
     }
+
+    private static UserInteractionEventExtrasToken parseUserInteractionEventExtras(
+            ProtoInputStream proto) throws IOException {
+        UserInteractionEventExtrasToken interactionExtrasToken =
+                new UserInteractionEventExtrasToken();
+        while (true) {
+            switch (proto.nextField()) {
+                case (int) ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN:
+                    interactionExtrasToken.mCategoryToken = proto.readInt(
+                            ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN) - 1;
+                    break;
+                case (int) ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN:
+                    interactionExtrasToken.mActionToken = proto.readInt(
+                            ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN) - 1;
+                    break;
+                case ProtoInputStream.NO_MORE_FIELDS:
+                    return interactionExtrasToken;
+            }
+        }
+    }
+
+    static void writeUserInteractionEventExtras(ProtoOutputStream proto, long fieldId,
+            UserInteractionEventExtrasToken interactionExtras) {
+        final long token = proto.start(fieldId);
+        proto.write(ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN,
+                interactionExtras.mCategoryToken + 1);
+        proto.write(ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN,
+                interactionExtras.mActionToken + 1);
+        proto.end(token);
+    }
+
+    /**
+     * Populates the extra details for pending interaction event from the protobuf stream.
+     */
+    private static PersistableBundle parsePendingEventExtras(ProtoInputStream proto, long fieldId)
+            throws IOException {
+        return PersistableBundle.readFromStream(new ByteArrayInputStream(proto.readBytes(fieldId)));
+    }
+
+    /**
+     * Write the extra details for pending interaction event to a protobuf stream.
+     */
+    static void writePendingEventExtras(ProtoOutputStream proto, long fieldId,
+            PersistableBundle eventExtras) throws IOException {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        eventExtras.writeToStream(baos);
+        proto.write(fieldId, baos.toByteArray());
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4c56f33..0e1e0c8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -83,6 +83,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -91,6 +92,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -193,6 +195,11 @@
 
     private static final char TOKEN_DELIMITER = '/';
 
+    // The maximum length for extras {@link UsageStatsManager#EXTRA_EVENT_CATEGORY},
+    // {@link UsageStatsManager#EXTRA_EVENT_ACTION} in a {@link UsageEvents.Event#mExtras}.
+    // The value will be truncated at this limit.
+    private static final int MAX_TEXT_LENGTH = 127;
+
     // Handler message types.
     static final int MSG_REPORT_EVENT = 0;
     static final int MSG_FLUSH_TO_DISK = 1;
@@ -1814,6 +1821,13 @@
         mHandler.removeMessages(MSG_FLUSH_TO_DISK);
     }
 
+    private String getTrimmedString(String input) {
+        if (input != null && input.length() > MAX_TEXT_LENGTH) {
+            return input.substring(0, MAX_TEXT_LENGTH);
+        }
+        return input;
+    }
+
     /**
      * Called by the Binder stub.
      */
@@ -2253,6 +2267,32 @@
             }
         }
 
+        private void reportUserInteractionInnerHelper(String packageName, @UserIdInt int userId,
+                PersistableBundle extras) {
+            if (Flags.reportUsageStatsPermission()) {
+                if (!canReportUsageStats()) {
+                    throw new SecurityException(
+                        "Only the system or holders of the REPORT_USAGE_STATS"
+                            + " permission are allowed to call reportUserInteraction");
+                }
+            } else {
+                if (!isCallingUidSystem()) {
+                    throw new SecurityException("Only system is allowed to call"
+                        + " reportUserInteraction");
+                }
+            }
+
+            // Verify if this package exists before reporting an event for it.
+            if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) {
+                throw new IllegalArgumentException("Package " + packageName + "not exist!");
+            }
+
+            final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
+            event.mPackage = packageName;
+            event.mExtras = extras;
+            reportEventOrAddToQueue(userId, event);
+        }
+
         @Override
         public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
                 long endTime, String callingPackage, int userId) {
@@ -2686,23 +2726,36 @@
 
         @Override
         public void reportUserInteraction(String packageName, int userId) {
+            reportUserInteractionInnerHelper(packageName, userId, null);
+        }
+
+        @Override
+        public void reportUserInteractionWithBundle(String packageName, @UserIdInt int userId,
+                PersistableBundle extras) {
             Objects.requireNonNull(packageName);
-            if (Flags.reportUsageStatsPermission()) {
-                if (!canReportUsageStats()) {
-                    throw new SecurityException(
-                        "Only the system or holders of the REPORT_USAGE_STATS"
-                            + " permission are allowed to call reportUserInteraction");
-                }
-            } else {
-                if (!isCallingUidSystem()) {
-                    throw new SecurityException("Only system is allowed to call"
-                            + " reportUserInteraction");
-                }
+            if (extras == null || extras.size() == 0) {
+                throw new IllegalArgumentException("Emtry extras!");
             }
 
-            final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
-            event.mPackage = packageName;
-            reportEventOrAddToQueue(userId, event);
+            // Only category/action are allowed now, other unknown keys will be trimmed.
+            // Also, empty category/action is not meanful.
+            String category = extras.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY);
+            if (TextUtils.isEmpty(category)) {
+                throw new IllegalArgumentException("Empty "
+                        + UsageStatsManager.EXTRA_EVENT_CATEGORY);
+            }
+            String action = extras.getString(UsageStatsManager.EXTRA_EVENT_ACTION);
+            if (TextUtils.isEmpty(action)) {
+                throw new IllegalArgumentException("Empty "
+                        + UsageStatsManager.EXTRA_EVENT_ACTION);
+            }
+
+            PersistableBundle extrasCopy = new PersistableBundle();
+            extrasCopy.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+                    getTrimmedString(category));
+            extrasCopy.putString(UsageStatsManager.EXTRA_EVENT_ACTION, getTrimmedString(action));
+
+            reportUserInteractionInnerHelper(packageName, userId, extrasCopy);
         }
 
         @Override
@@ -3160,6 +3213,24 @@
         }
 
         @Override
+        public void reportUserInteractionEvent(@NonNull String pkgName, @UserIdInt int userId,
+                @NonNull PersistableBundle extras) {
+            if (extras != null && extras.size() != 0) {
+                // Truncate the value if necessary.
+                String category = extras.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY);
+                String action = extras.getString(UsageStatsManager.EXTRA_EVENT_ACTION);
+                extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+                        getTrimmedString(category));
+                extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, getTrimmedString(action));
+            }
+
+            Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
+            event.mPackage = pkgName;
+            event.mExtras = extras;
+            reportEventOrAddToQueue(userId, event);
+        }
+
+        @Override
         public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
             return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
                     userId, SystemClock.elapsedRealtime());
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9b67ab6..3bc7752 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -1110,6 +1110,10 @@
         if (event.mNotificationChannelId != null) {
             pw.printPair("channelId", event.mNotificationChannelId);
         }
+
+        if ((event.mEventType == Event.USER_INTERACTION) && (event.mExtras != null)) {
+            pw.print(event.mExtras.toString());
+        }
         pw.printHexPair("flags", event.mFlags);
         pw.println();
     }
diff --git a/telephony/java/android/telephony/CellularIdentifierDisclosure.java b/telephony/java/android/telephony/CellularIdentifierDisclosure.java
new file mode 100644
index 0000000..7b2db6d
--- /dev/null
+++ b/telephony/java/android/telephony/CellularIdentifierDisclosure.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 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.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A single occurrence of a cellular identifier being disclosed in the clear before a security
+ * context is established.
+ *
+ * @hide
+ */
+public final class CellularIdentifierDisclosure implements Parcelable {
+    private static final String TAG = "CellularIdentifierDisclosure";
+
+    private @NasProtocolMessage int mNasProtocolMessage;
+    private @CellularIdentifier int mCellularIdentifier;
+    private String mPlmn;
+    private boolean mIsEmergency;
+
+    public CellularIdentifierDisclosure(@NasProtocolMessage int nasProtocolMessage,
+            @CellularIdentifier int cellularIdentifier, String plmn, boolean isEmergency) {
+        mNasProtocolMessage = nasProtocolMessage;
+        mCellularIdentifier = cellularIdentifier;
+        mPlmn = plmn;
+        mIsEmergency = isEmergency;
+    }
+
+    private CellularIdentifierDisclosure(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public @NasProtocolMessage int getNasProtocolMessage() {
+        return mNasProtocolMessage;
+    }
+
+    public @CellularIdentifier int getCellularIdentifier() {
+        return mCellularIdentifier;
+    }
+
+    public String getPlmn() {
+        return mPlmn;
+    }
+
+    public boolean isEmergency() {
+        return mIsEmergency;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mNasProtocolMessage);
+        out.writeInt(mCellularIdentifier);
+        out.writeBoolean(mIsEmergency);
+        out.writeString8(mPlmn);
+    }
+
+    public static final Parcelable.Creator<CellularIdentifierDisclosure> CREATOR =
+            new Parcelable.Creator<CellularIdentifierDisclosure>() {
+                public CellularIdentifierDisclosure createFromParcel(Parcel in) {
+                    return new CellularIdentifierDisclosure(in);
+                }
+
+                public CellularIdentifierDisclosure[] newArray(int size) {
+                    return new CellularIdentifierDisclosure[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return TAG + ":{ mNasProtocolMessage = " + mNasProtocolMessage
+                + " mCellularIdentifier = " + mCellularIdentifier + " mIsEmergency = "
+                + mIsEmergency + " mPlmn = " + mPlmn;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CellularIdentifierDisclosure)) return false;
+        CellularIdentifierDisclosure that = (CellularIdentifierDisclosure) o;
+        return mNasProtocolMessage == that.mNasProtocolMessage
+                && mCellularIdentifier == that.mCellularIdentifier
+                && mIsEmergency == that.mIsEmergency && mPlmn.equals(that.mPlmn);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mNasProtocolMessage, mCellularIdentifier, mIsEmergency,
+                mPlmn);
+    }
+
+    private void readFromParcel(@NonNull Parcel in) {
+        mNasProtocolMessage = in.readInt();
+        mCellularIdentifier = in.readInt();
+        mIsEmergency = in.readBoolean();
+        mPlmn = in.readString8();
+    }
+
+    public static final int NAS_PROTOCOL_MESSAGE_UNKNOWN = 0;
+    public static final int NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST = 1;
+    public static final int NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE = 2;
+    public static final int NAS_PROTOCOL_MESSAGE_DETACH_REQUEST = 3;
+    public static final int NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST = 4;
+    public static final int NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST = 5;
+    public static final int NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE = 6;
+    public static final int NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST = 7;
+    public static final int NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST = 8;
+    public static final int NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST = 9;
+    public static final int NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST = 10;
+    public static final int NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION = 11;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"NAS_PROTOCOL_MESSAGE_"}, value = {NAS_PROTOCOL_MESSAGE_UNKNOWN,
+            NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE,
+            NAS_PROTOCOL_MESSAGE_DETACH_REQUEST, NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST,
+            NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST,
+            NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE,
+            NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST, NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST,
+            NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST,
+            NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST, NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION})
+    public @interface NasProtocolMessage {
+    }
+
+    public static final int CELLULAR_IDENTIFIER_UNKNOWN = 0;
+    public static final int CELLULAR_IDENTIFIER_IMSI = 1;
+    public static final int CELLULAR_IDENTIFIER_IMEI = 2;
+    public static final int CELLULAR_IDENTIFIER_SUCI = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CELLULAR_IDENTIFIER_"}, value = {CELLULAR_IDENTIFIER_UNKNOWN,
+            CELLULAR_IDENTIFIER_IMSI, CELLULAR_IDENTIFIER_IMEI, CELLULAR_IDENTIFIER_SUCI})
+    public @interface CellularIdentifier {
+    }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 412aa9b..275a0e2 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -92,10 +92,6 @@
     srcs: [
         "compile/IdAssigner.cpp",
         "compile/InlineXmlFormatParser.cpp",
-        "compile/NinePatch.cpp",
-        "compile/Png.cpp",
-        "compile/PngChunkFilter.cpp",
-        "compile/PngCrunch.cpp",
         "compile/PseudolocaleGenerator.cpp",
         "compile/Pseudolocalizer.cpp",
         "compile/XmlIdCollector.cpp",
@@ -112,9 +108,7 @@
         "format/binary/XmlFlattener.cpp",
         "format/proto/ProtoDeserialize.cpp",
         "format/proto/ProtoSerialize.cpp",
-        "io/BigBufferStream.cpp",
         "io/File.cpp",
-        "io/FileStream.cpp",
         "io/FileSystem.cpp",
         "io/StringStream.cpp",
         "io/Util.cpp",
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 6b1fd9f..d6502d8 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -18,12 +18,12 @@
 
 #include "ResourceValues.h"
 #include "ValueVisitor.h"
+#include "androidfw/BigBufferStream.h"
 #include "format/Archive.h"
 #include "format/binary/TableFlattener.h"
 #include "format/binary/XmlFlattener.h"
 #include "format/proto/ProtoDeserialize.h"
 #include "format/proto/ProtoSerialize.h"
-#include "io/BigBufferStream.h"
 #include "io/Util.h"
 #include "xml/XmlDom.h"
 
@@ -48,7 +48,7 @@
     }
 
     // First try in proto format.
-    std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
+    std::unique_ptr<android::InputStream> manifest_in = manifest_file->OpenInputStream();
     if (manifest_in != nullptr) {
       pb::XmlNode pb_node;
       io::ProtoInputStreamReader proto_reader(manifest_in.get());
@@ -102,7 +102,7 @@
   io::IFile* table_file = collection->FindFile(kProtoResourceTablePath);
   if (table_file != nullptr) {
     pb::ResourceTable pb_table;
-    std::unique_ptr<io::InputStream> in = table_file->OpenInputStream();
+    std::unique_ptr<android::InputStream> in = table_file->OpenInputStream();
     if (in == nullptr) {
       diag->Error(android::DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
       return {};
@@ -129,7 +129,7 @@
     return {};
   }
 
-  std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
+  std::unique_ptr<android::InputStream> manifest_in = manifest_file->OpenInputStream();
   if (manifest_in == nullptr) {
     diag->Error(android::DiagMessage(source) << "failed to open " << kAndroidManifestPath);
     return {};
@@ -262,7 +262,7 @@
         return false;
       }
 
-      io::BigBufferInputStream input_stream(&buffer);
+      android::BigBufferInputStream input_stream(&buffer);
       if (!io::CopyInputStreamToArchive(context,
                                         &input_stream,
                                         path,
@@ -296,7 +296,7 @@
       }
 
       uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
-      io::BigBufferInputStream manifest_buffer_in(&buffer);
+      android::BigBufferInputStream manifest_buffer_in(&buffer);
       if (!io::CopyInputStreamToArchive(context, &manifest_buffer_in, path, compression_flags,
                                         writer)) {
         return false;
@@ -321,7 +321,7 @@
 
   std::unique_ptr<xml::XmlResource> doc;
   if (format_ == ApkFormat::kProto) {
-    std::unique_ptr<io::InputStream> in = file->OpenInputStream();
+    std::unique_ptr<android::InputStream> in = file->OpenInputStream();
     if (!in) {
       diag->Error(android::DiagMessage() << "failed to open file");
       return nullptr;
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index a0b4dab..b351d6e 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -27,6 +27,7 @@
 #include "Diagnostics.h"
 #include "android-base/stringprintf.h"
 #include "android-base/utf8.h"
+#include "androidfw/FileStream.h"
 #include "androidfw/IDiagnostics.h"
 #include "androidfw/StringPiece.h"
 #include "cmd/ApkInfo.h"
@@ -37,7 +38,6 @@
 #include "cmd/Dump.h"
 #include "cmd/Link.h"
 #include "cmd/Optimize.h"
-#include "io/FileStream.h"
 #include "trace/TraceBuffer.h"
 #include "util/Files.h"
 #include "util/Util.h"
@@ -99,7 +99,7 @@
  */
 class DaemonCommand : public Command {
  public:
-  explicit DaemonCommand(io::FileOutputStream* out, android::IDiagnostics* diagnostics)
+  explicit DaemonCommand(android::FileOutputStream* out, android::IDiagnostics* diagnostics)
       : Command("daemon", "m"), out_(out), diagnostics_(diagnostics) {
     SetDescription("Runs aapt in daemon mode. Each subsequent line is a single parameter to the\n"
         "command. The end of an invocation is signaled by providing an empty line.");
@@ -147,7 +147,7 @@
   }
 
  private:
-  io::FileOutputStream* out_;
+  android::FileOutputStream* out_;
   android::IDiagnostics* diagnostics_;
   std::optional<std::string> trace_folder_;
 };
@@ -167,7 +167,7 @@
 
   // Use a smaller buffer so that there is less latency for printing to stdout.
   constexpr size_t kStdOutBufferSize = 1024u;
-  aapt::io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+  android::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
   aapt::text::Printer printer(&fout);
 
   aapt::StdErrDiagnostics diagnostics;
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 728ba8a..031dd5b 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -25,13 +25,16 @@
 #include "android-base/errors.h"
 #include "android-base/file.h"
 #include "android-base/utf8.h"
+#include "androidfw/BigBufferStream.h"
 #include "androidfw/ConfigDescription.h"
+#include "androidfw/FileStream.h"
 #include "androidfw/IDiagnostics.h"
+#include "androidfw/Image.h"
+#include "androidfw/Png.h"
 #include "androidfw/StringPiece.h"
 #include "cmd/Util.h"
 #include "compile/IdAssigner.h"
 #include "compile/InlineXmlFormatParser.h"
-#include "compile/Png.h"
 #include "compile/PseudolocaleGenerator.h"
 #include "compile/XmlIdCollector.h"
 #include "format/Archive.h"
@@ -39,8 +42,6 @@
 #include "format/proto/ProtoSerialize.h"
 #include "google/protobuf/io/coded_stream.h"
 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
-#include "io/BigBufferStream.h"
-#include "io/FileStream.h"
 #include "io/FileSystem.h"
 #include "io/StringStream.h"
 #include "io/Util.h"
@@ -52,9 +53,9 @@
 #include "xml/XmlDom.h"
 #include "xml/XmlPullParser.h"
 
-using ::aapt::io::FileInputStream;
 using ::aapt::text::Printer;
 using ::android::ConfigDescription;
+using ::android::FileInputStream;
 using ::android::StringPiece;
 using ::android::base::SystemErrorCodeToString;
 using ::google::protobuf::io::CopyingOutputStreamAdaptor;
@@ -241,7 +242,7 @@
   }
 
   if (options.generate_text_symbols_path) {
-    io::FileOutputStream fout_text(options.generate_text_symbols_path.value());
+    android::FileOutputStream fout_text(options.generate_text_symbols_path.value());
 
     if (fout_text.HadError()) {
       context->GetDiagnostics()->Error(android::DiagMessage()
@@ -307,7 +308,7 @@
 }
 
 static bool WriteHeaderAndDataToWriter(StringPiece output_path, const ResourceFile& file,
-                                       io::KnownSizeInputStream* in, IArchiveWriter* writer,
+                                       android::KnownSizeInputStream* in, IArchiveWriter* writer,
                                        android::IDiagnostics* diag) {
   TRACE_CALL();
   // Start the entry so we can write the header.
@@ -448,7 +449,7 @@
   }
 
   if (options.generate_text_symbols_path) {
-    io::FileOutputStream fout_text(options.generate_text_symbols_path.value());
+    android::FileOutputStream fout_text(options.generate_text_symbols_path.value());
 
     if (fout_text.HadError()) {
       context->GetDiagnostics()->Error(android::DiagMessage()
@@ -498,21 +499,22 @@
     }
 
     android::BigBuffer crunched_png_buffer(4096);
-    io::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
+    android::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
 
     // Ensure that we only keep the chunks we care about if we end up
     // using the original PNG instead of the crunched one.
     const StringPiece content(reinterpret_cast<const char*>(data->data()), data->size());
-    PngChunkFilter png_chunk_filter(content);
-    std::unique_ptr<Image> image = ReadPng(context, path_data.source, &png_chunk_filter);
+    android::PngChunkFilter png_chunk_filter(content);
+    android::SourcePathDiagnostics source_diag(path_data.source, context->GetDiagnostics());
+    auto image = android::ReadPng(&png_chunk_filter, &source_diag);
     if (!image) {
       return false;
     }
 
-    std::unique_ptr<NinePatch> nine_patch;
+    std::unique_ptr<android::NinePatch> nine_patch;
     if (path_data.extension == "9.png") {
       std::string err;
-      nine_patch = NinePatch::Create(image->rows.get(), image->width, image->height, &err);
+      nine_patch = android::NinePatch::Create(image->rows.get(), image->width, image->height, &err);
       if (!nine_patch) {
         context->GetDiagnostics()->Error(android::DiagMessage() << err);
         return false;
@@ -537,7 +539,8 @@
     }
 
     // Write the crunched PNG.
-    if (!WritePng(context, image.get(), nine_patch.get(), &crunched_png_buffer_out, {})) {
+    if (!android::WritePng(image.get(), nine_patch.get(), &crunched_png_buffer_out, {},
+                           &source_diag, context->IsVerbose())) {
       return false;
     }
 
@@ -557,7 +560,7 @@
 
       png_chunk_filter.Rewind();
       android::BigBuffer filtered_png_buffer(4096);
-      io::BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
+      android::BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
       io::Copy(&filtered_png_buffer_out, &png_chunk_filter);
       buffer.AppendBuffer(std::move(filtered_png_buffer));
     }
@@ -567,7 +570,7 @@
       // This will help catch exotic cases where the new code may generate larger PNGs.
       std::stringstream legacy_stream{std::string(content)};
       android::BigBuffer legacy_buffer(4096);
-      Png png(context->GetDiagnostics());
+      android::Png png(context->GetDiagnostics());
       if (!png.process(path_data.source, &legacy_stream, &legacy_buffer, {})) {
         return false;
       }
@@ -578,7 +581,7 @@
     }
   }
 
-  io::BigBufferInputStream buffer_in(&buffer);
+  android::BigBufferInputStream buffer_in(&buffer);
   return WriteHeaderAndDataToWriter(output_path, res_file, &buffer_in, writer,
       context->GetDiagnostics());
 }
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 8880089..9337cb9 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -341,7 +341,7 @@
   // Check resources.pb contains relative sources.
   io::IFile* proto_file =
       apk.get()->GetFileCollection()->FindFile("resources.pb");
-  std::unique_ptr<io::InputStream> proto_stream = proto_file->OpenInputStream();
+  std::unique_ptr<android::InputStream> proto_stream = proto_file->OpenInputStream();
   io::ProtoInputStreamReader proto_reader(proto_stream.get());
   pb::ResourceTable pb_table;
   proto_reader.ReadMessage(&pb_table);
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 387dcfe..c132792 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -24,13 +24,13 @@
 #include "android-base/file.h"
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
+#include "androidfw/BigBufferStream.h"
 #include "androidfw/StringPiece.h"
 #include "cmd/Util.h"
 #include "format/binary/TableFlattener.h"
 #include "format/binary/XmlFlattener.h"
 #include "format/proto/ProtoDeserialize.h"
 #include "format/proto/ProtoSerialize.h"
-#include "io/BigBufferStream.h"
 #include "io/Util.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
@@ -80,7 +80,7 @@
       return false;
     }
 
-    io::BigBufferInputStream input_stream(&buffer);
+    android::BigBufferInputStream input_stream(&buffer);
     return io::CopyInputStreamToArchive(context_, &input_stream, path, compression_flags, writer);
   }
 
@@ -91,14 +91,14 @@
       return false;
     }
 
-    io::BigBufferInputStream input_stream(&buffer);
+    android::BigBufferInputStream input_stream(&buffer);
     return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
                                         ArchiveEntry::kAlign, writer);
   }
 
   bool SerializeFile(FileReference* file, IArchiveWriter* writer) override {
     if (file->type == ResourceFile::Type::kProtoXml) {
-      unique_ptr<io::InputStream> in = file->file->OpenInputStream();
+      unique_ptr<android::InputStream> in = file->file->OpenInputStream();
       if (in == nullptr) {
         context_->GetDiagnostics()->Error(android::DiagMessage(source_)
                                           << "failed to open file " << *file->path);
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 864af06..6fa9ecb 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -19,19 +19,18 @@
 #include <cinttypes>
 #include <vector>
 
-#include "android-base/stringprintf.h"
-#include "androidfw/ConfigDescription.h"
-#include "androidfw/StringPiece.h"
-
 #include "Debug.h"
 #include "Diagnostics.h"
 #include "LoadedApk.h"
 #include "Util.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ConfigDescription.h"
+#include "androidfw/FileStream.h"
+#include "androidfw/StringPiece.h"
 #include "format/Container.h"
 #include "format/binary/BinaryResourceParser.h"
 #include "format/binary/XmlFlattener.h"
 #include "format/proto/ProtoDeserialize.h"
-#include "io/FileStream.h"
 #include "io/ZipArchive.h"
 #include "process/IResourceTableConsumer.h"
 #include "text/Printer.h"
@@ -145,7 +144,7 @@
 
   bool error = false;
   for (auto container : args) {
-    io::FileInputStream input(container);
+    android::FileInputStream input(container);
     if (input.HadError()) {
       context.GetDiagnostics()->Error(android::DiagMessage(container)
                                       << "failed to open file: " << input.GetError());
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 76d33d7..119a59b 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -17,7 +17,7 @@
 #ifndef AAPT2_DUMP_H
 #define AAPT2_DUMP_H
 
-#include <io/FileStream.h>
+#include <androidfw/FileStream.h>
 #include <io/ZipArchive.h>
 
 #include "Command.h"
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index c638873..9ca546f 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -35,6 +35,8 @@
 #include "android-base/expected.h"
 #include "android-base/file.h"
 #include "android-base/stringprintf.h"
+#include "androidfw/BigBufferStream.h"
+#include "androidfw/FileStream.h"
 #include "androidfw/IDiagnostics.h"
 #include "androidfw/Locale.h"
 #include "androidfw/StringPiece.h"
@@ -48,8 +50,6 @@
 #include "format/binary/XmlFlattener.h"
 #include "format/proto/ProtoDeserialize.h"
 #include "format/proto/ProtoSerialize.h"
-#include "io/BigBufferStream.h"
-#include "io/FileStream.h"
 #include "io/FileSystem.h"
 #include "io/Util.h"
 #include "io/ZipArchive.h"
@@ -73,8 +73,8 @@
 #include "util/Files.h"
 #include "xml/XmlDom.h"
 
-using ::aapt::io::FileInputStream;
 using ::android::ConfigDescription;
+using ::android::FileInputStream;
 using ::android::StringPiece;
 using ::android::base::expected;
 using ::android::base::StringPrintf;
@@ -263,7 +263,7 @@
         return false;
       }
 
-      io::BigBufferInputStream input_stream(&buffer);
+      android::BigBufferInputStream input_stream(&buffer);
       return io::CopyInputStreamToArchive(context, &input_stream, path, ArchiveEntry::kCompress,
                                           writer);
     } break;
@@ -284,7 +284,7 @@
 static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path,
                                                  android::IDiagnostics* diag) {
   TRACE_CALL();
-  FileInputStream fin(path);
+  android::FileInputStream fin(path);
   if (fin.HadError()) {
     diag->Error(android::DiagMessage(path) << "failed to load XML file: " << fin.GetError());
     return {};
@@ -687,7 +687,7 @@
 static bool WriteStableIdMapToPath(android::IDiagnostics* diag,
                                    const std::unordered_map<ResourceName, ResourceId>& id_map,
                                    const std::string& id_map_path) {
-  io::FileOutputStream fout(id_map_path);
+  android::FileOutputStream fout(id_map_path);
   if (fout.HadError()) {
     diag->Error(android::DiagMessage(id_map_path) << "failed to open: " << fout.GetError());
     return false;
@@ -1197,7 +1197,7 @@
           return false;
         }
 
-        io::BigBufferInputStream input_stream(&buffer);
+        android::BigBufferInputStream input_stream(&buffer);
         return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
                                             ArchiveEntry::kAlign, writer);
       } break;
@@ -1221,7 +1221,7 @@
     }
 
     std::string out_path;
-    std::unique_ptr<io::FileOutputStream> fout;
+    std::unique_ptr<android::FileOutputStream> fout;
     if (options_.generate_java_class_path) {
       out_path = options_.generate_java_class_path.value();
       file::AppendPath(&out_path, file::PackageToPath(out_package));
@@ -1233,7 +1233,7 @@
 
       file::AppendPath(&out_path, "R.java");
 
-      fout = util::make_unique<io::FileOutputStream>(out_path);
+      fout = util::make_unique<android::FileOutputStream>(out_path);
       if (fout->HadError()) {
         context_->GetDiagnostics()->Error(android::DiagMessage()
                                           << "failed writing to '" << out_path
@@ -1242,9 +1242,9 @@
       }
     }
 
-    std::unique_ptr<io::FileOutputStream> fout_text;
+    std::unique_ptr<android::FileOutputStream> fout_text;
     if (out_text_symbols_path) {
-      fout_text = util::make_unique<io::FileOutputStream>(out_text_symbols_path.value());
+      fout_text = util::make_unique<android::FileOutputStream>(out_text_symbols_path.value());
       if (fout_text->HadError()) {
         context_->GetDiagnostics()->Error(android::DiagMessage()
                                           << "failed writing to '" << out_text_symbols_path.value()
@@ -1386,7 +1386,7 @@
 
     file::AppendPath(&out_path, "Manifest.java");
 
-    io::FileOutputStream fout(out_path);
+    android::FileOutputStream fout(out_path);
     if (fout.HadError()) {
       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to open '" << out_path
                                                                << "': " << fout.GetError());
@@ -1412,7 +1412,7 @@
     }
 
     const std::string& out_path = out.value();
-    io::FileOutputStream fout(out_path);
+    android::FileOutputStream fout(out_path);
     if (fout.HadError()) {
       context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to open '" << out_path
                                                                << "': " << fout.GetError());
@@ -1601,7 +1601,7 @@
       }
     }
 
-    std::unique_ptr<io::InputStream> input_stream = file->OpenInputStream();
+    std::unique_ptr<android::InputStream> input_stream = file->OpenInputStream();
     if (input_stream == nullptr) {
       context_->GetDiagnostics()->Error(android::DiagMessage(src) << "failed to open file");
       return false;
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index f045dad..762441e 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -30,6 +30,7 @@
 #include "ValueVisitor.h"
 #include "android-base/file.h"
 #include "android-base/stringprintf.h"
+#include "androidfw/BigBufferStream.h"
 #include "androidfw/ConfigDescription.h"
 #include "androidfw/IDiagnostics.h"
 #include "androidfw/ResourceTypes.h"
@@ -39,7 +40,6 @@
 #include "filter/AbiFilter.h"
 #include "format/binary/TableFlattener.h"
 #include "format/binary/XmlFlattener.h"
-#include "io/BigBufferStream.h"
 #include "io/Util.h"
 #include "optimize/MultiApkGenerator.h"
 #include "optimize/Obfuscator.h"
@@ -249,7 +249,7 @@
       return false;
     }
 
-    io::BigBufferInputStream manifest_buffer_in(&manifest_buffer);
+    android::BigBufferInputStream manifest_buffer_in(&manifest_buffer);
     if (!io::CopyInputStreamToArchive(context_, &manifest_buffer_in, "AndroidManifest.xml",
                                       ArchiveEntry::kCompress, writer)) {
       return false;
@@ -297,7 +297,7 @@
       return false;
     }
 
-    io::BigBufferInputStream table_buffer_in(&table_buffer);
+    android::BigBufferInputStream table_buffer_in(&table_buffer);
     return io::CopyInputStreamToArchive(context_, &table_buffer_in, "resources.arsc",
                                         ArchiveEntry::kAlign, writer);
   }
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index a2b4818..a596229 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -29,8 +29,8 @@
 #include "SdkConstants.h"
 #include "ValueVisitor.h"
 #include "androidfw/ConfigDescription.h"
+#include "androidfw/FileStream.h"
 #include "io/File.h"
-#include "io/FileStream.h"
 #include "process/IResourceTableConsumer.h"
 #include "xml/XmlDom.h"
 
diff --git a/tools/aapt2/format/Archive.cpp b/tools/aapt2/format/Archive.cpp
index e9a93d8..91768a0 100644
--- a/tools/aapt2/format/Archive.cpp
+++ b/tools/aapt2/format/Archive.cpp
@@ -91,7 +91,7 @@
     return true;
   }
 
-  bool WriteFile(StringPiece path, uint32_t flags, io::InputStream* in) override {
+  bool WriteFile(StringPiece path, uint32_t flags, android::InputStream* in) override {
     if (!StartEntry(path, flags)) {
       return false;
     }
@@ -182,7 +182,7 @@
     return true;
   }
 
-  bool WriteFile(StringPiece path, uint32_t flags, io::InputStream* in) override {
+  bool WriteFile(StringPiece path, uint32_t flags, android::InputStream* in) override {
     while (true) {
       if (!StartEntry(path, flags)) {
         return false;
diff --git a/tools/aapt2/format/Archive.h b/tools/aapt2/format/Archive.h
index 6cde753..3c3d0ab 100644
--- a/tools/aapt2/format/Archive.h
+++ b/tools/aapt2/format/Archive.h
@@ -24,9 +24,9 @@
 
 #include "androidfw/BigBuffer.h"
 #include "androidfw/IDiagnostics.h"
+#include "androidfw/Streams.h"
 #include "androidfw/StringPiece.h"
 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
-#include "io/Io.h"
 #include "util/Files.h"
 
 namespace aapt {
@@ -46,7 +46,7 @@
  public:
   virtual ~IArchiveWriter() = default;
 
-  virtual bool WriteFile(android::StringPiece path, uint32_t flags, io::InputStream* in) = 0;
+  virtual bool WriteFile(android::StringPiece path, uint32_t flags, android::InputStream* in) = 0;
 
   // Starts a new entry and allows caller to write bytes to it sequentially.
   // Only use StartEntry if code you do not control needs to write to a CopyingOutputStream.
diff --git a/tools/aapt2/format/Archive_test.cpp b/tools/aapt2/format/Archive_test.cpp
index fd50af9..df105f8 100644
--- a/tools/aapt2/format/Archive_test.cpp
+++ b/tools/aapt2/format/Archive_test.cpp
@@ -95,7 +95,7 @@
 
 void VerifyZipFile(const std::string& output_path, const std::string& file, const uint8_t array[]) {
   std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(output_path, nullptr);
-  std::unique_ptr<io::InputStream> stream = zip->FindFile(file)->OpenInputStream();
+  std::unique_ptr<android::InputStream> stream = zip->FindFile(file)->OpenInputStream();
 
   std::vector<uint8_t> buffer;
   const void* data;
diff --git a/tools/aapt2/format/Container.cpp b/tools/aapt2/format/Container.cpp
index 1ff6c49..cb4a225 100644
--- a/tools/aapt2/format/Container.cpp
+++ b/tools/aapt2/format/Container.cpp
@@ -94,7 +94,7 @@
 }
 
 bool ContainerWriter::AddResFileEntry(const pb::internal::CompiledFile& file,
-                                      io::KnownSizeInputStream* in) {
+                                      android::KnownSizeInputStream* in) {
   if (current_entry_count_ >= total_entry_count_) {
     error_ = "too many entries being serialized";
     return false;
@@ -264,7 +264,7 @@
   return reader_->GetError();
 }
 
-ContainerReader::ContainerReader(io::InputStream* in)
+ContainerReader::ContainerReader(android::InputStream* in)
     : in_(in),
       adaptor_(in),
       coded_in_(&adaptor_),
diff --git a/tools/aapt2/format/Container.h b/tools/aapt2/format/Container.h
index 121c592..c5d5676 100644
--- a/tools/aapt2/format/Container.h
+++ b/tools/aapt2/format/Container.h
@@ -22,9 +22,9 @@
 #include "Resources.pb.h"
 #include "ResourcesInternal.pb.h"
 #include "androidfw/BigBuffer.h"
+#include "androidfw/Streams.h"
 #include "google/protobuf/io/coded_stream.h"
 #include "google/protobuf/io/zero_copy_stream.h"
-#include "io/Io.h"
 #include "io/Util.h"
 
 namespace aapt {
@@ -39,7 +39,7 @@
   explicit ContainerWriter(::google::protobuf::io::ZeroCopyOutputStream* out, size_t entry_count);
 
   bool AddResTableEntry(const pb::ResourceTable& table);
-  bool AddResFileEntry(const pb::internal::CompiledFile& file, io::KnownSizeInputStream* in);
+  bool AddResFileEntry(const pb::internal::CompiledFile& file, android::KnownSizeInputStream* in);
   bool HadError() const;
   std::string GetError() const;
 
@@ -79,7 +79,7 @@
 
 class ContainerReader {
  public:
-  explicit ContainerReader(io::InputStream* in);
+  explicit ContainerReader(android::InputStream* in);
 
   ContainerReaderEntry* Next();
 
@@ -91,7 +91,7 @@
 
   friend class ContainerReaderEntry;
 
-  io::InputStream* in_;
+  android::InputStream* in_;
   io::ZeroCopyInputAdaptor adaptor_;
   ::google::protobuf::io::CodedInputStream coded_in_;
   size_t total_entry_count_;
diff --git a/tools/aapt2/io/Data.h b/tools/aapt2/io/Data.h
index db91a77..29f523a 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -20,15 +20,14 @@
 #include <memory>
 
 #include "android-base/macros.h"
+#include "androidfw/Streams.h"
 #include "utils/FileMap.h"
 
-#include "io/Io.h"
-
 namespace aapt {
 namespace io {
 
 // Interface for a block of contiguous memory. An instance of this interface owns the data.
-class IData : public KnownSizeInputStream {
+class IData : public android::KnownSizeInputStream {
  public:
   virtual ~IData() = default;
 
diff --git a/tools/aapt2/io/File.cpp b/tools/aapt2/io/File.cpp
index b4f1ff3..4dfdb5b 100644
--- a/tools/aapt2/io/File.cpp
+++ b/tools/aapt2/io/File.cpp
@@ -39,7 +39,7 @@
   return {};
 }
 
-std::unique_ptr<io::InputStream> FileSegment::OpenInputStream() {
+std::unique_ptr<android::InputStream> FileSegment::OpenInputStream() {
   return OpenAsData();
 }
 
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index 673d1b7..248756b 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -43,7 +43,7 @@
   // Returns nullptr on failure.
   virtual std::unique_ptr<IData> OpenAsData() = 0;
 
-  virtual std::unique_ptr<io::InputStream> OpenInputStream() = 0;
+  virtual std::unique_ptr<android::InputStream> OpenInputStream() = 0;
 
   // Returns the source of this file. This is for presentation to the user and
   // may not be a valid file system path (for example, it may contain a '@' sign to separate
@@ -78,7 +78,7 @@
       : file_(file), offset_(offset), len_(len) {}
 
   std::unique_ptr<IData> OpenAsData() override;
-  std::unique_ptr<io::InputStream> OpenInputStream() override;
+  std::unique_ptr<android::InputStream> OpenInputStream() override;
 
   const android::Source& GetSource() const override {
     return file_->GetSource();
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 6a692e4..03fabcc 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -22,9 +22,9 @@
 #include <sys/stat.h>
 
 #include "android-base/errors.h"
+#include "androidfw/FileStream.h"
 #include "androidfw/Source.h"
 #include "androidfw/StringPiece.h"
-#include "io/FileStream.h"
 #include "util/Files.h"
 #include "util/Util.h"
 #include "utils/FileMap.h"
@@ -49,8 +49,8 @@
   return {};
 }
 
-std::unique_ptr<io::InputStream> RegularFile::OpenInputStream() {
-  return util::make_unique<FileInputStream>(source_.path);
+std::unique_ptr<android::InputStream> RegularFile::OpenInputStream() {
+  return util::make_unique<android::FileInputStream>(source_.path);
 }
 
 const android::Source& RegularFile::GetSource() const {
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index f975196..d6ecfeb 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -30,7 +30,7 @@
   explicit RegularFile(const android::Source& source);
 
   std::unique_ptr<IData> OpenAsData() override;
-  std::unique_ptr<io::InputStream> OpenInputStream() override;
+  std::unique_ptr<android::InputStream> OpenInputStream() override;
   const android::Source& GetSource() const override;
   bool GetModificationTime(struct tm* buf) const override;
 
diff --git a/tools/aapt2/io/StringStream.cpp b/tools/aapt2/io/StringStream.cpp
index 9c49788..bb3911b 100644
--- a/tools/aapt2/io/StringStream.cpp
+++ b/tools/aapt2/io/StringStream.cpp
@@ -51,6 +51,23 @@
   return str_.size();
 }
 
+bool StringInputStream::ReadFullyAtOffset(void* data, size_t byte_count, off64_t offset) {
+  if (byte_count == 0) {
+    return true;
+  }
+  if (offset < 0) {
+    return false;
+  }
+  if (offset > std::numeric_limits<off64_t>::max() - byte_count) {
+    return false;
+  }
+  if (offset + byte_count > str_.size()) {
+    return false;
+  }
+  memcpy(data, str_.data() + offset, byte_count);
+  return true;
+}
+
 StringOutputStream::StringOutputStream(std::string* str, size_t buffer_capacity)
     : str_(str),
       buffer_capacity_(buffer_capacity),
diff --git a/tools/aapt2/io/StringStream.h b/tools/aapt2/io/StringStream.h
index f7bdecca..7e1abe5 100644
--- a/tools/aapt2/io/StringStream.h
+++ b/tools/aapt2/io/StringStream.h
@@ -17,17 +17,16 @@
 #ifndef AAPT_IO_STRINGSTREAM_H
 #define AAPT_IO_STRINGSTREAM_H
 
-#include "io/Io.h"
-
 #include <memory>
 
 #include "android-base/macros.h"
+#include "androidfw/Streams.h"
 #include "androidfw/StringPiece.h"
 
 namespace aapt {
 namespace io {
 
-class StringInputStream : public KnownSizeInputStream {
+class StringInputStream : public android::KnownSizeInputStream {
  public:
   explicit StringInputStream(android::StringPiece str);
 
@@ -47,6 +46,8 @@
 
   size_t TotalSize() const override;
 
+  bool ReadFullyAtOffset(void* data, size_t byte_count, off64_t offset) override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(StringInputStream);
 
@@ -54,7 +55,7 @@
   size_t offset_;
 };
 
-class StringOutputStream : public OutputStream {
+class StringOutputStream : public android::OutputStream {
  public:
   explicit StringOutputStream(std::string* str, size_t buffer_capacity = 4096u);
 
diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp
index 79d8d52..9616e47 100644
--- a/tools/aapt2/io/Util.cpp
+++ b/tools/aapt2/io/Util.cpp
@@ -26,8 +26,9 @@
 namespace aapt {
 namespace io {
 
-bool CopyInputStreamToArchive(IAaptContext* context, InputStream* in, std::string_view out_path,
-                              uint32_t compression_flags, IArchiveWriter* writer) {
+bool CopyInputStreamToArchive(IAaptContext* context, android::InputStream* in,
+                              std::string_view out_path, uint32_t compression_flags,
+                              IArchiveWriter* writer) {
   TRACE_CALL();
   if (context->IsVerbose()) {
     context->GetDiagnostics()->Note(android::DiagMessage()
@@ -91,7 +92,7 @@
   return false;
 }
 
-bool Copy(OutputStream* out, InputStream* in) {
+bool Copy(android::OutputStream* out, android::InputStream* in) {
   TRACE_CALL();
   const void* in_buffer;
   size_t in_len;
@@ -110,7 +111,7 @@
   return !in->HadError();
 }
 
-bool Copy(OutputStream* out, StringPiece in) {
+bool Copy(android::OutputStream* out, StringPiece in) {
   const char* in_buffer = in.data();
   size_t in_len = in.size();
   while (in_len != 0) {
@@ -129,7 +130,7 @@
   return true;
 }
 
-bool Copy(ZeroCopyOutputStream* out, InputStream* in) {
+bool Copy(ZeroCopyOutputStream* out, android::InputStream* in) {
   OutputStreamAdaptor adaptor(out);
   return Copy(&adaptor, in);
 }
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index 685f522..25aa8f8 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -19,18 +19,19 @@
 
 #include <string_view>
 
+#include "androidfw/Streams.h"
 #include "format/Archive.h"
 #include "google/protobuf/io/coded_stream.h"
 #include "google/protobuf/message.h"
 #include "io/File.h"
-#include "io/Io.h"
 #include "process/IResourceTableConsumer.h"
 
 namespace aapt {
 namespace io {
 
-bool CopyInputStreamToArchive(IAaptContext* context, InputStream* in, std::string_view out_path,
-                              uint32_t compression_flags, IArchiveWriter* writer);
+bool CopyInputStreamToArchive(IAaptContext* context, android::InputStream* in,
+                              std::string_view out_path, uint32_t compression_flags,
+                              IArchiveWriter* writer);
 
 bool CopyFileToArchive(IAaptContext* context, IFile* file, std::string_view out_path,
                        uint32_t compression_flags, IArchiveWriter* writer);
@@ -44,11 +45,11 @@
 
 // Copies the data from in to out. Returns false if there was an error.
 // If there was an error, check the individual streams' HadError/GetError methods.
-bool Copy(OutputStream* out, InputStream* in);
-bool Copy(OutputStream* out, android::StringPiece in);
-bool Copy(::google::protobuf::io::ZeroCopyOutputStream* out, InputStream* in);
+bool Copy(android::OutputStream* out, android::InputStream* in);
+bool Copy(android::OutputStream* out, android::StringPiece in);
+bool Copy(::google::protobuf::io::ZeroCopyOutputStream* out, android::InputStream* in);
 
-class OutputStreamAdaptor : public io::OutputStream {
+class OutputStreamAdaptor : public android::OutputStream {
  public:
   explicit OutputStreamAdaptor(::google::protobuf::io::ZeroCopyOutputStream* out) : out_(out) {
   }
@@ -84,7 +85,7 @@
 
 class ZeroCopyInputAdaptor : public ::google::protobuf::io::ZeroCopyInputStream {
  public:
-  explicit ZeroCopyInputAdaptor(io::InputStream* in) : in_(in) {
+  explicit ZeroCopyInputAdaptor(android::InputStream* in) : in_(in) {
   }
 
   bool Next(const void** data, int* size) override {
@@ -119,12 +120,13 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(ZeroCopyInputAdaptor);
 
-  io::InputStream* in_;
+  android::InputStream* in_;
 };
 
 class ProtoInputStreamReader {
  public:
-  explicit ProtoInputStreamReader(io::InputStream* in) : in_(in) { }
+  explicit ProtoInputStreamReader(android::InputStream* in) : in_(in) {
+  }
 
   /** Deserializes a Message proto from the current position in the input stream.*/
   template <typename T> bool ReadMessage(T *message) {
@@ -135,7 +137,7 @@
   }
 
  private:
-  io::InputStream* in_;
+  android::InputStream* in_;
 };
 
 }  // namespace io
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index cb5bbe9..e44d61e 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -63,7 +63,7 @@
   }
 }
 
-std::unique_ptr<io::InputStream> ZipFile::OpenInputStream() {
+std::unique_ptr<android::InputStream> ZipFile::OpenInputStream() {
   return OpenAsData();
 }
 
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index ac125d0..a53c4a2 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -35,7 +35,7 @@
   ZipFile(::ZipArchiveHandle handle, const ::ZipEntry& entry, const android::Source& source);
 
   std::unique_ptr<IData> OpenAsData() override;
-  std::unique_ptr<io::InputStream> OpenInputStream() override;
+  std::unique_ptr<android::InputStream> OpenInputStream() override;
   const android::Source& GetSource() const override;
   bool WasCompressed() override;
   bool GetModificationTime(struct tm* buf) const override;
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index 98f3bd2..db7aa35 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -111,7 +111,7 @@
     " */\n\n";
 
 void ClassDefinition::WriteJavaFile(const ClassDefinition* def, StringPiece package, bool final,
-                                    bool strip_api_annotations, io::OutputStream* out) {
+                                    bool strip_api_annotations, android::OutputStream* out) {
   Printer printer(out);
   printer.Print(sWarningHeader).Print("package ").Print(package).Println(";");
   printer.Println();
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 63c9982..84e3f33 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -241,7 +241,7 @@
 class ClassDefinition : public ClassMember {
  public:
   static void WriteJavaFile(const ClassDefinition* def, android::StringPiece package, bool final,
-                            bool strip_api_annotations, io::OutputStream* out);
+                            bool strip_api_annotations, android::OutputStream* out);
 
   ClassDefinition(android::StringPiece name, ClassQualifier qualifier, bool createIfEmpty)
       : name_(name), qualifier_(qualifier), create_if_empty_(createIfEmpty) {
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 7665d0e..58f6564 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -37,8 +37,8 @@
 #include "java/ClassDefinition.h"
 #include "process/SymbolTable.h"
 
-using ::aapt::io::OutputStream;
 using ::aapt::text::Printer;
+using ::android::OutputStream;
 using ::android::StringPiece;
 using ::android::base::StringPrintf;
 
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 234df04..9909eec 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -19,11 +19,10 @@
 
 #include <string>
 
-#include "androidfw/StringPiece.h"
-
 #include "ResourceTable.h"
 #include "ResourceValues.h"
-#include "io/Io.h"
+#include "androidfw/Streams.h"
+#include "androidfw/StringPiece.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
 #include "text/Printer.h"
@@ -70,12 +69,12 @@
   // All symbols technically belong to a single package, but linked libraries will
   // have their names mangled, denoting that they came from a different package.
   // We need to generate these symbols in a separate file. Returns true on success.
-  bool Generate(android::StringPiece package_name_to_generate, io::OutputStream* out,
-                io::OutputStream* out_r_txt = nullptr);
+  bool Generate(android::StringPiece package_name_to_generate, android::OutputStream* out,
+                android::OutputStream* out_r_txt = nullptr);
 
   bool Generate(android::StringPiece package_name_to_generate,
-                android::StringPiece output_package_name, io::OutputStream* out,
-                io::OutputStream* out_r_txt = nullptr);
+                android::StringPiece output_package_name, android::OutputStream* out,
+                android::OutputStream* out_r_txt = nullptr);
 
   const std::string& GetError() const;
 
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 80a46d5..aef48fc 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -29,8 +29,8 @@
 #include "util/Util.h"
 #include "xml/XmlDom.h"
 
-using ::aapt::io::OutputStream;
 using ::aapt::text::Printer;
+using ::android::OutputStream;
 
 namespace aapt {
 namespace proguard {
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 267f7ed..876ef48 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -26,8 +26,8 @@
 #include "ResourceTable.h"
 #include "ValueVisitor.h"
 #include "androidfw/Source.h"
+#include "androidfw/Streams.h"
 #include "androidfw/StringPiece.h"
-#include "io/Io.h"
 #include "process/IResourceTableConsumer.h"
 #include "xml/XmlDom.h"
 
@@ -69,7 +69,7 @@
   }
 
  private:
-  friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep,
+  friend void WriteKeepSet(const KeepSet& keep_set, android::OutputStream* out, bool minimal_keep,
                            bool no_location_reference);
 
   friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
@@ -89,7 +89,7 @@
 
 bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
 
-void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep,
+void WriteKeepSet(const KeepSet& keep_set, android::OutputStream* out, bool minimal_keep,
                   bool no_location_reference);
 
 bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index e48668c..0437980 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -90,7 +90,7 @@
     return {};
   }
 
-  std::unique_ptr<io::InputStream> OpenInputStream() override {
+  std::unique_ptr<android::InputStream> OpenInputStream() override {
     return OpenAsData();
   }
 
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 428372f..b91abe5 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -20,6 +20,7 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/utf8.h>
+#include <androidfw/FileStream.h>
 #include <androidfw/StringPiece.h>
 #include <dirent.h>
 #include <gmock/gmock.h>
@@ -28,7 +29,6 @@
 #include "Diagnostics.h"
 #include "cmd/Compile.h"
 #include "cmd/Link.h"
-#include "io/FileStream.h"
 #include "util/Files.h"
 
 using testing::Eq;
diff --git a/tools/aapt2/text/Printer.cpp b/tools/aapt2/text/Printer.cpp
index 8e491ac..c2fa8cc 100644
--- a/tools/aapt2/text/Printer.cpp
+++ b/tools/aapt2/text/Printer.cpp
@@ -20,7 +20,7 @@
 
 #include "io/Util.h"
 
-using ::aapt::io::OutputStream;
+using ::android::OutputStream;
 using ::android::StringPiece;
 
 namespace aapt {
diff --git a/tools/aapt2/text/Printer.h b/tools/aapt2/text/Printer.h
index f7ad98b..44f0fc5 100644
--- a/tools/aapt2/text/Printer.h
+++ b/tools/aapt2/text/Printer.h
@@ -18,17 +18,16 @@
 #define AAPT_TEXT_PRINTER_H
 
 #include "android-base/macros.h"
+#include "androidfw/Streams.h"
 #include "androidfw/StringPiece.h"
 
-#include "io/Io.h"
-
 namespace aapt {
 namespace text {
 
 // An indenting Printer that helps write formatted text to the OutputStream.
 class Printer {
  public:
-  explicit Printer(::aapt::io::OutputStream* out) : out_(out) {
+  explicit Printer(android::OutputStream* out) : out_(out) {
   }
 
   Printer& Print(android::StringPiece str);
@@ -41,7 +40,7 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(Printer);
 
-  ::aapt::io::OutputStream* out_;
+  android::OutputStream* out_;
   int indent_level_ = 0;
   bool needs_indent_ = false;
   bool error_ = false;
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 8dea8ea..49807db 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -30,7 +30,7 @@
 #include "XmlPullParser.h"
 #include "util/Util.h"
 
-using ::aapt::io::InputStream;
+using ::android::InputStream;
 using ::android::StringPiece;
 using ::android::StringPiece16;
 
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index c253b0a..9668b6a6 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -24,8 +24,8 @@
 #include "Resource.h"
 #include "ResourceValues.h"
 #include "androidfw/IDiagnostics.h"
+#include "androidfw/Streams.h"
 #include "androidfw/StringPiece.h"
-#include "io/Io.h"
 #include "util/Util.h"
 #include "xml/XmlUtil.h"
 
@@ -152,7 +152,7 @@
 };
 
 // Inflates an XML DOM from an InputStream, logging errors to the logger.
-std::unique_ptr<XmlResource> Inflate(io::InputStream* in, android::IDiagnostics* diag,
+std::unique_ptr<XmlResource> Inflate(android::InputStream* in, android::IDiagnostics* diag,
                                      const android::Source& source);
 
 // Inflates an XML DOM from a binary ResXMLTree.
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index d79446b..203832d 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -21,7 +21,7 @@
 #include "xml/XmlPullParser.h"
 #include "xml/XmlUtil.h"
 
-using ::aapt::io::InputStream;
+using ::android::InputStream;
 using ::android::StringPiece;
 
 namespace aapt {
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index fe4cd01..655e6dc 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -27,11 +27,10 @@
 #include <string>
 #include <vector>
 
-#include "android-base/macros.h"
-#include "androidfw/StringPiece.h"
-
 #include "Resource.h"
-#include "io/Io.h"
+#include "android-base/macros.h"
+#include "androidfw/Streams.h"
+#include "androidfw/StringPiece.h"
 #include "process/IResourceTableConsumer.h"
 #include "xml/XmlUtil.h"
 
@@ -66,7 +65,7 @@
   static bool SkipCurrentElement(XmlPullParser* parser);
   static bool IsGoodEvent(Event event);
 
-  explicit XmlPullParser(io::InputStream* in);
+  explicit XmlPullParser(android::InputStream* in);
   ~XmlPullParser();
 
   /**
@@ -179,7 +178,7 @@
     std::vector<Attribute> attributes;
   };
 
-  io::InputStream* in_;
+  android::InputStream* in_;
   XML_Parser parser_;
   std::queue<EventData> event_queue_;
   std::string error_;
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 356e1fa..8354d98 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -39,5 +39,5 @@
 private fun ClassNodes.isAidlClass(className: String): Boolean {
     return hasClass(className) &&
             hasClass("$className\$Stub") &&
-            hasClass("$className\$Proxy")
+            hasClass("$className\$Stub\$Proxy")
 }
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
index 45f61c5..cdd24e8 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
@@ -19,7 +19,7 @@
  * Base class for an [OutputFilter] that uses another filter as a fallback.
  */
 abstract class DelegatingFilter(
-        // fallback shouldn't be used by subclasses, so make it private.
+        // fallback shouldn't be used by subclasses directly, so make it private.
         // They should instead be calling into `super` or `outermostFilter`.
         private val fallback: OutputFilter
 ) : OutputFilter() {
@@ -27,11 +27,21 @@
         fallback.outermostFilter = this
     }
 
+    /**
+     * Returns the outermost filter in a filter chain.
+     *
+     * When methods in a subclass needs to refer to a policy on an item (class, fields, methods)
+     * that are not the "subject" item -- e.g.
+     * in [ClassWidePolicyPropagatingFilter.getPolicyForField], when it checks the
+     * class policy -- [outermostFilter] must be used, rather than the super's method.
+     * The former will always return the correct policy, but the later won't consult outer
+     * filters than the current filter.
+     */
     override var outermostFilter: OutputFilter = this
         get() = field
         set(value) {
             field = value
-            // Propagate the inner filters.
+            // Propagate to the inner filters.
             fallback.outermostFilter = value
         }
 
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 84856ac..ea7d1d0 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -149,7 +149,7 @@
         val fallback = super.getPolicyForField(className, fieldName)
 
         val cn = classes.getClass(className)
-        val classPolicy = super.getPolicyForClass(className)
+        val classPolicy = outermostFilter.getPolicyForClass(className)
 
         log.d("Class ${cn.name} Class policy: $classPolicy")
         if (classPolicy.policy.needsInImpl) {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 214de59..3956893 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -223,16 +223,16 @@
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
     )
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
   Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
   minor version: 0
   major version: 61
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
     Code:
@@ -243,7 +243,7 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
 
   public static int addTwo(int);
     descriptor: (I)I
@@ -262,7 +262,8 @@
 SourceFile: "IPretendingAidl.java"
 NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
 InnerClasses:
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;          // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 ## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
   Compiled from "IPretendingAidl.java"
 public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
@@ -303,6 +304,7 @@
 NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
 InnerClasses:
   public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 ## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
   Compiled from "IPretendingAidl.java"
 public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
@@ -315,11 +317,11 @@
 }
 SourceFile: "IPretendingAidl.java"
 NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
 InnerClasses:
-  public static #x= #x of #x;            // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 9031228..9349355 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -1,13 +1,13 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
   Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
   minor version: 0
   major version: 61
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
     Code:
@@ -30,7 +30,8 @@
          x: athrow
 }
 InnerClasses:
-  public static #x= #x of #x;            // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -71,6 +72,7 @@
 }
 InnerClasses:
   public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -89,8 +91,8 @@
   interfaces: 0, fields: 0, methods: 0, attributes: 4
 }
 InnerClasses:
-  public static #x= #x of #x;            // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -98,8 +100,8 @@
   x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index e01f49b..4f8c408e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -205,16 +205,16 @@
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
   Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
   minor version: 0
   major version: 61
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
     Code:
@@ -225,7 +225,7 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
 
   public static int addTwo(int);
     descriptor: (I)I
@@ -242,7 +242,8 @@
             0       4     0     a   I
 }
 InnerClasses:
-  public static #x= #x of #x;            // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -288,6 +289,7 @@
 }
 InnerClasses:
   public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -306,8 +308,8 @@
   interfaces: 0, fields: 0, methods: 0, attributes: 4
 }
 InnerClasses:
-  public static #x= #x of #x;            // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -315,8 +317,8 @@
   x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 9031228..9349355 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -1,13 +1,13 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
   Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
   minor version: 0
   major version: 61
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
     Code:
@@ -30,7 +30,8 @@
          x: athrow
 }
 InnerClasses:
-  public static #x= #x of #x;            // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -71,6 +72,7 @@
 }
 InnerClasses:
   public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -89,8 +91,8 @@
   interfaces: 0, fields: 0, methods: 0, attributes: 4
 }
 InnerClasses:
-  public static #x= #x of #x;            // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -98,8 +100,8 @@
   x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 5246355..5ff3cde 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -289,13 +289,13 @@
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
   Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
   minor version: 0
   major version: 61
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 3, attributes: 4
   private static {};
@@ -303,17 +303,17 @@
     flags: (0x000a) ACC_PRIVATE, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
          x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
          x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
          x: return
 
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
          x: ldc           #x                 // String <init>
          x: ldc           #x                 // String ()V
          x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -324,14 +324,14 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
 
   public static int addTwo(int);
     descriptor: (I)I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
          x: ldc           #x                 // String addTwo
          x: ldc           #x                 // String (I)I
          x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -346,7 +346,8 @@
            11       4     0     a   I
 }
 InnerClasses:
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;          // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -412,6 +413,7 @@
 }
 InnerClasses:
   public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -439,8 +441,8 @@
          x: return
 }
 InnerClasses:
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
   public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;          // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
 SourceFile: "IPretendingAidl.java"
 RuntimeVisibleAnnotations:
   x: #x()
@@ -448,8 +450,8 @@
   x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
   com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
   Compiled from "TinyFrameworkCallerCheck.java"
 class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
index 583e13c..0a07c2b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
@@ -25,11 +25,12 @@
         public static int addOne(int a) {
             return a + 1;
         }
-    }
 
-    public static class Proxy {
-        public static int addTwo(int a) {
-            return a + 2;
+        public static class Proxy {
+            public static int addTwo(int a) {
+                return a + 2;
+            }
         }
     }
+
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 0d52791..d350105 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -289,6 +289,6 @@
     @Test
     public void testAidlHeuristics() {
         assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2);
-        assertThat(IPretendingAidl.Proxy.addTwo(1)).isEqualTo(3);
+        assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3);
     }
 }