Merge "Activity canceled due to settings launch should trigger a different code."
diff --git a/core/api/current.txt b/core/api/current.txt
index 63ac7f7..c811a09 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7412,6 +7412,7 @@
     method public void dump(android.util.Printer, String);
     method public android.content.pm.ActivityInfo getActivityInfo();
     method @NonNull public android.content.ComponentName getComponent();
+    method public int getHeadlessDeviceOwnerMode();
     method public String getPackageName();
     method public String getReceiverName();
     method public String getTagForPolicy(int);
@@ -7423,6 +7424,8 @@
     method public boolean usesPolicy(int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DeviceAdminInfo> CREATOR;
+    field public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1; // 0x1
+    field public static final int HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED = 0; // 0x0
     field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7
     field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8
     field public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9; // 0x9
@@ -28411,15 +28414,24 @@
 
   public final class NfcAdapter {
     method public void disableForegroundDispatch(android.app.Activity);
+    method @Deprecated public void disableForegroundNdefPush(android.app.Activity);
     method public void disableReaderMode(android.app.Activity);
     method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
+    method @Deprecated public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
     method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
     method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
     method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
+    method @Deprecated public boolean invokeBeam(android.app.Activity);
     method public boolean isEnabled();
+    method @Deprecated public boolean isNdefPushEnabled();
     method public boolean isSecureNfcEnabled();
     method public boolean isSecureNfcSupported();
+    method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity);
+    method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
+    method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
+    method @Deprecated public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
+    method @Deprecated public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
     field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
     field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
@@ -28451,6 +28463,18 @@
     field public static final int STATE_TURNING_ON = 2; // 0x2
   }
 
+  @Deprecated public static interface NfcAdapter.CreateBeamUrisCallback {
+    method @Deprecated public android.net.Uri[] createBeamUris(android.nfc.NfcEvent);
+  }
+
+  @Deprecated public static interface NfcAdapter.CreateNdefMessageCallback {
+    method @Deprecated public android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
+  }
+
+  @Deprecated public static interface NfcAdapter.OnNdefPushCompleteCallback {
+    method @Deprecated public void onNdefPushComplete(android.nfc.NfcEvent);
+  }
+
   public static interface NfcAdapter.OnTagRemovedListener {
     method public void onTagRemoved();
   }
@@ -44882,6 +44906,7 @@
     method public int getCardIdForDefaultEuicc();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
     method public int getCarrierIdFromSimMccMnc();
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void getCarrierRestrictionStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public int getDataNetworkType();
@@ -45041,6 +45066,10 @@
     field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
     field public static final int CALL_STATE_RINGING = 1; // 0x1
     field public static final String CAPABILITY_SLICING_CONFIG_SUPPORTED = "CAPABILITY_SLICING_CONFIG_SUPPORTED";
+    field public static final int CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED = 1; // 0x1
+    field public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED = 2; // 0x2
+    field public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER = 3; // 0x3
+    field public static final int CARRIER_RESTRICTION_STATUS_UNKNOWN = 0; // 0x0
     field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
     field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
     field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
@@ -51032,6 +51061,7 @@
     method public void relayout(int, int);
     method public void release();
     method public void setView(@NonNull android.view.View, int, int);
+    method public boolean transferTouchGestureToHost();
   }
 
   public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 55ef6de..af35d96 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -83,7 +83,7 @@
 
   public abstract class Context {
     method @NonNull public android.content.Context createContextForSdkInSandbox(@NonNull android.content.pm.ApplicationInfo, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @NonNull public android.os.IBinder getIApplicationThreadBinder();
+    method @NonNull public android.os.IBinder getProcessToken();
     method @NonNull public android.os.UserHandle getUser();
     field public static final String PAC_PROXY_SERVICE = "pac_proxy";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 5c4fd10..1fa1e89 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -252,34 +252,6 @@
 
 }
 
-package android.nfc {
-
-  public final class NfcAdapter {
-    method @Deprecated public void disableForegroundNdefPush(android.app.Activity);
-    method @Deprecated public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
-    method @Deprecated public boolean invokeBeam(android.app.Activity);
-    method @Deprecated public boolean isNdefPushEnabled();
-    method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity);
-    method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
-    method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
-    method @Deprecated public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
-    method @Deprecated public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
-  }
-
-  @Deprecated public static interface NfcAdapter.CreateBeamUrisCallback {
-    method public android.net.Uri[] createBeamUris(android.nfc.NfcEvent);
-  }
-
-  @Deprecated public static interface NfcAdapter.CreateNdefMessageCallback {
-    method public android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
-  }
-
-  @Deprecated public static interface NfcAdapter.OnNdefPushCompleteCallback {
-    method public void onNdefPushComplete(android.nfc.NfcEvent);
-  }
-
-}
-
 package android.os {
 
   public class BatteryManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5183a82..d3775ad 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1278,6 +1278,7 @@
     field public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
     field public static final int STATUS_HAS_DEVICE_OWNER = 1; // 0x1
     field public static final int STATUS_HAS_PAIRED = 8; // 0x8
+    field public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED = 16; // 0x10
     field public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
     field public static final int STATUS_NONSYSTEM_USER_EXISTS = 5; // 0x5
     field public static final int STATUS_NOT_SYSTEM_USER = 7; // 0x7
@@ -3269,6 +3270,7 @@
     field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
     field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
     field public static final String TETHERING_SERVICE = "tethering";
+    field public static final String TIME_MANAGER_SERVICE = "time_manager";
     field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
     field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
     field public static final String UWB_SERVICE = "uwb";
@@ -9692,7 +9694,9 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
@@ -9701,8 +9705,10 @@
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
+    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+    field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
     field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
     field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0
     field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 1c10356..2c5acf1 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -140,17 +140,6 @@
 
 }
 
-package android.nfc {
-
-  public final class NfcAdapter {
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush();
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
-    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
-    field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
-  }
-
-}
-
 package android.os {
 
   public class Build {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 94075e5..44f3121 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1414,6 +1414,8 @@
 package android.hardware.location {
 
   public final class ContextHubManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public boolean disableTestMode();
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public boolean enableTestMode();
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public long[] getPreloadedNanoAppIds(@NonNull android.hardware.location.ContextHubInfo);
   }
 
@@ -1627,20 +1629,27 @@
 
   public class AudioManager {
     method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void forceComputeCsdOnAllDevices(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void forceUseFrameworkMel(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
     method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public float getCsd();
     method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
     method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
     method @Nullable public static android.media.AudioHalVersionInfo getHalVersion();
     method public static final int[] getPublicStreamTypes();
     method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public float getRs2Value();
     method public int getStreamMinVolumeInt(int);
     method @NonNull public java.util.Map<java.lang.Integer,java.lang.Boolean> getSurroundFormats();
     method public boolean hasRegisteredDynamicPolicy();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public boolean isCsdEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
     method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
     method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setCsd(float);
     method public void setRampingRingerEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setRs2Value(float);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean);
   }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b91fa35..89740af 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2028,7 +2028,7 @@
     /** @hide */
     @NonNull
     @Override
-    public IBinder getIApplicationThreadBinder() {
+    public IBinder getProcessToken() {
         return getIApplicationThread().asBinder();
     }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5b3b2a6..1f5182a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1335,7 +1335,7 @@
                         return new TimeZoneDetectorImpl();
                     }});
 
-        registerService(Context.TIME_MANAGER, TimeManager.class,
+        registerService(Context.TIME_MANAGER_SERVICE, TimeManager.class,
                 new CachedServiceFetcher<TimeManager>() {
                     @Override
                     public TimeManager createService(ContextImpl ctx)
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 67408a4..e4ee959 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -16,6 +16,7 @@
 
 package android.app.admin;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -158,6 +159,24 @@
      */
     public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;
 
+
+    /**
+     * Value for {@link #getHeadlessDeviceOwnerMode} which indicates that this DPC should not
+     * be provisioned into Device Owner mode on a Headless System User Mode device.
+     */
+    public static final int HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED = 0;
+
+    /**
+     * Value for {@link #getHeadlessDeviceOwnerMode} which indicates that this DPC should be
+     * provisioned into "affiliated" mode when on a Headless System User Mode device.
+     *
+     * <p>This mode adds a Profile Owner to all users other than the user the Device Owner is on.
+     */
+    public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1;
+
+    @IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED})
+    private @interface HeadlessDeviceOwnerMode {}
+
     /** @hide */
     public static class PolicyInfo {
         public final int ident;
@@ -255,6 +274,8 @@
      */
     boolean mSupportsTransferOwnership;
 
+    @HeadlessDeviceOwnerMode int mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+
     /**
      * Constructor.
      *
@@ -341,6 +362,17 @@
                                 "support-transfer-ownership tag must be empty.");
                     }
                     mSupportsTransferOwnership = true;
+                } else if (tagName.equals("headless-system-user")) {
+                    String deviceOwnerModeStringValue =
+                            parser.getAttributeValue(null, "device-owner-mode");
+
+                    if (deviceOwnerModeStringValue.equalsIgnoreCase("unsupported")) {
+                        mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+                    } else if (deviceOwnerModeStringValue.equalsIgnoreCase("affiliated")) {
+                        mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
+                    } else {
+                        throw new XmlPullParserException("headless-system-user mode must be valid");
+                    }
                 }
             }
         } catch (NameNotFoundException e) {
@@ -355,6 +387,7 @@
         mActivityInfo = ActivityInfo.CREATOR.createFromParcel(source);
         mUsesPolicies = source.readInt();
         mSupportsTransferOwnership = source.readBoolean();
+        mHeadlessDeviceOwnerMode = source.readInt();
     }
 
     /**
@@ -460,6 +493,14 @@
         return mSupportsTransferOwnership;
     }
 
+    /**
+     * Returns the mode this DeviceAdmin wishes to use if provisioned as a Device Owner on a
+     * headless system user mode device.
+     */
+    public @HeadlessDeviceOwnerMode int getHeadlessDeviceOwnerMode() {
+        return mHeadlessDeviceOwnerMode;
+    }
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public ArrayList<PolicyInfo> getUsedPolicies() {
@@ -505,6 +546,7 @@
         mActivityInfo.writeToParcel(dest, flags);
         dest.writeInt(mUsesPolicies);
         dest.writeBoolean(mSupportsTransferOwnership);
+        dest.writeInt(mHeadlessDeviceOwnerMode);
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 11584cc..7e5523a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,10 +16,10 @@
 
 package android.app.admin;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.QUERY_ADMIN_POLICY;
 import static android.Manifest.permission.SET_TIME;
 import static android.Manifest.permission.SET_TIME_ZONE;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
 import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
 
@@ -2812,6 +2812,17 @@
     public static final int STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15;
 
     /**
+     * Result code for {@link #checkProvisioningPrecondition}.
+     *
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when provisioning a DPC which does
+     * not support headless system user mode on a headless system user mode device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED = 16;
+
+    /**
      * Result codes for {@link #checkProvisioningPrecondition} indicating all the provisioning pre
      * conditions.
      *
@@ -2824,7 +2835,8 @@
             STATUS_HAS_PAIRED, STATUS_MANAGED_USERS_NOT_SUPPORTED, STATUS_SYSTEM_USER,
             STATUS_CANNOT_ADD_MANAGED_PROFILE, STATUS_DEVICE_ADMIN_NOT_SUPPORTED,
             STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER,
-            STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS
+            STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS,
+            STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED
     })
     public @interface ProvisioningPrecondition {}
 
diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java
index e35e359..7cb6c34 100644
--- a/core/java/android/app/time/TimeManager.java
+++ b/core/java/android/app/time/TimeManager.java
@@ -41,7 +41,7 @@
  * @hide
  */
 @SystemApi
-@SystemService(Context.TIME_MANAGER)
+@SystemService(Context.TIME_MANAGER_SERVICE)
 public final class TimeManager {
     private static final String TAG = "time.TimeManager";
     private static final boolean DEBUG = false;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a58cac3..7310138 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5854,7 +5854,7 @@
     public static final String SECURE_ELEMENT_SERVICE = "secure_element";
 
     /**
-     * Use with {@link #getSystemService(String)} to retrieve an
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.app.timedetector.TimeDetector}.
      * @hide
      *
@@ -5863,7 +5863,7 @@
     public static final String TIME_DETECTOR_SERVICE = "time_detector";
 
     /**
-     * Use with {@link #getSystemService(String)} to retrieve an
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.app.timezonedetector.TimeZoneDetector}.
      * @hide
      *
@@ -5872,12 +5872,14 @@
     public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
 
     /**
-     * Use with {@link #getSystemService(String)} to retrieve an {@link TimeManager}.
+     * Use with {@link #getSystemService(String)} to retrieve a {@link TimeManager}.
      * @hide
      *
      * @see #getSystemService(String)
      */
-    public static final String TIME_MANAGER = "time_manager";
+    @SystemApi
+    @SuppressLint("ServiceName")
+    public static final String TIME_MANAGER_SERVICE = "time_manager";
 
     /**
      * Binder service name for {@link AppBindingService}.
@@ -7527,14 +7529,12 @@
     }
 
     /**
-     * Get the binder object associated with the IApplicationThread of this Context.
-     *
-     * This can be used by a mainline module to uniquely identify a specific app process.
+     * Used by a mainline module to uniquely identify a specific app process.
      * @hide
      */
     @NonNull
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public IBinder getIApplicationThreadBinder() {
+    public IBinder getProcessToken() {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e65e91c..9027e2e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1286,8 +1286,8 @@
      * @hide
      */
     @Override
-    public IBinder getIApplicationThreadBinder() {
-        return mBase.getIApplicationThreadBinder();
+    public IBinder getProcessToken() {
+        return mBase.getProcessToken();
     }
 
     /**
diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java
index c77a372..ffdc7b38 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -42,7 +42,7 @@
                         /* fromSp= */
                         new float[] {   8f,   10f,   12f,   14f,   18f,   20f,   24f,   30f,  100},
                         /* toDp=   */
-                        new float[] { 9.2f, 11.5f, 13.8f, 16.1f, 20.7f,   23f, 27.6f, 34.5f,  115})
+                        new float[] { 9.2f, 11.5f, 13.8f, 16.4f, 19.8f, 21.8f, 25.2f,   30f,  100})
         );
 
         put(
@@ -51,7 +51,7 @@
                         /* fromSp= */
                         new float[] {   8f,   10f,   12f,   14f,   18f,   20f,   24f,   30f,  100},
                         /* toDp=   */
-                        new float[] {10.4f,   13f, 15.6f, 18.2f, 23.4f,   26f, 31.2f,   39f,  130})
+                        new float[] {10.4f,   13f, 15.6f, 18.8f, 21.6f, 23.6f, 26.4f,   30f,  100})
         );
 
         put(
@@ -60,7 +60,7 @@
                         /* fromSp= */
                         new float[] {   8f,   10f,   12f,   14f,   18f,   20f,   24f,   30f,  100},
                         /* toDp=   */
-                        new float[] {  12f,   15f,   18f,   21f,   27f,   30f,   36f,   45f,  150})
+                        new float[] {  12f,   15f,   18f,   22f,   24f,   26f,   28f,   30f,  100})
         );
 
         put(
@@ -69,7 +69,7 @@
                         /* fromSp= */
                         new float[] {   8f,   10f,   12f,   14f,   18f,   20f,   24f,   30f,  100},
                         /* toDp=   */
-                        new float[] {14.4f,   18f, 21.6f, 25.2f, 32.4f,   36f, 43.2f,   54f,  180})
+                        new float[] {14.4f,   18f, 21.6f, 24.4f, 27.6f, 30.8f, 32.8f, 34.8f,  100})
         );
 
         put(
@@ -78,7 +78,7 @@
                         /* fromSp= */
                         new float[] {   8f,   10f,   12f,   14f,   18f,   20f,   24f,   30f,  100},
                         /* toDp=   */
-                        new float[] {  16f,   20f,   24f,   28f,   36f,   40f,   48f,   60f,  200})
+                        new float[] {  16f,   20f,   24f,   26f,   30f,   34f,   36f,   38f,  100})
         );
 
     }
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index ac23af4..01ce7b9 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -995,6 +995,51 @@
     }
 
     /**
+     * Puts the Context Hub in test mode.
+     *
+     * The purpose of this API is to make testing CHRE/Context Hub more
+     * predictable and robust. This temporarily unloads all
+     * nanoapps.
+     *
+     * Note that this API must not cause CHRE/Context Hub to behave differently
+     * in test compared to production.
+     *
+     * @return true if the enable test mode operation succeeded.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+    @NonNull public boolean enableTestMode() {
+        try {
+            return mService.setTestMode(true);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Puts the Context Hub out of test mode.
+     *
+     * This API will undo any previously made enableTestMode() calls.
+     * After this API is called, it should restore the state of the system
+     * to the normal/production mode before any enableTestMode() call was
+     * made. If the enableTestMode() call unloaded any nanoapps
+     * to enter test mode, it should reload those nanoapps in this API call.
+     *
+     * @return true if the disable operation succeeded.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+    @NonNull public boolean disableTestMode() {
+        try {
+            return mService.setTestMode(false);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Unregister a callback for receive messages from the context hub.
      *
      * @see Callback
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 490267f..11f30461 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -113,4 +113,8 @@
     // Queries for a list of preloaded nanoapps
     @EnforcePermission("ACCESS_CONTEXT_HUB")
     long[] getPreloadedNanoAppIds(in ContextHubInfo hubInfo);
+
+    // Enables or disables test mode
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
+    boolean setTestMode(in boolean enable);
 }
diff --git a/core/java/android/nfc/BeamShareData.aidl b/core/java/android/nfc/BeamShareData.aidl
new file mode 100644
index 0000000..a47e240
--- /dev/null
+++ b/core/java/android/nfc/BeamShareData.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 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.nfc;
+
+parcelable BeamShareData;
diff --git a/core/java/android/nfc/BeamShareData.java b/core/java/android/nfc/BeamShareData.java
new file mode 100644
index 0000000..6a40f98
--- /dev/null
+++ b/core/java/android/nfc/BeamShareData.java
@@ -0,0 +1,67 @@
+package android.nfc;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+/**
+ * Class to IPC data to be shared over Android Beam.
+ * Allows bundling NdefMessage, Uris and flags in a single
+ * IPC call. This is important as we want to reduce the
+ * amount of IPC calls at "touch time".
+ * @hide
+ */
+public final class BeamShareData implements Parcelable {
+    public final NdefMessage ndefMessage;
+    public final Uri[] uris;
+    public final UserHandle userHandle;
+    public final int flags;
+
+    public BeamShareData(NdefMessage msg, Uri[] uris, UserHandle userHandle, int flags) {
+        this.ndefMessage = msg;
+        this.uris = uris;
+        this.userHandle = userHandle;
+        this.flags = flags;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        int urisLength = (uris != null) ? uris.length : 0;
+        dest.writeParcelable(ndefMessage, 0);
+        dest.writeInt(urisLength);
+        if (urisLength > 0) {
+            dest.writeTypedArray(uris, 0);
+        }
+        dest.writeParcelable(userHandle, 0);
+        dest.writeInt(this.flags);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<BeamShareData> CREATOR =
+            new Parcelable.Creator<BeamShareData>() {
+        @Override
+        public BeamShareData createFromParcel(Parcel source) {
+            Uri[] uris = null;
+            NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader(), android.nfc.NdefMessage.class);
+            int numUris = source.readInt();
+            if (numUris > 0) {
+                uris = new Uri[numUris];
+                source.readTypedArray(uris, Uri.CREATOR);
+            }
+            UserHandle userHandle = source.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class);
+            int flags = source.readInt();
+
+            return new BeamShareData(msg, uris, userHandle, flags);
+        }
+
+        @Override
+        public BeamShareData[] newArray(int size) {
+            return new BeamShareData[size];
+        }
+    };
+}
diff --git a/core/java/android/nfc/IAppCallback.aidl b/core/java/android/nfc/IAppCallback.aidl
index b06bf06..133146d 100644
--- a/core/java/android/nfc/IAppCallback.aidl
+++ b/core/java/android/nfc/IAppCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.nfc.BeamShareData;
 import android.nfc.Tag;
 
 /**
@@ -23,5 +24,7 @@
  */
 interface IAppCallback
 {
+    BeamShareData createBeamShareData(byte peerLlcpVersion);
+    oneway void onNdefPushComplete(byte peerLlcpVersion);
     oneway void onTagDiscovered(in Tag tag);
 }
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index a6d8caf..8a30ef4 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.content.IntentFilter;
+import android.nfc.BeamShareData;
 import android.nfc.NdefMessage;
 import android.nfc.Tag;
 import android.nfc.TechListParcel;
@@ -46,18 +47,24 @@
     int getState();
     boolean disable(boolean saveState);
     boolean enable();
+    boolean enableNdefPush();
+    boolean disableNdefPush();
+    boolean isNdefPushEnabled();
     void pausePolling(int timeoutInMs);
     void resumePolling();
 
     void setForegroundDispatch(in PendingIntent intent,
             in IntentFilter[] filters, in TechListParcel techLists);
     void setAppCallback(in IAppCallback callback);
+    oneway void invokeBeam();
+    oneway void invokeBeamInternal(in BeamShareData shareData);
 
     boolean ignore(int nativeHandle, int debounceMs, ITagRemovedCallback callback);
 
     void dispatch(in Tag tag);
 
     void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
+    void setP2pModes(int initatorModes, int targetModes);
 
     void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
     void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 8d75cac..911aaf3 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -19,6 +19,9 @@
 import android.app.Activity;
 import android.app.Application;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentProvider;
+import android.content.Intent;
+import android.net.Uri;
 import android.nfc.NfcAdapter.ReaderCallback;
 import android.os.Binder;
 import android.os.Bundle;
@@ -107,8 +110,14 @@
     class NfcActivityState {
         boolean resumed = false;
         Activity activity;
-        NfcAdapter.ReaderCallback readerCallback = null;
+        NdefMessage ndefMessage = null;  // static NDEF message
+        NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
+        NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
+        NfcAdapter.CreateBeamUrisCallback uriCallback = null;
+        Uri[] uris = null;
+        int flags = 0;
         int readerModeFlags = 0;
+        NfcAdapter.ReaderCallback readerCallback = null;
         Bundle readerModeExtras = null;
         Binder token;
 
@@ -128,16 +137,24 @@
             unregisterApplication(activity.getApplication());
             resumed = false;
             activity = null;
-            readerCallback = null;
+            ndefMessage = null;
+            ndefMessageCallback = null;
+            onNdefPushCompleteCallback = null;
+            uriCallback = null;
+            uris = null;
             readerModeFlags = 0;
-            readerModeExtras = null;
             token = null;
         }
         @Override
         public String toString() {
-            StringBuilder s = new StringBuilder("[");
-            s.append(readerCallback);
-            s.append("]");
+            StringBuilder s = new StringBuilder("[").append(" ");
+            s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
+            s.append(uriCallback).append(" ");
+            if (uris != null) {
+                for (Uri uri : uris) {
+                    s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
+                }
+            }
             return s.toString();
         }
     }
@@ -228,6 +245,92 @@
         }
     }
 
+    public void setNdefPushContentUri(Activity activity, Uri[] uris) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.uris = uris;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
+            requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
+        }
+    }
+
+
+    public void setNdefPushContentUriCallback(Activity activity,
+            NfcAdapter.CreateBeamUrisCallback callback) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.uriCallback = callback;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
+            requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
+        }
+    }
+
+    public void setNdefPushMessage(Activity activity, NdefMessage message, int flags) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.ndefMessage = message;
+            state.flags = flags;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
+            requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
+        }
+    }
+
+    public void setNdefPushMessageCallback(Activity activity,
+            NfcAdapter.CreateNdefMessageCallback callback, int flags) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.ndefMessageCallback = callback;
+            state.flags = flags;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
+            requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
+        }
+    }
+
+    public void setOnNdefPushCompleteCallback(Activity activity,
+            NfcAdapter.OnNdefPushCompleteCallback callback) {
+        boolean isResumed;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            state.onNdefPushCompleteCallback = callback;
+            isResumed = state.resumed;
+        }
+        if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
+            requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
+        }
+    }
+
     /**
      * Request or unrequest NFC service callbacks.
      * Makes IPC call - do not hold lock.
@@ -248,6 +351,86 @@
         }
     }
 
+    /** Callback from NFC service, usually on binder thread */
+    @Override
+    public BeamShareData createBeamShareData(byte peerLlcpVersion) {
+        NfcAdapter.CreateNdefMessageCallback ndefCallback;
+        NfcAdapter.CreateBeamUrisCallback urisCallback;
+        NdefMessage message;
+        Activity activity;
+        Uri[] uris;
+        int flags;
+        NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findResumedActivityState();
+            if (state == null) return null;
+
+            ndefCallback = state.ndefMessageCallback;
+            urisCallback = state.uriCallback;
+            message = state.ndefMessage;
+            uris = state.uris;
+            flags = state.flags;
+            activity = state.activity;
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            // Make callbacks without lock
+            if (ndefCallback != null) {
+                message = ndefCallback.createNdefMessage(event);
+            }
+            if (urisCallback != null) {
+                uris = urisCallback.createBeamUris(event);
+                if (uris != null) {
+                    ArrayList<Uri> validUris = new ArrayList<Uri>();
+                    for (Uri uri : uris) {
+                        if (uri == null) {
+                            Log.e(TAG, "Uri not allowed to be null.");
+                            continue;
+                        }
+                        String scheme = uri.getScheme();
+                        if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
+                                !scheme.equalsIgnoreCase("content"))) {
+                            Log.e(TAG, "Uri needs to have " +
+                                    "either scheme file or scheme content");
+                            continue;
+                        }
+                        uri = ContentProvider.maybeAddUserId(uri, activity.getUserId());
+                        validUris.add(uri);
+                    }
+
+                    uris = validUris.toArray(new Uri[validUris.size()]);
+                }
+            }
+            if (uris != null && uris.length > 0) {
+                for (Uri uri : uris) {
+                    // Grant the NFC process permission to read these URIs
+                    activity.grantUriPermission("com.android.nfc", uri,
+                            Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return new BeamShareData(message, uris, activity.getUser(), flags);
+    }
+
+    /** Callback from NFC service, usually on binder thread */
+    @Override
+    public void onNdefPushComplete(byte peerLlcpVersion) {
+        NfcAdapter.OnNdefPushCompleteCallback callback;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = findResumedActivityState();
+            if (state == null) return;
+
+            callback = state.onNdefPushCompleteCallback;
+        }
+        NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
+        // Make callback without lock
+        if (callback != null) {
+            callback.onNdefPushComplete(event);
+        }
+    }
+
     @Override
     public void onTagDiscovered(Tag tag) throws RemoteException {
         NfcAdapter.ReaderCallback callback;
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index d947b48..a980158 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -345,10 +345,7 @@
      */
     public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
 
-    /**
-     * @hide
-     * @removed
-     */
+    /** @hide */
     @SystemApi
     public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
 
@@ -423,6 +420,7 @@
     // Guarded by NfcAdapter.class
     static boolean sIsInitialized = false;
     static boolean sHasNfcFeature;
+    static boolean sHasBeamFeature;
 
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -486,7 +484,7 @@
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
      * to another device.
      * @see #setOnNdefPushCompleteCallback
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -512,7 +510,7 @@
      * content currently visible to the user. Alternatively, you can call {@link
      * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the
      * same data.
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -542,7 +540,7 @@
 
 
      /**
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -574,6 +572,26 @@
     }
 
     /**
+     * Helper to check if this device has FEATURE_NFC_BEAM, but without using
+     * a context.
+     * Equivalent to
+     * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_BEAM)
+     */
+    private static boolean hasBeamFeature() {
+        IPackageManager pm = ActivityThread.getPackageManager();
+        if (pm == null) {
+            Log.e(TAG, "Cannot get package manager, assuming no Android Beam feature");
+            return false;
+        }
+        try {
+            return pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM, 0);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Package manager query failed, assuming no Android Beam feature", e);
+            return false;
+        }
+    }
+
+    /**
      * Helper to check if this device has FEATURE_NFC, but without using
      * a context.
      * Equivalent to
@@ -652,6 +670,7 @@
     public static synchronized NfcAdapter getNfcAdapter(Context context) {
         if (!sIsInitialized) {
             sHasNfcFeature = hasNfcFeature();
+            sHasBeamFeature = hasBeamFeature();
             boolean hasHceFeature = hasNfcHceFeature();
             /* is this device meant to have NFC */
             if (!sHasNfcFeature && !hasHceFeature) {
@@ -1144,7 +1163,7 @@
      * @param uris an array of Uri(s) to push over Android Beam
      * @param activity activity for which the Uri(s) will be pushed
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -1153,7 +1172,26 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        if (uris != null) {
+            for (Uri uri : uris) {
+                if (uri == null) throw new NullPointerException("Uri not " +
+                        "allowed to be null");
+                String scheme = uri.getScheme();
+                if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
+                        !scheme.equalsIgnoreCase("content"))) {
+                    throw new IllegalArgumentException("URI needs to have " +
+                            "either scheme file or scheme content");
+                }
+            }
+        }
+        mNfcActivityManager.setNdefPushContentUri(activity, uris);
     }
 
     /**
@@ -1213,7 +1251,7 @@
      * @param callback callback, or null to disable
      * @param activity activity for which the Uri(s) will be pushed
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -1222,7 +1260,14 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        mNfcActivityManager.setNdefPushContentUriCallback(activity, callback);
     }
 
     /**
@@ -1296,7 +1341,7 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -1306,12 +1351,36 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
+        }
+        int targetSdkVersion = getSdkVersion();
+        try {
+            if (activity == null) {
+                throw new NullPointerException("activity cannot be null");
+            }
+            mNfcActivityManager.setNdefPushMessage(activity, message, 0);
+            for (Activity a : activities) {
+                if (a == null) {
+                    throw new NullPointerException("activities cannot contain null");
+                }
+                mNfcActivityManager.setNdefPushMessage(a, message, 0);
+            }
+        } catch (IllegalStateException e) {
+            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+                // Less strict on old applications - just log the error
+                Log.e(TAG, "Cannot call API with Activity that has already " +
+                        "been destroyed", e);
+            } else {
+                // Prevent new applications from making this mistake, re-throw
+                throw(e);
+            }
         }
     }
 
     /**
      * @hide
-     * @removed
      */
     @SystemApi
     public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
@@ -1320,6 +1389,10 @@
                 throw new UnsupportedOperationException();
             }
         }
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        mNfcActivityManager.setNdefPushMessage(activity, message, flags);
     }
 
     /**
@@ -1387,7 +1460,7 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -1397,7 +1470,44 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
+        int targetSdkVersion = getSdkVersion();
+        try {
+            if (activity == null) {
+                throw new NullPointerException("activity cannot be null");
+            }
+            mNfcActivityManager.setNdefPushMessageCallback(activity, callback, 0);
+            for (Activity a : activities) {
+                if (a == null) {
+                    throw new NullPointerException("activities cannot contain null");
+                }
+                mNfcActivityManager.setNdefPushMessageCallback(a, callback, 0);
+            }
+        } catch (IllegalStateException e) {
+            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+                // Less strict on old applications - just log the error
+                Log.e(TAG, "Cannot call API with Activity that has already " +
+                        "been destroyed", e);
+            } else {
+                // Prevent new applications from making this mistake, re-throw
+                throw(e);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
+            int flags) {
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        mNfcActivityManager.setNdefPushMessageCallback(activity, callback, flags);
     }
 
     /**
@@ -1437,7 +1547,7 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -1447,6 +1557,31 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
+        }
+        int targetSdkVersion = getSdkVersion();
+        try {
+            if (activity == null) {
+                throw new NullPointerException("activity cannot be null");
+            }
+            mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
+            for (Activity a : activities) {
+                if (a == null) {
+                    throw new NullPointerException("activities cannot contain null");
+                }
+                mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
+            }
+        } catch (IllegalStateException e) {
+            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+                // Less strict on old applications - just log the error
+                Log.e(TAG, "Cannot call API with Activity that has already " +
+                        "been destroyed", e);
+            } else {
+                // Prevent new applications from making this mistake, re-throw
+                throw(e);
+            }
         }
     }
 
@@ -1629,7 +1764,7 @@
      * @param activity the current foreground Activity that has registered data to share
      * @return whether the Beam animation was successfully invoked
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -1638,8 +1773,37 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return false;
+            }
         }
-        return false;
+        if (activity == null) {
+            throw new NullPointerException("activity may not be null.");
+        }
+        enforceResumed(activity);
+        try {
+            sService.invokeBeam();
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "invokeBeam: NFC process has died.");
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public boolean invokeBeam(BeamShareData shareData) {
+        try {
+            Log.e(TAG, "invokeBeamInternal()");
+            sService.invokeBeamInternal(shareData);
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG, "invokeBeam: NFC process has died.");
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
     }
 
     /**
@@ -1665,9 +1829,9 @@
      *
      * @param activity foreground activity
      * @param message a NDEF Message to push over NFC
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
-     * @removed this feature is removed. File sharing can work using other technology like
-     * Bluetooth.
+     * @throws IllegalStateException if the activity is not currently in the foreground
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated use {@link #setNdefPushMessage} instead
      */
     @Deprecated
     public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
@@ -1675,7 +1839,15 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
+        if (activity == null || message == null) {
+            throw new NullPointerException();
+        }
+        enforceResumed(activity);
+        mNfcActivityManager.setNdefPushMessage(activity, message, 0);
     }
 
     /**
@@ -1694,9 +1866,9 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param activity the Foreground activity
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
-     * @removed this feature is removed. File sharing can work using other technology like
-     * Bluetooth.
+     * @throws IllegalStateException if the Activity has already been paused
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated use {@link #setNdefPushMessage} instead
      */
     @Deprecated
     public void disableForegroundNdefPush(Activity activity) {
@@ -1704,7 +1876,17 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
+        if (activity == null) {
+            throw new NullPointerException();
+        }
+        enforceResumed(activity);
+        mNfcActivityManager.setNdefPushMessage(activity, null, 0);
+        mNfcActivityManager.setNdefPushMessageCallback(activity, null, 0);
+        mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null);
     }
 
     /**
@@ -1829,24 +2011,40 @@
      * Enable NDEF Push feature.
      * <p>This API is for the Settings application.
      * @hide
-     * @removed
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean enableNdefPush() {
-        return false;
+        if (!sHasNfcFeature) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return sService.enableNdefPush();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
     }
 
     /**
      * Disable NDEF Push feature.
      * <p>This API is for the Settings application.
      * @hide
-     * @removed
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean disableNdefPush() {
-        return false;
+        synchronized (NfcAdapter.class) {
+            if (!sHasNfcFeature) {
+                throw new UnsupportedOperationException();
+            }
+        }
+        try {
+            return sService.disableNdefPush();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
     }
 
     /**
@@ -1872,17 +2070,26 @@
      * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS
      * @return true if NDEF Push feature is enabled
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @removed this feature is removed. File sharing can work using other technology like
+     * @deprecated this feature is deprecated. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
+
     public boolean isNdefPushEnabled() {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return false;
+            }
         }
-        return false;
+        try {
+            return sService.isNdefPushEnabled();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
     }
 
     /**
@@ -1973,6 +2180,17 @@
     }
 
     /**
+     * @hide
+     */
+    public void setP2pModes(int initiatorModes, int targetModes) {
+        try {
+            sService.setP2pModes(initiatorModes, targetModes);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
      * Registers a new NFC unlock handler with the NFC service.
      *
      * <p />NFC unlock handlers are intended to unlock the keyguard in the presence of a trusted
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 32773a0..249f486 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -754,9 +754,12 @@
          * PackageManager.setComponentEnabledSetting} will now throw an
          * IllegalArgumentException if the given component class name does not
          * exist in the application's manifest.
-         * <li> {@code NfcAdapter.setNdefPushMessage},
-         * {@code NfcAdapter.setNdefPushMessageCallback} and
-         * {@code NfcAdapter.setOnNdefPushCompleteCallback} will throw
+         * <li> {@link android.nfc.NfcAdapter#setNdefPushMessage
+         * NfcAdapter.setNdefPushMessage},
+         * {@link android.nfc.NfcAdapter#setNdefPushMessageCallback
+         * NfcAdapter.setNdefPushMessageCallback} and
+         * {@link android.nfc.NfcAdapter#setOnNdefPushCompleteCallback
+         * NfcAdapter.setOnNdefPushCompleteCallback} will throw
          * IllegalStateException if called after the Activity has been destroyed.
          * <li> Accessibility services must require the new
          * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission or
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 62b99ba..5b05f21 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1711,6 +1711,7 @@
      * Input: Nothing.
      * <p>
      * Output: Nothing
+     * @see android.nfc.NfcAdapter#isNdefPushEnabled()
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_NFCSHARING_SETTINGS =
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index fc47e69..5810642 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -153,6 +153,12 @@
     boolean performHapticFeedback(int effectId, boolean always);
 
     /**
+     * Called by attached views to perform predefined haptic feedback without requiring VIBRATE
+     * permission.
+     */
+    oneway void performHapticFeedbackAsync(int effectId, boolean always);
+
+    /**
      * Initiate the drag operation itself
      *
      * @param window Window which initiates drag operation.
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 3efbb75..f6348d7 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -485,7 +485,6 @@
      * SurfaceControlViewHost was created with an associated hostInputToken.
      *
      * @return Whether the touch stream was transferred.
-     * @hide
      */
     public boolean transferTouchGestureToHost() {
         if (mViewRoot == null) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2df4b5b..5dccd06 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -280,6 +280,14 @@
     private static final boolean ENABLE_INPUT_LATENCY_TRACKING = true;
 
     /**
+     * Controls whether to use the new oneway performHapticFeedback call. This returns
+     * true in a few more conditions, but doesn't affect which haptics happen. Notably, it
+     * makes the call to performHapticFeedback non-blocking, which reduces potential UI jank.
+     * This is intended as a temporary flag, ultimately becoming permanently 'true'.
+     */
+    private static final boolean USE_ASYNC_PERFORM_HAPTIC_FEEDBACK = true;
+
+    /**
      * Whether the caption is drawn by the shell.
      * @hide
      */
@@ -8583,7 +8591,13 @@
         }
 
         try {
-            return mWindowSession.performHapticFeedback(effectId, always);
+            if (USE_ASYNC_PERFORM_HAPTIC_FEEDBACK) {
+                mWindowSession.performHapticFeedbackAsync(effectId, always);
+                return true;
+            } else {
+                // Original blocking binder call path.
+                return mWindowSession.performHapticFeedback(effectId, always);
+            }
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 4ddd485..7d37c50 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -466,6 +466,11 @@
     }
 
     @Override
+    public void performHapticFeedbackAsync(int effectId, boolean always) {
+        performHapticFeedback(effectId, always);
+    }
+
+    @Override
     public android.os.IBinder performDrag(android.view.IWindow window, int flags,
             android.view.SurfaceControl surface, int touchSource, float touchX, float touchY,
             float thumbCenterX, float thumbCenterY, android.content.ClipData data) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 95a42aa..f2f4557 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -73,10 +73,12 @@
 import android.text.SpannedString;
 import android.text.StaticLayout;
 import android.text.TextUtils;
+import android.text.method.InsertModeTransformationMethod;
 import android.text.method.KeyListener;
 import android.text.method.MetaKeyKeyListener;
 import android.text.method.MovementMethod;
 import android.text.method.OffsetMapping;
+import android.text.method.TransformationMethod;
 import android.text.method.WordIterator;
 import android.text.style.EasyEditSpan;
 import android.text.style.SuggestionRangeSpan;
@@ -136,6 +138,7 @@
 import android.window.OnBackInvokedDispatcher;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.inputmethod.EditableInputConnection;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -461,6 +464,7 @@
     private int mLineChangeSlopMin;
 
     private final AccessibilitySmartActions mA11ySmartActions;
+    private InsertModeController mInsertModeController;
 
     Editor(TextView textView) {
         mTextView = textView;
@@ -2109,6 +2113,10 @@
             }
         }
 
+        if (mInsertModeController != null) {
+            mInsertModeController.onDraw(canvas);
+        }
+
         if (mTextView.canHaveDisplayList() && canvas.isHardwareAccelerated()) {
             drawHardwareAccelerated(canvas, layout, highlightPaths, highlightPaints,
                     selectionHighlight, selectionHighlightPaint, cursorOffsetVertical);
@@ -8054,6 +8062,110 @@
         }
     }
 
+    private static final class InsertModeController {
+        private final TextView mTextView;
+        private boolean mIsInsertModeActive;
+        private InsertModeTransformationMethod mInsertModeTransformationMethod;
+        private final Paint mHighlightPaint;
+
+        InsertModeController(@NonNull TextView textView) {
+            mTextView = Objects.requireNonNull(textView);
+            mIsInsertModeActive = false;
+            mInsertModeTransformationMethod = null;
+            mHighlightPaint = new Paint();
+
+            // The highlight color is supposed to be 12% of the color primary40. We can't
+            // directly access Material 3 theme. But because Material 3 sets the colorPrimary to
+            // be primary40, here we hardcoded it to be 12% of colorPrimary.
+            final TypedValue typedValue = new TypedValue();
+            mTextView.getContext().getTheme()
+                    .resolveAttribute(R.attr.colorPrimary, typedValue, true);
+            final int colorPrimary = typedValue.data;
+            final int highlightColor = ColorUtils.setAlphaComponent(colorPrimary,
+                    (int) (0.12f * Color.alpha(colorPrimary)));
+            mHighlightPaint.setColor(highlightColor);
+        }
+
+        /**
+         * Enter insert mode.
+         * @param offset the index to set the cursor.
+         * @return true if the call is successful. false if a) it's already in the insert mode,
+         * b) it failed to enter the insert mode.
+         */
+        boolean enterInsertMode(int offset) {
+            if (mIsInsertModeActive) return false;
+
+            TransformationMethod oldTransformationMethod =
+                    mTextView.getTransformationMethod();
+            if (oldTransformationMethod instanceof OffsetMapping) {
+                // We can't support the case where the oldTransformationMethod is an OffsetMapping.
+                return false;
+            }
+
+            final boolean isSingleLine = mTextView.isSingleLine();
+            mInsertModeTransformationMethod = new InsertModeTransformationMethod(offset,
+                    isSingleLine, oldTransformationMethod);
+            mTextView.setTransformationMethod(mInsertModeTransformationMethod);
+            Selection.setSelection((Spannable) mTextView.getText(), offset);
+
+            mIsInsertModeActive = true;
+            return true;
+        }
+
+        void exitInsertMode() {
+            if (!mIsInsertModeActive) return;
+            if (mInsertModeTransformationMethod == null
+                    || mInsertModeTransformationMethod != mTextView.getTransformationMethod()) {
+                // If mInsertionModeTransformationMethod doesn't match the one on TextView,
+                // something else have changed the TextView's TransformationMethod while the
+                // insertion mode is active. We don't need to restore the oldTransformationMethod.
+                // TODO(265871733): support the case where setTransformationMethod is called in
+                // the insert mode.
+                mIsInsertModeActive = false;
+                return;
+            }
+            // Changing TransformationMethod will reset selection range to [0, 0), we need to
+            // manually restore the old selection range.
+            final int selectionStart = mTextView.getSelectionStart();
+            final int selectionEnd = mTextView.getSelectionEnd();
+            final TransformationMethod oldTransformationMethod =
+                    mInsertModeTransformationMethod.getOldTransformationMethod();
+            mTextView.setTransformationMethod(oldTransformationMethod);
+            Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
+            mIsInsertModeActive = false;
+        }
+
+        void onDraw(Canvas canvas) {
+            if (!mIsInsertModeActive) return;
+            final CharSequence transformedText = mTextView.getTransformed();
+            if (transformedText instanceof InsertModeTransformationMethod.TransformedText) {
+                final Layout layout = mTextView.getLayout();
+                if (layout == null) return;
+                final InsertModeTransformationMethod.TransformedText insertModeTransformedText =
+                        ((InsertModeTransformationMethod.TransformedText) transformedText);
+                final int highlightStart = insertModeTransformedText.getHighlightStart();
+                final int highlightEnd = insertModeTransformedText.getHighlightEnd();
+                final Layout.SelectionRectangleConsumer consumer =
+                        (left, top, right, bottom, textSelectionLayout) ->
+                                canvas.drawRect(left, top, right, bottom, mHighlightPaint);
+                layout.getSelection(highlightStart, highlightEnd, consumer);
+            }
+        }
+    }
+
+    boolean enterInsertMode(int offset) {
+        if (mInsertModeController == null) {
+            if (mTextView == null) return false;
+            mInsertModeController = new InsertModeController(mTextView);
+        }
+        return mInsertModeController.enterInsertMode(offset);
+    }
+
+    void exitInsertMode() {
+        if (mInsertModeController == null) return;
+        mInsertModeController.exitInsertMode();
+    }
+
     /**
      * Initializes the nodeInfo with smart actions.
      */
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6ff808e..6e36a03 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -202,6 +202,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.InsertModeGesture;
 import android.view.inputmethod.JoinOrSplitGesture;
 import android.view.inputmethod.PreviewableHandwritingGesture;
 import android.view.inputmethod.RemoveSpaceGesture;
@@ -2534,7 +2535,7 @@
     /**
      * @hide
      */
-    @VisibleForTesting
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public CharSequence getTransformed() {
         return mTransformed;
     }
@@ -9620,6 +9621,7 @@
                 gestures.add(InsertGesture.class);
                 gestures.add(RemoveSpaceGesture.class);
                 gestures.add(JoinOrSplitGesture.class);
+                gestures.add(InsertModeGesture.class);
                 outAttrs.setSupportedHandwritingGestures(gestures);
 
                 Set<Class<? extends PreviewableHandwritingGesture>> previews = new ArraySet<>();
@@ -10169,6 +10171,27 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
     }
 
+    /** @hide */
+    public int performHandwritingInsertModeGesture(@NonNull InsertModeGesture gesture) {
+        final PointF insertPoint =
+                convertFromScreenToContentCoordinates(gesture.getInsertionPoint());
+        final int line = getLineForHandwritingGesture(insertPoint);
+        final CancellationSignal cancellationSignal = gesture.getCancellationSignal();
+
+        // If no cancellationSignal is provided, don't enter the insert mode.
+        if (line == -1 || cancellationSignal == null) {
+            return handleGestureFailure(gesture);
+        }
+
+        final int offset = mLayout.getOffsetForHorizontal(line, insertPoint.x);
+
+        if (!mEditor.enterInsertMode(offset)) {
+            return InputConnection.HANDWRITING_GESTURE_RESULT_FAILED;
+        }
+        cancellationSignal.setOnCancelListener(() -> mEditor.exitInsertMode());
+        return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
+    }
+
     private int handleGestureFailure(HandwritingGesture gesture) {
         return handleGestureFailure(gesture, /* isPreview= */ false);
     }
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 72b9cd2..818a503 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -73,6 +73,7 @@
 
 import java.io.PrintWriter;
 import java.util.Comparator;
+import java.util.concurrent.TimeUnit;
 
 public final class ProcessState {
     private static final String TAG = "ProcessStats";
@@ -1542,6 +1543,75 @@
         proto.write(fieldId, procName);
     }
 
+    /** Dumps the duration of each state to statsEventOutput. */
+    public void dumpStateDurationToStatsd(
+            int atomTag, ProcessStats processStats, StatsEventOutput statsEventOutput) {
+        long topMs = 0;
+        long fgsMs = 0;
+        long boundTopMs = 0;
+        long boundFgsMs = 0;
+        long importantForegroundMs = 0;
+        long cachedMs = 0;
+        long frozenMs = 0;
+        long otherMs = 0;
+        for (int i = 0, size = mDurations.getKeyCount(); i < size; i++) {
+            final int key = mDurations.getKeyAt(i);
+            final int type = SparseMappingTable.getIdFromKey(key);
+            int procStateIndex = type % STATE_COUNT;
+            long duration = mDurations.getValue(key);
+            switch (procStateIndex) {
+                case STATE_TOP:
+                    topMs += duration;
+                    break;
+                case STATE_BOUND_TOP_OR_FGS:
+                    boundTopMs += duration;
+                    break;
+                case STATE_FGS:
+                    fgsMs += duration;
+                    break;
+                case STATE_IMPORTANT_FOREGROUND:
+                case STATE_IMPORTANT_BACKGROUND:
+                    importantForegroundMs += duration;
+                    break;
+                case STATE_BACKUP:
+                case STATE_SERVICE:
+                case STATE_SERVICE_RESTARTING:
+                case STATE_RECEIVER:
+                case STATE_HEAVY_WEIGHT:
+                case STATE_HOME:
+                case STATE_LAST_ACTIVITY:
+                case STATE_PERSISTENT:
+                    otherMs += duration;
+                    break;
+                case STATE_CACHED_ACTIVITY:
+                case STATE_CACHED_ACTIVITY_CLIENT:
+                case STATE_CACHED_EMPTY:
+                    cachedMs += duration;
+                    break;
+                    // TODO (b/261910877) Add support for tracking boundFgsMs and
+                    // frozenMs.
+            }
+        }
+        statsEventOutput.write(
+                atomTag,
+                getUid(),
+                getName(),
+                (int) TimeUnit.MILLISECONDS.toSeconds(processStats.mTimePeriodStartUptime),
+                (int) TimeUnit.MILLISECONDS.toSeconds(processStats.mTimePeriodEndUptime),
+                (int)
+                        TimeUnit.MILLISECONDS.toSeconds(
+                                processStats.mTimePeriodEndUptime
+                                        - processStats.mTimePeriodStartUptime),
+                (int) TimeUnit.MILLISECONDS.toSeconds(topMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(fgsMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(boundTopMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(boundFgsMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(importantForegroundMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(cachedMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(frozenMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(otherMs));
+    }
+
     /** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
     public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto, long fieldId,
             String procName, int uid, long now,
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index d2b2f0a..f3ed09a 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -43,6 +43,7 @@
 import com.android.internal.app.ProcessMap;
 import com.android.internal.app.procstats.AssociationState.SourceKey;
 import com.android.internal.app.procstats.AssociationState.SourceState;
+import com.android.internal.util.function.QuintConsumer;
 
 import dalvik.system.VMRuntime;
 
@@ -56,6 +57,8 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -2389,6 +2392,79 @@
         }
     }
 
+    void forEachProcess(Consumer<ProcessState> consumer) {
+        final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        for (int ip = 0, size = procMap.size(); ip < size; ip++) {
+            final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+            for (int iu = 0, uidsSize = uids.size(); iu < uidsSize; iu++) {
+                final ProcessState processState = uids.valueAt(iu);
+                consumer.accept(processState);
+            }
+        }
+    }
+
+    void forEachAssociation(
+            QuintConsumer<AssociationState, Integer, String, SourceKey, SourceState> consumer) {
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
+        for (int ip = 0, size = pkgMap.size(); ip < size; ip++) {
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            for (int iu = 0, uidsSize = uids.size(); iu < uidsSize; iu++) {
+                final int uid = uids.keyAt(iu);
+                final LongSparseArray<PackageState> versions = uids.valueAt(iu);
+                for (int iv = 0, versionsSize = versions.size(); iv < versionsSize; iv++) {
+                    final PackageState state = versions.valueAt(iv);
+                    for (int iasc = 0, ascSize = state.mAssociations.size();
+                            iasc < ascSize;
+                            iasc++) {
+                        final String serviceName = state.mAssociations.keyAt(iasc);
+                        final AssociationState asc = state.mAssociations.valueAt(iasc);
+                        for (int is = 0, sourcesSize = asc.mSources.size();
+                                is < sourcesSize;
+                                is++) {
+                            final SourceState src = asc.mSources.valueAt(is);
+                            final SourceKey key = asc.mSources.keyAt(is);
+                            consumer.accept(asc, uid, serviceName, key, src);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /** Dumps the stats of all processes to statsEventOutput. */
+    public void dumpProcessState(int atomTag, StatsEventOutput statsEventOutput) {
+        forEachProcess(
+                (processState) -> {
+                    if (processState.isMultiPackage()
+                            && processState.getCommonProcess() != processState) {
+                        return;
+                    }
+                    processState.dumpStateDurationToStatsd(atomTag, this, statsEventOutput);
+                });
+    }
+
+    /** Dumps all process association data to statsEventOutput. */
+    public void dumpProcessAssociation(int atomTag, StatsEventOutput statsEventOutput) {
+        forEachAssociation(
+                (asc, serviceUid, serviceName, key, src) -> {
+                    statsEventOutput.write(
+                            atomTag,
+                            key.mUid,
+                            key.mProcess,
+                            serviceUid,
+                            serviceName,
+                            (int) TimeUnit.MILLISECONDS.toSeconds(mTimePeriodStartUptime),
+                            (int) TimeUnit.MILLISECONDS.toSeconds(mTimePeriodEndUptime),
+                            (int)
+                                    TimeUnit.MILLISECONDS.toSeconds(
+                                            mTimePeriodEndUptime - mTimePeriodStartUptime),
+                            (int) TimeUnit.MILLISECONDS.toSeconds(src.mDuration),
+                            src.mActiveCount,
+                            asc.getProcessName());
+                });
+    }
+
     private void dumpProtoPreamble(ProtoOutputStream proto) {
         proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
         proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
diff --git a/core/java/com/android/internal/app/procstats/StatsEventOutput.java b/core/java/com/android/internal/app/procstats/StatsEventOutput.java
new file mode 100644
index 0000000..b2e4054
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/StatsEventOutput.java
@@ -0,0 +1,98 @@
+/*
+ * 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.internal.app.procstats;
+
+import android.util.StatsEvent;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.List;
+
+/**
+ * A simple wrapper of FrameworkStatsLog.buildStatsEvent. This allows unit tests to mock out the
+ * dependency.
+ */
+public class StatsEventOutput {
+
+    List<StatsEvent> mOutput;
+
+    public StatsEventOutput(List<StatsEvent> output) {
+        mOutput = output;
+    }
+
+    /** Writes the data to the output. */
+    public void write(
+            int atomTag,
+            int uid,
+            String processName,
+            int measurementStartUptimeSecs,
+            int measurementEndUptimeSecs,
+            int measurementDurationUptimeSecs,
+            int topSeconds,
+            int fgsSeconds,
+            int boundTopSeconds,
+            int boundFgsSeconds,
+            int importantForegroundSeconds,
+            int cachedSeconds,
+            int frozenSeconds,
+            int otherSeconds) {
+        mOutput.add(
+                FrameworkStatsLog.buildStatsEvent(
+                        atomTag,
+                        uid,
+                        processName,
+                        measurementStartUptimeSecs,
+                        measurementEndUptimeSecs,
+                        measurementDurationUptimeSecs,
+                        topSeconds,
+                        fgsSeconds,
+                        boundTopSeconds,
+                        boundFgsSeconds,
+                        importantForegroundSeconds,
+                        cachedSeconds,
+                        frozenSeconds,
+                        otherSeconds));
+    }
+
+    /** Writes the data to the output. */
+    public void write(
+            int atomTag,
+            int clientUid,
+            String processName,
+            int serviceUid,
+            String serviceName,
+            int measurementStartUptimeSecs,
+            int measurementEndUptimeSecs,
+            int measurementDurationUptimeSecs,
+            int activeDurationUptimeSecs,
+            int activeCount,
+            String serviceProcessName) {
+        mOutput.add(
+                FrameworkStatsLog.buildStatsEvent(
+                        atomTag,
+                        clientUid,
+                        processName,
+                        serviceUid,
+                        serviceName,
+                        measurementStartUptimeSecs,
+                        measurementEndUptimeSecs,
+                        measurementDurationUptimeSecs,
+                        activeDurationUptimeSecs,
+                        activeCount,
+                        serviceProcessName));
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 8690e8d..3020d77 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -44,6 +44,7 @@
 import android.view.inputmethod.HandwritingGesture;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.InsertModeGesture;
 import android.view.inputmethod.JoinOrSplitGesture;
 import android.view.inputmethod.PreviewableHandwritingGesture;
 import android.view.inputmethod.RemoveSpaceGesture;
@@ -314,6 +315,8 @@
             result = mTextView.performHandwritingRemoveSpaceGesture((RemoveSpaceGesture) gesture);
         } else if (gesture instanceof JoinOrSplitGesture) {
             result = mTextView.performHandwritingJoinOrSplitGesture((JoinOrSplitGesture) gesture);
+        } else if (gesture instanceof InsertModeGesture) {
+            result = mTextView.performHandwritingInsertModeGesture((InsertModeGesture) gesture);
         } else {
             result = HANDWRITING_GESTURE_RESULT_UNSUPPORTED;
         }
diff --git a/core/proto/android/nfc/nfc_service.proto b/core/proto/android/nfc/nfc_service.proto
index 1dcd5cc..2df1d5d 100644
--- a/core/proto/android/nfc/nfc_service.proto
+++ b/core/proto/android/nfc/nfc_service.proto
@@ -60,7 +60,7 @@
     optional bool secure_nfc_capable = 13;
     optional bool vr_mode_enabled = 14;
     optional DiscoveryParamsProto discovery_params = 15;
-    reserved 16;
+    optional P2pLinkManagerProto p2p_link_manager = 16;
     optional com.android.nfc.cardemulation.CardEmulationManagerProto card_emulation_manager = 17;
     optional NfcDispatcherProto nfc_dispatcher = 18;
     optional string native_crash_logs = 19 [(.android.privacy).dest = DEST_EXPLICIT];
@@ -77,6 +77,38 @@
     optional bool enable_p2p = 5;
 }
 
+// Debugging information for com.android.nfc.P2pLinkManager
+message P2pLinkManagerProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    enum LinkState {
+        LINK_STATE_UNKNOWN = 0;
+        LINK_STATE_DOWN = 1;
+        LINK_STATE_DEBOUNCE = 2;
+        LINK_STATE_UP = 3;
+    }
+
+    enum SendState {
+        SEND_STATE_UNKNOWN = 0;
+        SEND_STATE_NOTHING_TO_SEND = 1;
+        SEND_STATE_NEED_CONFIRMATION = 2;
+        SEND_STATE_SENDING = 3;
+        SEND_STATE_COMPLETE = 4;
+        SEND_STATE_CANCELED = 5;
+    }
+
+    optional int32 default_miu = 1;
+    optional int32 default_rw_size = 2;
+    optional LinkState link_state = 3;
+    optional SendState send_state = 4;
+    optional int32 send_flags = 5;
+    optional bool send_enabled = 6;
+    optional bool receive_enabled = 7;
+    optional string callback_ndef = 8 [(.android.privacy).dest = DEST_EXPLICIT];
+    optional .android.nfc.NdefMessageProto message_to_send = 9;
+    repeated string uris_to_send = 10 [(.android.privacy).dest = DEST_EXPLICIT];
+}
+
 // Debugging information for com.android.nfc.NfcDispatcher
 message NfcDispatcherProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4b0db3b..be22095 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2003,6 +2003,9 @@
          STREAM_MUSIC as if it's on TV platform. -->
     <bool name="config_single_volume">false</bool>
 
+    <!-- Volume policy -->
+    <bool name="config_volume_down_to_enter_silent">false</bool>
+
     <!-- The number of volume steps for the notification stream -->
     <integer name="config_audio_notif_vol_steps">7</integer>
 
@@ -5414,6 +5417,9 @@
     <!-- Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. -->
     <bool name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled">false</bool>
 
+    <!-- Whether using display aspect ratio as a default aspect ratio for all letterboxed apps. -->
+    <bool name="config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled">false</bool>
+
     <!-- Whether the specific behaviour for translucent activities letterboxing is enabled.
          TODO(b/255532890) Enable when ignoreOrientationRequest is set -->
     <bool name="config_letterboxIsEnabledForTranslucentActivities">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 399d145..2715c94 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -328,6 +328,7 @@
   <java-symbol type="bool" name="config_use_strict_phone_number_comparation_for_kazakhstan" />
   <java-symbol type="integer" name="config_phonenumber_compare_min_match" />
   <java-symbol type="bool" name="config_single_volume" />
+  <java-symbol type="bool" name="config_volume_down_to_enter_silent" />
   <java-symbol type="bool" name="config_voice_capable" />
   <java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
   <java-symbol type="bool" name="config_user_notification_of_restrictied_mobile_access" />
@@ -4442,6 +4443,7 @@
   <java-symbol type="bool" name="config_letterboxIsEducationEnabled" />
   <java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" />
   <java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" />
+  <java-symbol type="bool" name="config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled" />
   <java-symbol type="bool" name="config_isCompatFakeFocusEnabled" />
   <java-symbol type="bool" name="config_isWindowManagerCameraCompatTreatmentEnabled" />
   <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
new file mode 100644
index 0000000..9b9a84b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.internal.app.procstats;
+
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.mockito.Mock;
+
+import java.util.concurrent.TimeUnit;
+
+/** Provides test cases for ProcessStats. */
+public class ProcessStatsTest extends TestCase {
+
+    private static final String APP_1_PACKAGE_NAME = "com.android.testapp";
+    private static final int APP_1_UID = 5001;
+    private static final long APP_1_VERSION = 10;
+    private static final String APP_1_PROCESS_NAME = "com.android.testapp.p";
+    private static final String APP_1_SERVICE_NAME = "com.android.testapp.service";
+
+    private static final String APP_2_PACKAGE_NAME = "com.android.testapp2";
+    private static final int APP_2_UID = 5002;
+    private static final long APP_2_VERSION = 30;
+    private static final String APP_2_PROCESS_NAME = "com.android.testapp2.p";
+
+    private static final long NOW_MS = 123000;
+    private static final int DURATION_SECS = 6;
+
+    @Mock StatsEventOutput mStatsEventOutput;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+    }
+
+    @SmallTest
+    public void testDumpProcessState() throws Exception {
+        ProcessStats processStats = new ProcessStats();
+        processStats.getProcessStateLocked(
+                APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+        processStats.getProcessStateLocked(
+                APP_2_PACKAGE_NAME, APP_2_UID, APP_2_VERSION, APP_2_PROCESS_NAME);
+        processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_STATE),
+                        eq(APP_1_UID),
+                        eq(APP_1_PROCESS_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0));
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_STATE),
+                        eq(APP_2_UID),
+                        eq(APP_2_PROCESS_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0));
+    }
+
+    @SmallTest
+    public void testNonZeroProcessStateDuration() throws Exception {
+        ProcessStats processStats = new ProcessStats();
+        ProcessState processState =
+                processStats.getProcessStateLocked(
+                        APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+        processState.setCombinedState(STATE_TOP, NOW_MS);
+        processState.commitStateTime(NOW_MS + TimeUnit.SECONDS.toMillis(DURATION_SECS));
+        processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_STATE),
+                        eq(APP_1_UID),
+                        eq(APP_1_PROCESS_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(DURATION_SECS),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0));
+    }
+
+    @SmallTest
+    public void testDumpProcessAssociation() throws Exception {
+        ProcessStats processStats = new ProcessStats();
+        AssociationState associationState =
+                processStats.getAssociationStateLocked(
+                        APP_1_PACKAGE_NAME,
+                        APP_1_UID,
+                        APP_1_VERSION,
+                        APP_1_PROCESS_NAME,
+                        APP_1_SERVICE_NAME);
+        AssociationState.SourceState sourceState =
+                associationState.startSource(APP_2_UID, APP_2_PROCESS_NAME, APP_2_PACKAGE_NAME);
+        sourceState.stop();
+        processStats.dumpProcessAssociation(
+                FrameworkStatsLog.PROCESS_ASSOCIATION, mStatsEventOutput);
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_ASSOCIATION),
+                        eq(APP_2_UID),
+                        eq(APP_2_PROCESS_NAME),
+                        eq(APP_1_UID),
+                        eq(APP_1_SERVICE_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(APP_1_PROCESS_NAME));
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 618c446..36ce0a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -445,7 +445,6 @@
                 backgroundColorForTransition = getTransitionBackgroundColorIfSet(info, change, a,
                         backgroundColorForTransition);
 
-                boolean delayedEdgeExtension = false;
                 if (!isTask && a.hasExtension()) {
                     if (!Transitions.isOpeningType(change.getMode())) {
                         // Can screenshot now (before startTransaction is applied)
@@ -455,7 +454,6 @@
                         // may not be visible or ready yet.
                         postStartTransactionCallbacks
                                 .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
-                        delayedEdgeExtension = true;
                     }
                 }
 
@@ -464,19 +462,9 @@
                         : new Rect(change.getEndAbsBounds());
                 clipRect.offsetTo(0, 0);
 
-                if (delayedEdgeExtension) {
-                    // If the edge extension needs to happen after the startTransition has been
-                    // applied, then we want to only start the animation after the edge extension
-                    // postStartTransaction callback has been run
-                    postStartTransactionCallbacks.add(t ->
-                            startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
-                                    mTransactionPool, mMainExecutor, mAnimExecutor,
-                                    change.getEndRelOffset(), cornerRadius, clipRect));
-                } else {
-                    startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
-                            mTransactionPool, mMainExecutor, mAnimExecutor,
-                            change.getEndRelOffset(), cornerRadius, clipRect);
-                }
+                buildSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+                        mTransactionPool, mMainExecutor, change.getEndRelOffset(), cornerRadius,
+                        clipRect);
 
                 if (info.getAnimationOptions() != null) {
                     attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
@@ -490,19 +478,25 @@
                     startTransaction, finishTransaction);
         }
 
-        // postStartTransactionCallbacks require that the start transaction is already
-        // applied to run otherwise they may result in flickers and UI inconsistencies.
-        boolean waitForStartTransactionApply = postStartTransactionCallbacks.size() > 0;
-        startTransaction.apply(waitForStartTransactionApply);
-
-        // Run tasks that require startTransaction to already be applied
-        for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
-                postStartTransactionCallbacks) {
-            final SurfaceControl.Transaction t = mTransactionPool.acquire();
-            postStartTransactionCallback.accept(t);
-            t.apply();
-            mTransactionPool.release(t);
+        if (postStartTransactionCallbacks.size() > 0) {
+            // postStartTransactionCallbacks require that the start transaction is already
+            // applied to run otherwise they may result in flickers and UI inconsistencies.
+            startTransaction.apply(true /* sync */);
+            // startTransaction is empty now, so fill it with the edge-extension setup
+            for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
+                    postStartTransactionCallbacks) {
+                postStartTransactionCallback.accept(startTransaction);
+            }
         }
+        startTransaction.apply();
+
+        // now start animations. they are started on another thread, so we have to post them
+        // *after* applying the startTransaction
+        mAnimExecutor.execute(() -> {
+            for (int i = 0; i < animations.size(); ++i) {
+                animations.get(i).start();
+            }
+        });
 
         mRotator.cleanUp(finishTransaction);
         TransitionMetrics.getInstance().reportAnimationStart(transition);
@@ -539,8 +533,8 @@
             animations.removeAll(animGroupStore);
             onAnimFinish.run();
         };
-        anim.startAnimation(animGroup, finishCallback, mTransitionAnimationScaleSetting,
-                mMainExecutor, mAnimExecutor);
+        anim.buildAnimation(animGroup, finishCallback, mTransitionAnimationScaleSetting,
+                mMainExecutor);
         for (int i = animGroup.size() - 1; i >= 0; i--) {
             final Animator animator = animGroup.get(i);
             animGroupStore.add(animator);
@@ -633,11 +627,12 @@
         return a;
     }
 
-    static void startSurfaceAnimation(@NonNull ArrayList<Animator> animations,
+    /** Builds an animator for the surface and adds it to the `animations` list. */
+    static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations,
             @NonNull Animation anim, @NonNull SurfaceControl leash,
             @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
-            @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor,
-            @Nullable Point position, float cornerRadius, @Nullable Rect clipRect) {
+            @NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
+            @Nullable Rect clipRect) {
         final SurfaceControl.Transaction transaction = pool.acquire();
         final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
         final Transformation transformation = new Transformation();
@@ -691,7 +686,6 @@
             }
         });
         animations.add(va);
-        animExecutor.execute(va::start);
     }
 
     private void attachThumbnail(@NonNull ArrayList<Animator> animations,
@@ -745,9 +739,8 @@
         };
         a.restrictDuration(MAX_ANIMATION_DURATION);
         a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
-        startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
-                mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
-                cornerRadius, change.getEndAbsBounds());
+        buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+                mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
     }
 
     private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@@ -770,9 +763,8 @@
         };
         a.restrictDuration(MAX_ANIMATION_DURATION);
         a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
-        startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
-                mMainExecutor, mAnimExecutor, change.getEndRelOffset(),
-                cornerRadius, change.getEndAbsBounds());
+        buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+                mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
     }
 
     private static int getWallpaperTransitType(TransitionInfo info) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 665267f..e643170 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -21,7 +21,7 @@
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
 import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
 
-import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation;
+import static com.android.wm.shell.transition.DefaultTransitionHandler.buildSurfaceAnimation;
 import static com.android.wm.shell.transition.Transitions.TAG;
 
 import android.animation.Animator;
@@ -244,11 +244,11 @@
     }
 
     /**
-     * Returns true if animating.
+     * Returns true if any animations were added to `animations`.
      */
-    public boolean startAnimation(@NonNull ArrayList<Animator> animations,
+    boolean buildAnimation(@NonNull ArrayList<Animator> animations,
             @NonNull Runnable finishCallback, float animationScale,
-            @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+            @NonNull ShellExecutor mainExecutor) {
         if (mScreenshotLayer == null) {
             // Can't do animation.
             return false;
@@ -311,13 +311,11 @@
             mRotateAlphaAnimation.restrictDuration(MAX_ANIMATION_DURATION);
             mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
 
-            startScreenshotAlphaAnimation(animations, finishCallback, mainExecutor,
-                    animExecutor);
-            startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+            buildScreenshotAlphaAnimation(animations, finishCallback, mainExecutor);
+            startDisplayRotation(animations, finishCallback, mainExecutor);
         } else {
-            startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
-            startScreenshotRotationAnimation(animations, finishCallback, mainExecutor,
-                    animExecutor);
+            startDisplayRotation(animations, finishCallback, mainExecutor);
+            startScreenshotRotationAnimation(animations, finishCallback, mainExecutor);
             //startColorAnimation(mTransaction, animationScale);
         }
 
@@ -325,27 +323,24 @@
     }
 
     private void startDisplayRotation(@NonNull ArrayList<Animator> animations,
-            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
-            @NonNull ShellExecutor animExecutor) {
-        startSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
-                mTransactionPool, mainExecutor, animExecutor, null /* position */,
-                0 /* cornerRadius */, null /* clipRect */);
+            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
+        buildSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
+                mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
+                null /* clipRect */);
     }
 
     private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
-            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
-            @NonNull ShellExecutor animExecutor) {
-        startSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
-                mTransactionPool, mainExecutor, animExecutor, null /* position */,
-                0 /* cornerRadius */, null /* clipRect */);
+            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
+        buildSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
+                mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
+                null /* clipRect */);
     }
 
-    private void startScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
-            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
-            @NonNull ShellExecutor animExecutor) {
-        startSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
-                mTransactionPool, mainExecutor, animExecutor, null /* position */,
-                0 /* cornerRadius */, null /* clipRect */);
+    private void buildScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
+            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
+        buildSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
+                mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
+                null /* clipRect */);
     }
 
     private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index ff1d2990..d5bb901 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -28,9 +28,11 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -82,13 +84,14 @@
     @Mock
     SyncTransactionQueue mSyncQueue;
     @Mock
-    TaskViewTransitions mTaskViewTransitions;
+    Transitions mTransitions;
 
     SurfaceSession mSession;
     SurfaceControl mLeash;
 
     Context mContext;
     TaskView mTaskView;
+    TaskViewTransitions mTaskViewTransitions;
 
     @Before
     public void setUp() {
@@ -118,6 +121,10 @@
             return null;
         }).when(mSyncQueue).runInSync(any());
 
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            doReturn(true).when(mTransitions).isRegistered();
+        }
+        mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
         mTaskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
         mTaskView.setListener(mExecutor, mViewListener);
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 65e1ea8..0bb809d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -29,11 +29,13 @@
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -45,6 +47,8 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.window.WindowContainerToken;
@@ -57,6 +61,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
@@ -65,6 +70,8 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitLayout;
 import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
@@ -98,11 +105,7 @@
     @Mock
     private DisplayInsetsController mDisplayInsetsController;
     @Mock
-    private Transitions mTransitions;
-    @Mock
     private TransactionPool mTransactionPool;
-    @Mock
-    private ShellExecutor mMainExecutor;
 
     private final Rect mBounds1 = new Rect(10, 20, 30, 40);
     private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -112,11 +115,16 @@
     private SurfaceControl mRootLeash;
     private ActivityManager.RunningTaskInfo mRootTask;
     private StageCoordinator mStageCoordinator;
+    private Transitions mTransitions;
+    private final TestShellExecutor mMainExecutor = new TestShellExecutor();
+    private final ShellExecutor mAnimExecutor = new TestShellExecutor();
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
 
     @Before
     @UiThreadTest
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        mTransitions = createTestTransitions();
         mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                 mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
@@ -329,7 +337,20 @@
 
         mStageCoordinator.onFoldedStateChanged(true);
 
-        verify(mStageCoordinator).onSplitScreenExit();
-        verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            verify(mTaskOrganizer).startNewTransition(eq(TRANSIT_SPLIT_DISMISS), notNull());
+        } else {
+            verify(mStageCoordinator).onSplitScreenExit();
+            verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
+        }
+    }
+
+    private Transitions createTestTransitions() {
+        ShellInit shellInit = new ShellInit(mMainExecutor);
+        final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
+                mTaskOrganizer, mTransactionPool, mock(DisplayController.class), mMainExecutor,
+                mMainHandler, mAnimExecutor);
+        shellInit.init();
+        return t;
     }
 }
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index cb385d4..c5580b3 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -21,6 +21,7 @@
 #include <hwui/Paint.h>
 
 #include <experimental/type_traits>
+#include <log/log.h>
 #include <utility>
 
 #include "SkAndroidFrameworkUtils.h"
@@ -65,16 +66,24 @@
 
 template <typename S, typename... Rest>
 static void copy_v(void* dst, const S* src, int n, Rest&&... rest) {
-    SkASSERTF(((uintptr_t)dst & (alignof(S) - 1)) == 0,
-              "Expected %p to be aligned for at least %zu bytes.", dst, alignof(S));
-    sk_careful_memcpy(dst, src, n * sizeof(S));
-    copy_v(SkTAddOffset<void>(dst, n * sizeof(S)), std::forward<Rest>(rest)...);
+    LOG_FATAL_IF(((uintptr_t)dst & (alignof(S) - 1)) != 0,
+                 "Expected %p to be aligned for at least %zu bytes.",
+                 dst, alignof(S));
+    // If n is 0, there is nothing to copy into dst from src.
+    if (n > 0) {
+        memcpy(dst, src, n * sizeof(S));
+        dst = reinterpret_cast<void*>(
+                reinterpret_cast<uint8_t*>(dst) + n * sizeof(S));
+    }
+    // Repeat for the next items, if any
+    copy_v(dst, std::forward<Rest>(rest)...);
 }
 
 // Helper for getting back at arrays which have been copy_v'd together after an Op.
 template <typename D, typename T>
 static const D* pod(const T* op, size_t offset = 0) {
-    return SkTAddOffset<const D>(op + 1, offset);
+    return reinterpret_cast<const D*>(
+                reinterpret_cast<const uint8_t*>(op + 1) + offset);
 }
 
 namespace {
@@ -612,7 +621,7 @@
 template <typename T, typename... Args>
 void* DisplayListData::push(size_t pod, Args&&... args) {
     size_t skip = SkAlignPtr(sizeof(T) + pod);
-    SkASSERT(skip < (1 << 24));
+    LOG_FATAL_IF(skip >= (1 << 24));
     if (fUsed + skip > fReserved) {
         static_assert(is_power_of_two(SKLITEDL_PAGE),
                       "This math needs updating for non-pow2.");
@@ -621,7 +630,7 @@
         fBytes.realloc(fReserved);
         LOG_ALWAYS_FATAL_IF(fBytes.get() == nullptr, "realloc(%zd) failed", fReserved);
     }
-    SkASSERT(fUsed + skip <= fReserved);
+    LOG_FATAL_IF((fUsed + skip) > fReserved);
     auto op = (T*)(fBytes.get() + fUsed);
     fUsed += skip;
     new (op) T{std::forward<Args>(args)...};
@@ -751,7 +760,7 @@
     int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0;
     size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::RectType) +
                    fs * sizeof(SkColor);
-    SkASSERT(lattice.fBounds);
+    LOG_FATAL_IF(!lattice.fBounds);
     void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds,
                                              dst, filter, paint, palette);
     copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes,
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index af8bd26..1e0c359 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -19,6 +19,8 @@
 #include <SkAndroidFrameworkUtils.h>
 #include <SkAnimatedImage.h>
 #include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
 #include <SkCanvasPriv.h>
 #include <SkCanvasStateUtils.h>
 #include <SkColorFilter.h>
@@ -35,7 +37,6 @@
 #include <SkRect.h>
 #include <SkRefCnt.h>
 #include <SkShader.h>
-#include <SkTemplates.h>
 #include <SkTextBlob.h>
 #include <SkVertices.h>
 
@@ -45,13 +46,14 @@
 
 #include "CanvasProperty.h"
 #include "NinePatchUtils.h"
-#include "SkBlendMode.h"
 #include "VectorDrawable.h"
 #include "hwui/Bitmap.h"
 #include "hwui/MinikinUtils.h"
 #include "hwui/PaintFilter.h"
+#include <log/log.h>
 #include "pipeline/skia/AnimatedDrawables.h"
 #include "pipeline/skia/HolePunch.h"
+#include <ui/FatVector.h>
 
 namespace android {
 
@@ -248,7 +250,7 @@
                                  ? static_cast<const SaveRec*>(&mSaveStack->back())
                                  : nullptr;
     int currentSaveCount = mCanvas->getSaveCount();
-    SkASSERT(!rec || currentSaveCount >= rec->saveCount);
+    LOG_FATAL_IF(!(!rec || currentSaveCount >= rec->saveCount));
 
     return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
 }
@@ -298,7 +300,7 @@
 
 // Applies and optionally removes all clips >= index.
 void SkiaCanvas::applyPersistentClips(size_t clipStartIndex) {
-    SkASSERT(clipStartIndex <= mClipStack.size());
+    LOG_FATAL_IF(clipStartIndex > mClipStack.size());
     const auto begin = mClipStack.cbegin() + clipStartIndex;
     const auto end = mClipStack.cend();
 
@@ -646,7 +648,7 @@
             texsPtr += 1;
             y += dy;
         }
-        SkASSERT(texsPtr - texs == ptCount);
+        LOG_FATAL_IF((texsPtr - texs) != ptCount);
     }
 
     // cons up indices
@@ -669,14 +671,14 @@
             // bump to the next row
             index += 1;
         }
-        SkASSERT(indexPtr - indices == indexCount);
+        LOG_FATAL_IF((indexPtr - indices) != indexCount);
     }
 
 // double-check that we have legal indices
-#ifdef SK_DEBUG
+#if !defined(NDEBUG)
     {
         for (int i = 0; i < indexCount; i++) {
-            SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
+            LOG_FATAL_IF((unsigned)indices[i] >= (unsigned)ptCount);
         }
     }
 #endif
@@ -718,10 +720,12 @@
         numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
     }
 
-    SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags);
-    SkAutoSTMalloc<25, SkColor> colors(numFlags);
+    // Most times, we do not have very many flags/colors, so the stack allocated part of
+    // FatVector will save us a heap allocation.
+    FatVector<SkCanvas::Lattice::RectType, 25> flags(numFlags);
+    FatVector<SkColor, 25> colors(numFlags);
     if (numFlags > 0) {
-        NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
+        NinePatchUtils::SetLatticeFlags(&lattice, flags.data(), numFlags, chunk, colors.data());
     }
 
     lattice.fBounds = nullptr;
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index ae2e974..f5cd793 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -8,7 +8,6 @@
 #include <nativehelper/JNIHelp.h>
 #include "GraphicsJNI.h"
 
-#include "include/private/SkTemplates.h" // SkTAddOffset
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColorSpace.h"
@@ -21,6 +20,7 @@
 #include "SkTypes.h"
 #include <cutils/ashmem.h>
 #include <hwui/Canvas.h>
+#include <log/log.h>
 
 using namespace android;
 
@@ -490,7 +490,7 @@
 
 void GraphicsJNI::set_metrics(JNIEnv* env, jobject metrics, const SkFontMetrics& skmetrics) {
     if (metrics == nullptr) return;
-    SkASSERT(env->IsInstanceOf(metrics, gFontMetrics_class));
+    LOG_FATAL_IF(!env->IsInstanceOf(metrics, gFontMetrics_class));
     env->SetFloatField(metrics, gFontMetrics_top, SkScalarToFloat(skmetrics.fTop));
     env->SetFloatField(metrics, gFontMetrics_ascent, SkScalarToFloat(skmetrics.fAscent));
     env->SetFloatField(metrics, gFontMetrics_descent, SkScalarToFloat(skmetrics.fDescent));
@@ -504,7 +504,7 @@
     int leading = SkScalarRoundToInt(skmetrics.fLeading);
 
     if (metrics) {
-        SkASSERT(env->IsInstanceOf(metrics, gFontMetricsInt_class));
+        LOG_FATAL_IF(!env->IsInstanceOf(metrics, gFontMetricsInt_class));
         env->SetIntField(metrics, gFontMetricsInt_top, SkScalarFloorToInt(skmetrics.fTop));
         env->SetIntField(metrics, gFontMetricsInt_ascent, ascent);
         env->SetIntField(metrics, gFontMetricsInt_descent, descent);
@@ -713,7 +713,9 @@
                 mSkiaBitmap->info().height());
         for (int y = 0; y < rowsToCopy; y++) {
             memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy);
-            dst = SkTAddOffset<void>(dst, dstRowBytes);
+            // Cast to bytes in order to apply the dstRowBytes offset correctly.
+            dst = reinterpret_cast<void*>(
+                    reinterpret_cast<uint8_t*>(dst) + dstRowBytes);
         }
         recycledPixels->notifyPixelsChanged();
         recycledPixels->unref();
diff --git a/libs/hwui/jni/Mesh.h b/libs/hwui/jni/Mesh.h
index 7a73f2d..61c2260 100644
--- a/libs/hwui/jni/Mesh.h
+++ b/libs/hwui/jni/Mesh.h
@@ -20,6 +20,7 @@
 #include <SkMesh.h>
 #include <jni.h>
 
+#include <log/log.h>
 #include <utility>
 
 #include "graphics_jni_helpers.h"
@@ -186,23 +187,24 @@
         std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=(
                 const T& val) {
             if (!fVar) {
-                SkDEBUGFAIL("Assigning to missing variable");
+                LOG_FATAL("Assigning to missing variable");
             } else if (sizeof(val) != fVar->sizeInBytes()) {
-                SkDEBUGFAIL("Incorrect value size");
+                LOG_FATAL("Incorrect value size");
             } else {
-                memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), &val,
-                       szeof(val));
+                void* dst = reinterpret_cast<void*>(
+                    reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
+                memcpy(dst, &val, sizeof(val));
             }
         }
 
         MeshUniform& operator=(const SkMatrix& val) {
             if (!fVar) {
-                SkDEBUGFAIL("Assigning to missing variable");
+                LOG_FATAL("Assigning to missing variable");
             } else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
-                SkDEBUGFAIL("Incorrect value size");
+                LOG_FATAL("Incorrect value size");
             } else {
-                float* data =
-                        SkTAddOffset<float>(fOwner->writableUniformData(), (ptrdiff_t)fVar->offset);
+                float* data = reinterpret_cast<float*>(
+                    reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
                 data[0] = val.get(0);
                 data[1] = val.get(3);
                 data[2] = val.get(6);
@@ -220,14 +222,15 @@
         bool set(const T val[], const int count) {
             static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
             if (!fVar) {
-                SkDEBUGFAIL("Assigning to missing variable");
+                LOG_FATAL("Assigning to missing variable");
                 return false;
             } else if (sizeof(T) * count != fVar->sizeInBytes()) {
-                SkDEBUGFAIL("Incorrect value size");
+                LOG_FATAL("Incorrect value size");
                 return false;
             } else {
-                memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), val,
-                       sizeof(T) * count);
+                void* dst = reinterpret_cast<void*>(
+                    reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
+                memcpy(dst, val, sizeof(T) * count);
             }
             return true;
         }
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index db449d6..08f0291 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -16,7 +16,6 @@
 
 #include "SkiaRecordingCanvas.h"
 #include "hwui/Paint.h"
-#include <include/private/SkTemplates.h> // SkAutoSTMalloc
 #include <SkBlendMode.h>
 #include <SkData.h>
 #include <SkDrawable.h>
@@ -43,6 +42,8 @@
 #include "pipeline/skia/VkFunctorDrawable.h"
 #include "pipeline/skia/VkInteropFunctorDrawable.h"
 #endif
+#include <log/log.h>
+#include <ui/FatVector.h>
 
 namespace android {
 namespace uirenderer {
@@ -55,7 +56,7 @@
 void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
                                           int height) {
     mCurrentBarrier = nullptr;
-    SkASSERT(mDisplayList.get() == nullptr);
+    LOG_FATAL_IF(mDisplayList.get() != nullptr);
 
     if (renderNode) {
         mDisplayList = renderNode->detachAvailableList();
@@ -285,10 +286,12 @@
         numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
     }
 
-    SkAutoSTMalloc<25, SkCanvas::Lattice::RectType> flags(numFlags);
-    SkAutoSTMalloc<25, SkColor> colors(numFlags);
+    // Most times, we do not have very many flags/colors, so the stack allocated part of
+    // FatVector will save us a heap allocation.
+    FatVector<SkCanvas::Lattice::RectType, 25> flags(numFlags);
+    FatVector<SkColor, 25> colors(numFlags);
     if (numFlags > 0) {
-        NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
+        NinePatchUtils::SetLatticeFlags(&lattice, flags.data(), numFlags, chunk, colors.data());
     }
 
     lattice.fBounds = nullptr;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3fbada7..813929e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6442,6 +6442,20 @@
     }
 
     /**
+     * Returns the registered volume controller interface.
+     *
+     * @hide
+     */
+    @Nullable
+    public IVolumeController getVolumeController() {
+        try {
+            return getService().getVolumeController();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Notify audio manager about volume controller visibility changes.
      * Currently limited to SystemUI.
      *
@@ -6505,6 +6519,106 @@
 
     /**
      * @hide
+     * @return the RS2 value used for momentary exposure warnings
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public float getRs2Value() {
+        try {
+            return getService().getRs2Value();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Sets the RS2 value used for momentary exposure warnings
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public void setRs2Value(float rs2Value) {
+        try {
+            getService().setRs2Value(rs2Value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * @return the current computed sound dose value
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public float getCsd() {
+        try {
+            return getService().getCsd();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Sets the computed sound dose value to {@code csd}
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public void setCsd(float csd) {
+        try {
+            getService().setCsd(csd);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Forces the computation of MEL values (used for CSD) on framework level. This will have the
+     * result of ignoring the MEL values computed on HAL level. Should only be used in testing
+     * since this can affect the certification of a device with EN50332-3 regulation.
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public void forceUseFrameworkMel(boolean useFrameworkMel) {
+        try {
+            getService().forceUseFrameworkMel(useFrameworkMel);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Forces the computation of CSD on all output devices.
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
+        try {
+            getService().forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Returns whether CSD is enabled on this device.
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public boolean isCsdEnabled() {
+        try {
+            return getService().isCsdEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
      * Sound dose warning at every 100% of dose during integration window
      */
     public static final int CSD_WARNING_DOSE_REACHED_1X = 1;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c06352c..5ba7891 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -260,6 +260,8 @@
 
     void setVolumeController(in IVolumeController controller);
 
+    @nullable IVolumeController getVolumeController();
+
     void notifyVolumeControllerVisible(in IVolumeController controller, boolean visible);
 
     boolean isStreamAffectedByRingerMode(int streamType);
@@ -270,6 +272,27 @@
 
     void lowerVolumeToRs1(String callingPackage);
 
+    @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+    float getRs2Value();
+
+    @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+    oneway void setRs2Value(float rs2Value);
+
+    @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+    float getCsd();
+
+    @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+    oneway void setCsd(float csd);
+
+    @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+    oneway void forceUseFrameworkMel(boolean useFrameworkMel);
+
+    @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+    oneway void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices);
+
+    @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS")
+    boolean isCsdEnabled();
+
     int setHdmiSystemAudioSupported(boolean on);
 
     boolean isHdmiSystemAudioSupported();
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 03d7de5..1e1c833 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -477,7 +477,8 @@
                 Icons.Outlined.NewReleases,
                 contentDescription = null,
                 modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
-                    .padding(all = 24.dp)
+                    .padding(all = 24.dp),
+                tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
             )
             TextOnSurface(
                 text = stringResource(
@@ -642,7 +643,7 @@
             Icon(
                 painter = painterResource(R.drawable.ic_other_devices),
                 contentDescription = null,
-                tint = Color.Unspecified,
+                tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
                 modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
                     .padding(all = 24.dp).size(32.dp)
             )
@@ -972,7 +973,8 @@
                 Icon(
                     Icons.Filled.Add,
                     contentDescription = null,
-                    modifier = Modifier.padding(start = 16.dp)
+                    modifier = Modifier.padding(start = 16.dp),
+                    tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
                 )
             },
             label = {
@@ -1006,8 +1008,8 @@
             Icon(
                 painter = painterResource(R.drawable.ic_other_devices),
                 contentDescription = null,
-                tint = Color.Unspecified,
-                modifier = Modifier.padding(start = 18.dp)
+                tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
+                modifier = Modifier.padding(start = 10.dp)
             )
         },
         label = {
@@ -1015,7 +1017,7 @@
                 TextOnSurfaceVariant(
                     text = stringResource(R.string.another_device),
                     style = MaterialTheme.typography.titleLarge,
-                    modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp)
+                    modifier = Modifier.padding(start = 10.dp, top = 18.dp, bottom = 18.dp)
                         .align(alignment = Alignment.CenterHorizontally),
                 )
             }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 1cc1ac5..420b4d5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -278,7 +278,7 @@
     ContainerCard() {
         Column() {
             TopAppBar(
-                colors = TopAppBarDefaults.smallTopAppBarColors(
+                colors = TopAppBarDefaults.topAppBarColors(
                     containerColor = Color.Transparent,
                 ),
                 title = {
@@ -415,8 +415,8 @@
                     Icon(
                         painter = painterResource(R.drawable.ic_other_devices),
                         contentDescription = null,
-                        tint = Color.Unspecified,
-                        modifier = Modifier.padding(start = 16.dp)
+                        tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
+                        modifier = Modifier.padding(start = 10.dp)
                     )
                 },
                 label = {
@@ -424,7 +424,7 @@
                         text = stringResource(
                             R.string.get_dialog_option_headline_use_a_different_device),
                         style = MaterialTheme.typography.titleLarge,
-                        modifier = Modifier.padding(start = 16.dp, top = 18.dp, bottom = 18.dp)
+                        modifier = Modifier.padding(start = 10.dp, top = 18.dp, bottom = 18.dp)
                             .align(alignment = Alignment.CenterHorizontally)
                     )
                 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
index eb2bffe..1aa2079 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
@@ -25,7 +25,7 @@
 import androidx.navigation.NavHostController
 
 interface NavControllerWrapper {
-    fun navigate(route: String)
+    fun navigate(route: String, popUpCurrent: Boolean = false)
     fun navigateBack()
 
     val highlightEntryId: String?
@@ -48,17 +48,17 @@
 
 val LocalNavController = compositionLocalOf<NavControllerWrapper> {
     object : NavControllerWrapper {
-        override fun navigate(route: String) {}
+        override fun navigate(route: String, popUpCurrent: Boolean) {}
 
         override fun navigateBack() {}
     }
 }
 
 @Composable
-fun navigator(route: String?): () -> Unit {
+fun navigator(route: String?, popUpCurrent: Boolean = false): () -> Unit {
     if (route == null) return {}
     val navController = LocalNavController.current
-    return { navController.navigate(route) }
+    return { navController.navigate(route, popUpCurrent) }
 }
 
 internal class NavControllerWrapperImpl(
@@ -68,8 +68,16 @@
     var highlightId: String? = null
     var sessionName: String? = null
 
-    override fun navigate(route: String) {
-        navController.navigate(route)
+    override fun navigate(route: String, popUpCurrent: Boolean) {
+        navController.navigate(route) {
+            if (popUpCurrent) {
+                navController.currentDestination?.let { currentDestination ->
+                    popUpTo(currentDestination.id) {
+                        inclusive = true
+                    }
+                }
+            }
+        }
     }
 
     override fun navigateBack() {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/NavControllerWrapperTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/NavControllerWrapperTest.kt
new file mode 100644
index 0000000..118585e
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/NavControllerWrapperTest.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.settingslib.spa.framework.compose
+
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.waitUntilExists
+import kotlinx.coroutines.delay
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class NavControllerWrapperTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun navigate_canNavigate() {
+        composeTestRule.setContent {
+            TestNavHost {
+                LocalNavController.current.navigate(ROUTE_B)
+            }
+        }
+
+        composeTestRule.onNodeWithText(ROUTE_B).assertIsDisplayed()
+    }
+
+    @Test
+    fun navigate_canNavigateBack() {
+        composeTestRule.setContent {
+            TestNavHost {
+                val navController = LocalNavController.current
+                LaunchedEffect(Unit) {
+                    navController.navigate(ROUTE_B)
+                    delay(100)
+                    navController.navigateBack()
+                }
+            }
+        }
+
+        composeTestRule.waitUntilExists(hasText(ROUTE_A))
+    }
+
+    @Test
+    fun navigate_canNavigateAndPopUpCurrent() {
+        composeTestRule.setContent {
+            TestNavHost {
+                val navController = LocalNavController.current
+                LaunchedEffect(Unit) {
+                    navController.navigate(ROUTE_B)
+                    delay(100)
+                    navController.navigate(ROUTE_C, popUpCurrent = true)
+                    delay(100)
+                    navController.navigateBack()
+                }
+            }
+        }
+
+        composeTestRule.waitUntilExists(hasText(ROUTE_A))
+    }
+
+    private companion object {
+        @Composable
+        fun TestNavHost(content: @Composable () -> Unit) {
+            val navController = rememberNavController()
+            CompositionLocalProvider(navController.localNavController()) {
+                NavHost(navController, ROUTE_A) {
+                    composable(route = ROUTE_A) { Text(ROUTE_A) }
+                    composable(route = ROUTE_B) { Text(ROUTE_B) }
+                    composable(route = ROUTE_C) { Text(ROUTE_C) }
+                }
+                content()
+            }
+        }
+
+        const val ROUTE_A = "RouteA"
+        const val ROUTE_B = "RouteB"
+        const val ROUTE_C = "RouteC"
+    }
+}
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt
index 5a3044d..f289d0d 100644
--- a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FakeNavControllerWrapper.kt
@@ -25,7 +25,7 @@
     var navigateCalledWith: String? = null
     var navigateBackIsCalled = false
 
-    override fun navigate(route: String) {
+    override fun navigate(route: String, popUpCurrent: Boolean) {
         navigateCalledWith = route
     }
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c641a85..b4598bf 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -646,6 +646,9 @@
     <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
 
+    <!-- Permission required for CTS test - SoundDoseHelperTest -->
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SYSTEM_SETTINGS" />
+
     <!-- Permission required for CTS test - CtsAmbientContextDetectionServiceDeviceTest -->
     <uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 26a68bc..628fb38 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -937,7 +937,7 @@
             android:excludeFromRecents="true"
             android:theme="@android:style/Theme.NoDisplay"
             android:label="@string/note_task_button_label"
-            android:icon="@drawable/ic_note_task_button">
+            android:icon="@drawable/ic_note_task_shortcut_widget">
 
             <intent-filter>
                 <action android:name="android.intent.action.CREATE_SHORTCUT" />
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index f92625b..333aeba 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -16,6 +16,9 @@
         },
         {
             "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+        },
+        {
+            "exclude-annotation": "android.platform.test.annotations.FlakyTest"
         }
       ]
     }
@@ -73,16 +76,16 @@
   // Curious where your @Scenario tests will run?
   //
   // @Ignore: nowhere
-  // @Staging or @FlakyTest: in staged-postsubmit, but not postsubmit or
-  // 	presubmit
+  // @FlakyTest: in staged-postsubmit, but not blocking postsubmit or
+  // presubmit
   // @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit
   // none of the above: in presubmit, postsubmit, and staged-postsubmit
   //
-  // Therefore, please annotate new tests with @Staging, then with @Postsubmit
-  // once they're ready for postsubmit, then with neither once they're ready
-  // for presubmit.
+  // Ideally, please annotate new tests with @FlakyTest, then with @Postsubmit
+  // once they're ready for postsubmit as they will immediately block go/android-platinum,
+  // then with neither once they're ready for presubmit.
   //
-  // If you don't use @Staging or @Postsubmit, your new test will immediately
+  // If you don't use @Postsubmit, your new test will immediately
   // block presubmit, which is probably not what you want!
   "sysui-platinum-postsubmit": [
     {
@@ -99,6 +102,9 @@
         },
         {
             "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+            "exclude-annotation": "android.platform.test.annotations.FlakyTest"
         }
       ]
     }
@@ -131,6 +137,9 @@
         },
         {
             "include-filter": "android.platform.test.scenario.sysui"
+        },
+        {
+            "exclude-annotation": "android.platform.test.annotations.FlakyTest"
         }
       ]
     }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index dfac02d..26aa0e8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -26,6 +26,7 @@
 import android.graphics.drawable.GradientDrawable
 import android.graphics.drawable.InsetDrawable
 import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.StateListDrawable
 import android.util.Log
 import android.view.GhostView
 import android.view.View
@@ -35,6 +36,7 @@
 import com.android.internal.jank.InteractionJankMonitor
 import java.util.LinkedList
 import kotlin.math.min
+import kotlin.math.roundToInt
 
 private const val TAG = "GhostedViewLaunchAnimatorController"
 
@@ -49,7 +51,9 @@
  * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
  * whenever possible instead.
  */
-open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
+open class GhostedViewLaunchAnimatorController
+@JvmOverloads
+constructor(
     /** The view that will be ghosted and from which the background will be extracted. */
     private val ghostedView: View,
 
@@ -145,7 +149,8 @@
         val gradient = findGradientDrawable(drawable) ?: return 0f
 
         // TODO(b/184121838): Support more than symmetric top & bottom radius.
-        return gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
+        val radius = gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
+        return radius * ghostedView.scaleX
     }
 
     /** Return the current bottom corner radius of the background. */
@@ -154,7 +159,8 @@
         val gradient = findGradientDrawable(drawable) ?: return 0f
 
         // TODO(b/184121838): Support more than symmetric top & bottom radius.
-        return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
+        val radius = gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
+        return radius * ghostedView.scaleX
     }
 
     override fun createAnimatorState(): LaunchAnimator.State {
@@ -173,9 +179,13 @@
         ghostedView.getLocationOnScreen(ghostedViewLocation)
         val insets = backgroundInsets
         state.top = ghostedViewLocation[1] + insets.top
-        state.bottom = ghostedViewLocation[1] + ghostedView.height - insets.bottom
+        state.bottom =
+            ghostedViewLocation[1] + (ghostedView.height * ghostedView.scaleY).roundToInt() -
+                insets.bottom
         state.left = ghostedViewLocation[0] + insets.left
-        state.right = ghostedViewLocation[0] + ghostedView.width - insets.right
+        state.right =
+            ghostedViewLocation[0] + (ghostedView.width * ghostedView.scaleX).roundToInt() -
+                insets.right
     }
 
     override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
@@ -341,6 +351,10 @@
                 }
             }
 
+            if (drawable is StateListDrawable) {
+                return findGradientDrawable(drawable.current)
+            }
+
             return null
         }
     }
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 2148cb0..d6b48f9 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -1,6 +1,5 @@
 +packages/SystemUI
 -packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
 -packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
 -packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
 -packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
index d3fabac..faf1b78 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt
@@ -21,6 +21,7 @@
 import android.net.Uri
 import android.os.Handler
 import android.os.Looper
+import android.os.Trace
 import android.provider.Settings
 
 /**
@@ -51,14 +52,21 @@
         }
     }
 
+    private fun clearCache() {
+        Trace.beginSection("LogcatEchoTrackerDebug#clearCache")
+        cachedBufferLevels.clear()
+        Trace.endSection()
+    }
+
     private fun attach(mainLooper: Looper) {
+        Trace.beginSection("LogcatEchoTrackerDebug#attach")
         contentResolver.registerContentObserver(
             Settings.Global.getUriFor(BUFFER_PATH),
             true,
             object : ContentObserver(Handler(mainLooper)) {
                 override fun onChange(selfChange: Boolean, uri: Uri?) {
                     super.onChange(selfChange, uri)
-                    cachedBufferLevels.clear()
+                    clearCache()
                 }
             }
         )
@@ -69,10 +77,11 @@
             object : ContentObserver(Handler(mainLooper)) {
                 override fun onChange(selfChange: Boolean, uri: Uri?) {
                     super.onChange(selfChange, uri)
-                    cachedTagLevels.clear()
+                    clearCache()
                 }
             }
         )
+        Trace.endSection()
     }
 
     /** Whether [bufferName] should echo messages of [level] or higher to logcat. */
@@ -97,9 +106,12 @@
 
     private fun readSetting(path: String): LogLevel {
         return try {
+            Trace.beginSection("LogcatEchoTrackerDebug#readSetting")
             parseProp(Settings.Global.getString(contentResolver, path))
         } catch (_: Settings.SettingNotFoundException) {
             DEFAULT_LEVEL
+        } finally {
+            Trace.endSection()
         }
     }
 
diff --git a/packages/SystemUI/res/drawable/ic_note_task_button.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
similarity index 92%
copy from packages/SystemUI/res/drawable/ic_note_task_button.xml
copy to packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
index bb5e224..ee8d4883 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_button.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
@@ -19,10 +19,13 @@
     android:viewportHeight="24"
     android:viewportWidth="24">
     <path
-        android:fillColor="#636C6F"
+        android:fillAlpha="1"
+        android:fillColor="@android:color/white"
+        android:fillType="nonZero"
         android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
     <path
-        android:fillColor="#636C6F"
-        android:fillType="evenOdd"
+        android:fillAlpha="1"
+        android:fillColor="@android:color/white"
+        android:fillType="nonZero"
         android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_button.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
similarity index 95%
rename from packages/SystemUI/res/drawable/ic_note_task_button.xml
rename to packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
index bb5e224..7590182 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_button.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
@@ -19,10 +19,13 @@
     android:viewportHeight="24"
     android:viewportWidth="24">
     <path
+        android:fillAlpha="1"
         android:fillColor="#636C6F"
+        android:fillType="nonZero"
         android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
     <path
+        android:fillAlpha="1"
         android:fillColor="#636C6F"
-        android:fillType="evenOdd"
+        android:fillType="nonZero"
         android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index 18fcebb..dfb73a9 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -27,22 +27,26 @@
           android:top="3dp"
           android:right="3dp"
           android:bottom="3dp">
-        <shape android:shape="oval">
+        <!-- We make the shapes a rounded rectangle instead of an oval so that it can animate -->
+        <!-- properly into an app/dialog. -->
+        <shape android:shape="rectangle">
           <solid android:color="?androidprv:attr/colorSurface"/>
           <size
-              android:width="@dimen/keyguard_affordance_width"
-              android:height="@dimen/keyguard_affordance_height"/>
+              android:width="@dimen/keyguard_affordance_fixed_width"
+              android:height="@dimen/keyguard_affordance_fixed_height"/>
+          <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
         </shape>
       </item>
 
       <item>
-        <shape android:shape="oval">
+        <shape android:shape="rectangle">
           <stroke
               android:color="@color/control_primary_text"
               android:width="2dp"/>
           <size
-              android:width="@dimen/keyguard_affordance_width"
-              android:height="@dimen/keyguard_affordance_height"/>
+              android:width="@dimen/keyguard_affordance_fixed_width"
+              android:height="@dimen/keyguard_affordance_fixed_height"/>
+          <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
         </shape>
       </item>
     </layer-list>
@@ -55,11 +59,12 @@
           android:top="3dp"
           android:right="3dp"
           android:bottom="3dp">
-        <shape android:shape="oval">
+        <shape android:shape="rectangle">
           <solid android:color="?androidprv:attr/colorSurface"/>
           <size
-              android:width="@dimen/keyguard_affordance_width"
-              android:height="@dimen/keyguard_affordance_height"/>
+              android:width="@dimen/keyguard_affordance_fixed_width"
+              android:height="@dimen/keyguard_affordance_fixed_height"/>
+          <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
         </shape>
       </item>
     </layer-list>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index f4434e8..ea3c012 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -36,4 +36,15 @@
     <integer name="qs_security_footer_maxLines">1</integer>
 
     <bool name="config_use_large_screen_shade_header">true</bool>
+
+    <!-- A collection of defaults for the quick affordances on the lock screen. Each item must be a
+    string with two parts: the ID of the slot and the comma-delimited list of affordance IDs,
+    separated by a colon ':' character. For example: <item>bottom_end:home,wallet</item>. The
+    default is displayed by System UI as long as the user hasn't made a different choice for that
+    slot. If the user did make a choice, even if the choice is the "None" option, the default is
+    ignored. -->
+    <string-array name="config_keyguardQuickAffordanceDefaults" translatable="false">
+        <item>bottom_start:home</item>
+        <item>bottom_end:create_note</item>
+    </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 9bc0dde..db7fb48 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -30,10 +30,6 @@
     <!-- Margin on the left side of the carrier text on Keyguard -->
     <dimen name="keyguard_carrier_text_margin">24dp</dimen>
 
-    <!-- The width/height of the phone/camera/unlock icon on keyguard. -->
-    <dimen name="keyguard_affordance_height">80dp</dimen>
-    <dimen name="keyguard_affordance_width">120dp</dimen>
-
     <!-- Screen pinning request width -->
     <dimen name="screen_pinning_request_width">400dp</dimen>
     <!-- Screen pinning request bottom button circle widths -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fc67015..6202939 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -762,12 +762,10 @@
     <dimen name="go_to_full_shade_appearing_translation">200dp</dimen>
 
     <!-- The width/height of the keyguard bottom area icon view on keyguard. -->
-    <dimen name="keyguard_affordance_height">48dp</dimen>
-    <dimen name="keyguard_affordance_width">48dp</dimen>
-
     <dimen name="keyguard_affordance_fixed_height">48dp</dimen>
     <dimen name="keyguard_affordance_fixed_width">48dp</dimen>
     <dimen name="keyguard_affordance_fixed_radius">24dp</dimen>
+
     <!-- Amount the button should shake when it's not long-pressed for long enough. -->
     <dimen name="keyguard_affordance_shake_amplitude">8dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2539828..428a0a8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -534,6 +534,13 @@
     val OUTPUT_SWITCHER_SHOW_API_ENABLED =
         unreleasedFlag(2503, "output_switcher_show_api_enabled", teamfood = true)
 
+    // 2700 - unfold transitions
+    // TODO(b/265764985): Tracking Bug
+    @Keep
+    @JvmField
+    val ENABLE_DARK_VIGNETTE_WHEN_FOLDING =
+        unreleasedFlag(2700, "enable_dark_vignette_when_folding")
+
     // TODO(b259590361): Tracking bug
     val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index cd4dac0..76c2430 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -25,6 +25,7 @@
 object BuiltInKeyguardQuickAffordanceKeys {
     // Please keep alphabetical order of const names to simplify future maintenance.
     const val CAMERA = "camera"
+    const val CREATE_NOTE = "create_note"
     const val DO_NOT_DISTURB = "do_not_disturb"
     const val FLASHLIGHT = "flashlight"
     const val HOME_CONTROLS = "home"
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
index 26e3f49..4b10d69 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskIntentResolver.kt
@@ -72,6 +72,9 @@
     companion object {
         // TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
         const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
+
+        // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
+        const val NOTE_ROLE = "android.app.role.NOTES"
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 8bdf319..22ce121 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -18,11 +18,13 @@
 
 import android.app.Activity
 import android.app.KeyguardManager
+import android.app.role.RoleManager
 import android.content.Context
 import android.os.UserManager
 import androidx.core.content.getSystemService
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule
 import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
 import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
 import dagger.Binds
@@ -33,20 +35,25 @@
 import java.util.Optional
 
 /** Compose all dependencies required by Note Task feature. */
-@Module
+@Module(includes = [NoteTaskQuickAffordanceModule::class])
 internal interface NoteTaskModule {
 
     @[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
-    fun bindNoteTaskLauncherActivity(activity: LaunchNoteTaskActivity): Activity?
+    fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity
 
     @[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
-    fun bindNoteTaskShortcutActivity(activity: CreateNoteTaskShortcutActivity): Activity?
+    fun CreateNoteTaskShortcutActivity.bindNoteTaskShortcutActivity(): Activity
 
     companion object {
 
         @[Provides NoteTaskEnabledKey]
-        fun provideIsNoteTaskEnabled(featureFlags: FeatureFlags): Boolean {
-            return featureFlags.isEnabled(Flags.NOTE_TASKS)
+        fun provideIsNoteTaskEnabled(
+            featureFlags: FeatureFlags,
+            roleManager: RoleManager,
+        ): Boolean {
+            val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.NOTE_ROLE)
+            val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
+            return isRoleAvailable && isFeatureEnabled
         }
 
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
new file mode 100644
index 0000000..cfbaa48
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.notetask.quickaffordance
+
+import android.content.Context
+import com.android.systemui.R
+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.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskEnabledKey
+import javax.inject.Inject
+import kotlinx.coroutines.flow.flowOf
+
+internal class NoteTaskQuickAffordanceConfig
+@Inject
+constructor(
+    context: Context,
+    private val noteTaskController: NoteTaskController,
+    @NoteTaskEnabledKey private val isEnabled: Boolean,
+) : KeyguardQuickAffordanceConfig {
+
+    override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE
+
+    override val pickerName: String = context.getString(R.string.note_task_button_label)
+
+    override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard
+
+    override val lockScreenState = flowOf(getLockScreenState())
+
+    // TODO(b/265949213)
+    private fun getLockScreenState() =
+        if (isEnabled) {
+            val icon = Icon.Resource(pickerIconResourceId, ContentDescription.Loaded(pickerName))
+            LockScreenState.Visible(icon)
+        } else {
+            LockScreenState.Hidden
+        }
+
+    override suspend fun getPickerScreenState() =
+        if (isEnabled) {
+            PickerScreenState.Default()
+        } else {
+            PickerScreenState.UnavailableOnDevice
+        }
+
+    override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
+        noteTaskController.showNoteTask()
+        return OnTriggeredResult.Handled
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
new file mode 100644
index 0000000..7cb932a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.notetask.quickaffordance
+
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+internal interface NoteTaskQuickAffordanceModule {
+
+    @[Binds IntoSet]
+    fun NoteTaskQuickAffordanceConfig.bindNoteTaskQuickAffordance(): KeyguardQuickAffordanceConfig
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
index f6a623e..6ab0da6 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -46,7 +46,7 @@
                 id = SHORTCUT_ID,
                 shortLabel = getString(R.string.note_task_button_label),
                 intent = LaunchNoteTaskActivity.newIntent(context = this),
-                iconResource = R.drawable.ic_note_task_button,
+                iconResource = R.drawable.ic_note_task_shortcut_widget,
             )
         setResult(Activity.RESULT_OK, intent)
 
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 1558ac5..287e810 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -62,12 +62,24 @@
     fun removeCallback(callback: Callback)
 
     /**
-     * Ćallback for notifying of changes.
+     * Callback for notifying of changes.
      */
     interface Callback {
 
         /**
+         * Notifies that the current user is being changed.
+         * Override this method to run things while the screen is frozen for the user switch.
+         * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
+         * screen further. Please be aware that code executed in this callback will lengthen the
+         * user switch duration.
+         */
+        @JvmDefault
+        fun onUserChanging(newUser: Int, userContext: Context) {}
+
+        /**
          * Notifies that the current user has changed.
+         * Override this method to run things after the screen is unfrozen for the user switch.
+         * Please see {@link #onUserChanging} if you need to hide jank.
          */
         @JvmDefault
         fun onUserChanged(newUser: Int, userContext: Context) {}
@@ -78,4 +90,4 @@
         @JvmDefault
         fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 4dbe099..3a5d0a7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.settings
 
+import android.app.IActivityManager
+import android.app.UserSwitchObserver
 import android.content.BroadcastReceiver
 import android.content.ContentResolver
 import android.content.Context
@@ -23,6 +25,7 @@
 import android.content.IntentFilter
 import android.content.pm.UserInfo
 import android.os.Handler
+import android.os.IRemoteCallback
 import android.os.UserHandle
 import android.os.UserManager
 import android.util.Log
@@ -33,6 +36,7 @@
 import com.android.systemui.util.Assert
 import java.io.PrintWriter
 import java.lang.ref.WeakReference
+import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
 import kotlin.properties.ReadWriteProperty
 import kotlin.reflect.KProperty
@@ -55,6 +59,7 @@
 open class UserTrackerImpl internal constructor(
     private val context: Context,
     private val userManager: UserManager,
+    private val iActivityManager: IActivityManager,
     private val dumpManager: DumpManager,
     private val backgroundHandler: Handler
 ) : UserTracker, Dumpable, BroadcastReceiver() {
@@ -106,7 +111,6 @@
         setUserIdInternal(startingUser)
 
         val filter = IntentFilter().apply {
-            addAction(Intent.ACTION_USER_SWITCHED)
             addAction(Intent.ACTION_USER_INFO_CHANGED)
             // These get called when a managed profile goes in or out of quiet mode.
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
@@ -117,14 +121,13 @@
         }
         context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
 
+        registerUserSwitchObserver()
+
         dumpManager.registerDumpable(TAG, this)
     }
 
     override fun onReceive(context: Context, intent: Intent) {
         when (intent.action) {
-            Intent.ACTION_USER_SWITCHED -> {
-                handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
-            }
             Intent.ACTION_USER_INFO_CHANGED,
             Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
             Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
@@ -156,22 +159,43 @@
         return ctx to profiles
     }
 
+    private fun registerUserSwitchObserver() {
+        iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
+            override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
+                backgroundHandler.run {
+                    handleUserSwitching(newUserId)
+                    reply?.sendResult(null)
+                }
+            }
+
+            override fun onUserSwitchComplete(newUserId: Int) {
+                backgroundHandler.run {
+                    handleUserSwitchComplete(newUserId)
+                }
+            }
+        }, TAG)
+    }
+
     @WorkerThread
-    protected open fun handleSwitchUser(newUser: Int) {
+    protected open fun handleUserSwitching(newUserId: Int) {
         Assert.isNotMainThread()
-        if (newUser == UserHandle.USER_NULL) {
-            Log.w(TAG, "handleSwitchUser - Couldn't get new id from intent")
-            return
-        }
+        Log.i(TAG, "Switching to user $newUserId")
 
-        if (newUser == userId) return
-        Log.i(TAG, "Switching to user $newUser")
-
-        val (ctx, profiles) = setUserIdInternal(newUser)
-
+        setUserIdInternal(newUserId)
         notifySubscribers {
-            onUserChanged(newUser, ctx)
-            onProfilesChanged(profiles)
+            onUserChanging(newUserId, userContext)
+        }.await()
+    }
+
+    @WorkerThread
+    protected open fun handleUserSwitchComplete(newUserId: Int) {
+        Assert.isNotMainThread()
+        Log.i(TAG, "Switched to user $newUserId")
+
+        setUserIdInternal(newUserId)
+        notifySubscribers {
+            onUserChanged(newUserId, userContext)
+            onProfilesChanged(userProfiles)
         }
     }
 
@@ -200,17 +224,25 @@
         }
     }
 
-    private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) {
+    private inline fun notifySubscribers(
+            crossinline action: UserTracker.Callback.() -> Unit
+    ): CountDownLatch {
         val list = synchronized(callbacks) {
             callbacks.toList()
         }
+        val latch = CountDownLatch(list.size)
+
         list.forEach {
             if (it.callback.get() != null) {
                 it.executor.execute {
                     it.callback.get()?.action()
+                    latch.countDown()
                 }
+            } else {
+                latch.countDown()
             }
         }
+        return latch
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -257,4 +289,4 @@
     fun sameOrEmpty(other: UserTracker.Callback): Boolean {
         return callback.get()?.equals(other) ?: true
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index 2f62e44..809fa29 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -17,6 +17,7 @@
 package com.android.systemui.settings.dagger;
 
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.content.Context;
 import android.os.Handler;
 import android.os.UserManager;
@@ -57,11 +58,13 @@
     static UserTracker provideUserTracker(
             Context context,
             UserManager userManager,
+            IActivityManager iActivityManager,
             DumpManager dumpManager,
             @Background Handler handler
     ) {
         int startingUser = ActivityManager.getCurrentUser();
-        UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, dumpManager, handler);
+        UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, iActivityManager,
+                dumpManager, handler);
         tracker.initialize(startingUser);
         return tracker;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index bf4a768..b532c13 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4186,9 +4186,7 @@
     }
 
     private void updateStatusBarIcons() {
-        boolean showIconsWhenExpanded =
-                (isPanelVisibleBecauseOfHeadsUp() || mIsFullWidth)
-                        && getExpandedHeight() < getOpeningHeight();
+        boolean showIconsWhenExpanded = getExpandedHeight() < getOpeningHeight();
         if (showIconsWhenExpanded && isOnKeyguard()) {
             showIconsWhenExpanded = false;
         }
@@ -4255,7 +4253,7 @@
                 && mHeadsUpAppearanceController.shouldBeVisible()) {
             return false;
         }
-        return !mIsFullWidth || !mShowIconsWhenExpanded;
+        return !mShowIconsWhenExpanded;
     }
 
     private void onQsPanelScrollChanged(int scrollY) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
index ffd931c..197ae1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
@@ -18,9 +18,12 @@
 package com.android.systemui.statusbar.notification.logging
 
 import android.stats.sysui.NotificationEnums
+import android.util.Log
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.DumpsysTableLogger
+import com.android.systemui.dump.Row
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -33,6 +36,7 @@
 
     fun init() {
         dumpManager.registerNormalDumpable(javaClass.simpleName, this)
+        Log.i("NotificationMemory", "Registered dumpable.")
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -45,27 +49,36 @@
 
     /** Renders a table of notification object usage into passed [PrintWriter]. */
     private fun dumpNotificationObjects(pw: PrintWriter, memoryUse: List<NotificationMemoryUsage>) {
-        pw.println("Notification Object Usage")
-        pw.println("-----------")
-        pw.println(
-            "Package".padEnd(35) +
-                "\t\tSmall\tLarge\t${"Style".padEnd(15)}\t\tStyle\tBig\tExtend.\tExtras\tCustom"
-        )
-        pw.println("".padEnd(35) + "\t\tIcon\tIcon\t${"".padEnd(15)}\t\tIcon\tPicture\t \t \tView")
-        pw.println()
-
-        memoryUse.forEach { use ->
-            pw.println(
-                use.packageName.padEnd(35) +
-                    "\t\t" +
-                    "${use.objectUsage.smallIcon}\t${use.objectUsage.largeIcon}\t" +
-                    (styleEnumToString(use.objectUsage.style).take(15) ?: "").padEnd(15) +
-                    "\t\t${use.objectUsage.styleIcon}\t" +
-                    "${use.objectUsage.bigPicture}\t${use.objectUsage.extender}\t" +
-                    "${use.objectUsage.extras}\t${use.objectUsage.hasCustomView}\t" +
-                    use.notificationKey
+        val columns =
+            listOf(
+                "Package",
+                "Small Icon",
+                "Large Icon",
+                "Style",
+                "Style Icon",
+                "Big Picture",
+                "Extender",
+                "Extras",
+                "Custom View",
+                "Key"
             )
-        }
+        val rows: List<Row> =
+            memoryUse.map {
+                listOf(
+                    it.packageName,
+                    toKb(it.objectUsage.smallIcon),
+                    toKb(it.objectUsage.largeIcon),
+                    styleEnumToString(it.objectUsage.style),
+                    toKb(it.objectUsage.styleIcon),
+                    toKb(it.objectUsage.bigPicture),
+                    toKb(it.objectUsage.extender),
+                    toKb(it.objectUsage.extras),
+                    it.objectUsage.hasCustomView.toString(),
+                    // | is a  field delimiter in the output format so we need to replace
+                    // it to avoid breakage.
+                    it.notificationKey.replace('|', '│')
+                )
+            }
 
         // Calculate totals for easily glanceable summary.
         data class Totals(
@@ -88,18 +101,23 @@
                 t
             }
 
-        pw.println()
-        pw.println("TOTALS")
-        pw.println(
-            "".padEnd(35) +
-                "\t\t" +
-                "${toKb(totals.smallIcon)}\t${toKb(totals.largeIcon)}\t" +
-                "".padEnd(15) +
-                "\t\t${toKb(totals.styleIcon)}\t" +
-                "${toKb(totals.bigPicture)}\t${toKb(totals.extender)}\t" +
-                toKb(totals.extras)
-        )
-        pw.println()
+        val totalsRow: List<Row> =
+            listOf(
+                listOf(
+                    "TOTALS",
+                    toKb(totals.smallIcon),
+                    toKb(totals.largeIcon),
+                    "",
+                    toKb(totals.styleIcon),
+                    toKb(totals.bigPicture),
+                    toKb(totals.extender),
+                    toKb(totals.extras),
+                    "",
+                    ""
+                )
+            )
+        val tableLogger = DumpsysTableLogger("Notification Object Usage", columns, rows + totalsRow)
+        tableLogger.printTableData(pw)
     }
 
     /** Renders a table of notification view usage into passed [PrintWriter] */
@@ -116,40 +134,65 @@
             var softwareBitmapsPenalty: Int = 0,
         )
 
-        val totals = Totals()
-        pw.println("Notification View Usage")
-        pw.println("-----------")
-        pw.println("View Type".padEnd(24) + "\tSmall\tLarge\tStyle\tCustom\tSoftware")
-        pw.println("".padEnd(24) + "\tIcon\tIcon\tUse\tView\tBitmaps")
-        pw.println()
-        memoryUse
-            .filter { it.viewUsage.isNotEmpty() }
-            .forEach { use ->
-                pw.println(use.packageName + " " + use.notificationKey)
-                use.viewUsage.forEach { view ->
-                    pw.println(
-                        "  ${view.viewType.toString().padEnd(24)}\t${view.smallIcon}" +
-                            "\t${view.largeIcon}\t${view.style}" +
-                            "\t${view.customViews}\t${view.softwareBitmapsPenalty}"
-                    )
-
-                    if (view.viewType == ViewType.TOTAL) {
-                        totals.smallIcon += view.smallIcon
-                        totals.largeIcon += view.largeIcon
-                        totals.style += view.style
-                        totals.customViews += view.customViews
-                        totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty
+        val columns =
+            listOf(
+                "Package",
+                "View Type",
+                "Small Icon",
+                "Large Icon",
+                "Style Use",
+                "Custom View",
+                "Software Bitmaps",
+                "Key"
+            )
+        val rows =
+            memoryUse
+                .filter { it.viewUsage.isNotEmpty() }
+                .flatMap { use ->
+                    use.viewUsage.map { view ->
+                        listOf(
+                            use.packageName,
+                            view.viewType.toString(),
+                            toKb(view.smallIcon),
+                            toKb(view.largeIcon),
+                            toKb(view.style),
+                            toKb(view.customViews),
+                            toKb(view.softwareBitmapsPenalty),
+                            // | is a  field delimiter in the output format so we need to replace
+                            // it to avoid breakage.
+                            use.notificationKey.replace('|', '│')
+                        )
                     }
                 }
+
+        val totals = Totals()
+        memoryUse
+            .filter { it.viewUsage.isNotEmpty() }
+            .map { it.viewUsage.firstOrNull { view -> view.viewType == ViewType.TOTAL } }
+            .filterNotNull()
+            .forEach { view ->
+                totals.smallIcon += view.smallIcon
+                totals.largeIcon += view.largeIcon
+                totals.style += view.style
+                totals.customViews += view.customViews
+                totals.softwareBitmapsPenalty += view.softwareBitmapsPenalty
             }
-        pw.println()
-        pw.println("TOTALS")
-        pw.println(
-            "  ${"".padEnd(24)}\t${toKb(totals.smallIcon)}" +
-                "\t${toKb(totals.largeIcon)}\t${toKb(totals.style)}" +
-                "\t${toKb(totals.customViews)}\t${toKb(totals.softwareBitmapsPenalty)}"
-        )
-        pw.println()
+
+        val totalsRow: List<Row> =
+            listOf(
+                listOf(
+                    "TOTALS",
+                    "",
+                    toKb(totals.smallIcon),
+                    toKb(totals.largeIcon),
+                    toKb(totals.style),
+                    toKb(totals.customViews),
+                    toKb(totals.softwareBitmapsPenalty),
+                    ""
+                )
+            )
+        val tableLogger = DumpsysTableLogger("Notification View Usage", columns, rows + totalsRow)
+        tableLogger.printTableData(pw)
     }
 
     private fun styleEnumToString(styleEnum: Int): String =
@@ -168,6 +211,10 @@
         }
 
     private fun toKb(bytes: Int): String {
-        return (bytes / 1024).toString() + " KB"
+        if (bytes == 0) {
+            return "--"
+        }
+
+        return "%.2f KB".format(bytes / 1024f)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index ec08bd4..e630271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -867,6 +867,11 @@
         wiredChargingRippleController.registerCallbacks();
 
         mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
+
+        // Based on teamfood flag, turn predictive back dispatch on at runtime.
+        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
+            mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
+        }
     }
 
     @Override
@@ -981,11 +986,6 @@
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy.init();
 
-        // Based on teamfood flag, turn predictive back dispatch on at runtime.
-        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
-            mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
-        }
-
         mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
             public void onUnlockedChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index f784723..80093a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -791,20 +791,28 @@
             if (!mScreenOffAnimationController.shouldExpandNotifications()
                     && !mAnimatingPanelExpansionOnUnlock
                     && !occluding) {
-                float behindFraction = getInterpolatedFraction();
-                behindFraction = (float) Math.pow(behindFraction, 0.8f);
                 if (mClipsQsScrim) {
+                    float behindFraction = getInterpolatedFraction();
+                    behindFraction = (float) Math.pow(behindFraction, 0.8f);
                     mBehindAlpha = mTransparentScrimBackground ? 0 : 1;
                     mNotificationsAlpha =
                             mTransparentScrimBackground ? 0 : behindFraction * mDefaultScrimAlpha;
                 } else {
-                    mBehindAlpha =
-                            mTransparentScrimBackground ? 0 : behindFraction * mDefaultScrimAlpha;
-                    // Delay fade-in of notification scrim a bit further, to coincide with the
-                    // view fade in. Otherwise the empty panel can be quite jarring.
-                    mNotificationsAlpha = mTransparentScrimBackground
-                            ? 0 : MathUtils.constrainedMap(0f, 1f, 0.3f, 0.75f,
-                            mPanelExpansionFraction);
+                    if (mTransparentScrimBackground) {
+                        mBehindAlpha = 0;
+                        mNotificationsAlpha = 0;
+                    } else {
+                        // Behind scrim will finish fading in at 30% expansion.
+                        float behindFraction = MathUtils
+                                .constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction);
+                        mBehindAlpha = behindFraction * mDefaultScrimAlpha;
+                        // Delay fade-in of notification scrim a bit further, to coincide with the
+                        // behind scrim finishing fading in.
+                        // Also to coincide with the view starting to fade in, otherwise the empty
+                        // panel can be quite jarring.
+                        mNotificationsAlpha = MathUtils
+                                .constrainedMap(0f, 1f, 0.3f, 0.75f, mPanelExpansionFraction);
+                    }
                 }
                 mBehindTint = mState.getBehindTint();
                 mInFrontAlpha = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 523cf68..0069bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -36,6 +36,8 @@
 import android.view.WindowManager
 import android.view.WindowlessWindowManager
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.LightRevealEffect
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.LinearLightRevealEffect
@@ -57,6 +59,7 @@
 @Inject
 constructor(
     private val context: Context,
+    private val featureFlags: FeatureFlags,
     private val deviceStateManager: DeviceStateManager,
     private val contentResolver: ContentResolver,
     private val displayManager: DisplayManager,
@@ -81,6 +84,7 @@
     private var scrimView: LightRevealScrim? = null
     private var isFolded: Boolean = false
     private var isUnfoldHandled: Boolean = true
+    private var overlayAddReason: AddOverlayReason? = null
 
     private var currentRotation: Int = context.display!!.rotation
 
@@ -158,6 +162,8 @@
         ensureInBackground()
         ensureOverlayRemoved()
 
+        overlayAddReason = reason
+
         val newRoot = SurfaceControlViewHost(context, context.display!!, wwm)
         val params = getLayoutParams()
         val newView =
@@ -170,11 +176,7 @@
                 .apply {
                     revealEffect = createLightRevealEffect()
                     isScrimOpaqueChangedListener = Consumer {}
-                    revealAmount =
-                        when (reason) {
-                            FOLD -> TRANSPARENT
-                            UNFOLD -> BLACK
-                        }
+                    revealAmount = calculateRevealAmount()
                 }
 
         newRoot.setView(newView, params)
@@ -207,6 +209,31 @@
         root = newRoot
     }
 
+    private fun calculateRevealAmount(animationProgress: Float? = null): Float {
+        val overlayAddReason = overlayAddReason ?: UNFOLD
+
+        if (animationProgress == null) {
+            // Animation progress is unknown, calculate the initial value based on the overlay
+            // add reason
+            return when (overlayAddReason) {
+                FOLD -> TRANSPARENT
+                UNFOLD -> BLACK
+            }
+        }
+
+        val showVignetteWhenFolding =
+            featureFlags.isEnabled(Flags.ENABLE_DARK_VIGNETTE_WHEN_FOLDING)
+
+        return if (!showVignetteWhenFolding && overlayAddReason == FOLD) {
+            // Do not darken the content when SHOW_VIGNETTE_WHEN_FOLDING flag is off
+            // and we are folding the device. We still add the overlay to block touches
+            // while the animation is running but the overlay is transparent.
+            TRANSPARENT
+        } else {
+            animationProgress
+        }
+    }
+
     private fun getLayoutParams(): WindowManager.LayoutParams {
         val params: WindowManager.LayoutParams = WindowManager.LayoutParams()
 
@@ -259,7 +286,7 @@
     private inner class TransitionListener : TransitionProgressListener {
 
         override fun onTransitionProgress(progress: Float) {
-            executeInBackground { scrimView?.revealAmount = progress }
+            executeInBackground { scrimView?.revealAmount = calculateRevealAmount(progress) }
         }
 
         override fun onTransitionFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index c0f0390..8cb4deb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -174,7 +174,7 @@
 
                 val callback =
                     object : UserTracker.Callback {
-                        override fun onUserChanged(newUser: Int, userContext: Context) {
+                        override fun onUserChanging(newUser: Int, userContext: Context) {
                             send()
                         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index b61b2e6..47d505e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -16,14 +16,21 @@
 
 package com.android.systemui.util.kotlin
 
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.time.SystemClockImpl
+import kotlinx.coroutines.CoroutineStart
 import java.util.concurrent.atomic.AtomicReference
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
+import kotlin.math.max
 
 /**
  * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -167,3 +174,89 @@
  * Note that the returned Flow will not emit anything until [other] has emitted at least one value.
  */
 fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a }
+
+/**
+ * Returns a flow that mirrors the original flow, but delays values following emitted values for the
+ * given [periodMs]. If the original flow emits more than one value during this period, only the
+ * latest value is emitted.
+ *
+ * Example:
+ *
+ * ```kotlin
+ * flow {
+ *     emit(1)     // t=0ms
+ *     delay(90)
+ *     emit(2)     // t=90ms
+ *     delay(90)
+ *     emit(3)     // t=180ms
+ *     delay(1010)
+ *     emit(4)     // t=1190ms
+ *     delay(1010)
+ *     emit(5)     // t=2200ms
+ * }.throttle(1000)
+ * ```
+ *
+ * produces the following emissions at the following times
+ *
+ * ```text
+ * 1 (t=0ms), 3 (t=1000ms), 4 (t=2000ms), 5 (t=3000ms)
+ * ```
+ */
+fun <T> Flow<T>.throttle(periodMs: Long): Flow<T> = this.throttle(periodMs, SystemClockImpl())
+
+/**
+ * Returns a flow that mirrors the original flow, but delays values following emitted values for the
+ * given [periodMs] as reported by the given [clock]. If the original flow emits more than one value
+ * during this period, only The latest value is emitted.
+ *
+ * Example:
+ *
+ * ```kotlin
+ * flow {
+ *     emit(1)     // t=0ms
+ *     delay(90)
+ *     emit(2)     // t=90ms
+ *     delay(90)
+ *     emit(3)     // t=180ms
+ *     delay(1010)
+ *     emit(4)     // t=1190ms
+ *     delay(1010)
+ *     emit(5)     // t=2200ms
+ * }.throttle(1000)
+ * ```
+ *
+ * produces the following emissions at the following times
+ *
+ * ```text
+ * 1 (t=0ms), 3 (t=1000ms), 4 (t=2000ms), 5 (t=3000ms)
+ * ```
+ */
+fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock): Flow<T> = channelFlow {
+    coroutineScope {
+        var previousEmitTimeMs = 0L
+        var delayJob: Job? = null
+        var sendJob: Job? = null
+        val outerScope = this
+
+        collect {
+            delayJob?.cancel()
+            sendJob?.join()
+            val currentTimeMs = clock.elapsedRealtime()
+            val timeSinceLastEmit = currentTimeMs - previousEmitTimeMs
+            val timeUntilNextEmit = max(0L, periodMs - timeSinceLastEmit)
+            if (timeUntilNextEmit > 0L) {
+                // We create delayJob to allow cancellation during the delay period
+                delayJob = launch {
+                    delay(timeUntilNextEmit)
+                    sendJob = outerScope.launch(start = CoroutineStart.UNDISPATCHED) {
+                        send(it)
+                        previousEmitTimeMs = clock.elapsedRealtime()
+                    }
+                }
+            } else {
+                send(it)
+                previousEmitTimeMs = currentTimeMs
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index f71d988..a453726 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -25,6 +25,7 @@
 import android.provider.Settings;
 import android.view.WindowManager.LayoutParams;
 
+import com.android.internal.R;
 import com.android.settingslib.applications.InterestingConfigChanges;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.demomode.DemoMode;
@@ -55,7 +56,7 @@
     public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent";
     public static final String VOLUME_SILENT_DO_NOT_DISTURB = "sysui_do_not_disturb";
 
-    public static final boolean DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT = false;
+    private final boolean mDefaultVolumeDownToEnterSilent;
     public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
     public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
 
@@ -72,12 +73,7 @@
     private final KeyguardViewMediator mKeyguardViewMediator;
     private final ActivityStarter mActivityStarter;
     private VolumeDialog mDialog;
-    private VolumePolicy mVolumePolicy = new VolumePolicy(
-            DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT,  // volumeDownToEnterSilent
-            DEFAULT_VOLUME_UP_TO_EXIT_SILENT,  // volumeUpToExitSilent
-            DEFAULT_DO_NOT_DISTURB_WHEN_SILENT,  // doNotDisturbWhenSilent
-            400    // vibrateToSilentDebounce
-    );
+    private VolumePolicy mVolumePolicy;
 
     @Inject
     public VolumeDialogComponent(
@@ -107,7 +103,20 @@
                     mDialog = dialog;
                     mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
                 }).build();
+
+
+        mDefaultVolumeDownToEnterSilent = mContext.getResources()
+                .getBoolean(R.bool.config_volume_down_to_enter_silent);
+
+        mVolumePolicy = new VolumePolicy(
+                mDefaultVolumeDownToEnterSilent,  // volumeDownToEnterSilent
+                DEFAULT_VOLUME_UP_TO_EXIT_SILENT,  // volumeUpToExitSilent
+                DEFAULT_DO_NOT_DISTURB_WHEN_SILENT,  // doNotDisturbWhenSilent
+                400    // vibrateToSilentDebounce
+        );
+
         applyConfiguration();
+
         tunerService.addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
                 VOLUME_SILENT_DO_NOT_DISTURB);
         demoModeController.addCallback(this);
@@ -121,7 +130,7 @@
 
         if (VOLUME_DOWN_SILENT.equals(key)) {
             volumeDownToEnterSilent =
-                TunerService.parseIntegerSwitch(newValue, DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT);
+                TunerService.parseIntegerSwitch(newValue, mDefaultVolumeDownToEnterSilent);
         } else if (VOLUME_UP_SILENT.equals(key)) {
             volumeUpToExitSilent =
                 TunerService.parseIntegerSwitch(newValue, DEFAULT_VOLUME_UP_TO_EXIT_SILENT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
new file mode 100644
index 0000000..a1d42a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.notetask.quickaffordance
+
+import android.test.suitebuilder.annotation.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+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.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [NoteTaskQuickAffordanceConfig].
+ *
+ * Build/Install/Run:
+ * - atest SystemUITests:NoteTaskQuickAffordanceConfigTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
+
+    @Mock lateinit var noteTaskController: NoteTaskController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(noteTaskController.showNoteTask()).then {}
+    }
+
+    private fun createUnderTest(isEnabled: Boolean) =
+        NoteTaskQuickAffordanceConfig(
+            context = context,
+            noteTaskController = noteTaskController,
+            isEnabled = isEnabled,
+        )
+
+    @Test
+    fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
+        val underTest = createUnderTest(isEnabled = false)
+
+        val actual = collectLastValue(underTest.lockScreenState)
+
+        assertThat(actual()).isEqualTo(LockScreenState.Hidden)
+    }
+
+    @Test
+    fun lockScreenState_isEnabled_shouldEmitVisible() = runTest {
+        val stringResult = "Notetaking"
+        val underTest = createUnderTest(isEnabled = true)
+
+        val actual = collectLastValue(underTest.lockScreenState)
+
+        val expected =
+            LockScreenState.Visible(
+                icon =
+                    Icon.Resource(
+                        res = R.drawable.ic_note_task_shortcut_keyguard,
+                        contentDescription = ContentDescription.Loaded(stringResult),
+                    )
+            )
+        assertThat(actual()).isEqualTo(expected)
+    }
+
+    @Test
+    fun onTriggered_shouldLaunchNoteTask() {
+        val underTest = createUnderTest(isEnabled = false)
+
+        underTest.onTriggered(expandable = null)
+
+        verify(noteTaskController).showNoteTask()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index 3710281..57b6b2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.settings
 
+import android.app.IActivityManager
 import android.content.Context
 import android.content.Intent
 import android.content.pm.UserInfo
@@ -51,6 +52,7 @@
 
     @Mock private lateinit var context: Context
     @Mock private lateinit var userManager: UserManager
+    @Mock private lateinit var iActivityManager: IActivityManager
     @Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
     @Mock(stubOnly = true) private lateinit var handler: Handler
 
@@ -67,7 +69,7 @@
         `when`(context.user).thenReturn(UserHandle.SYSTEM)
         `when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
 
-        tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+        tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index e65bbb1..71ba215 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -16,11 +16,14 @@
 
 package com.android.systemui.settings
 
+import android.app.IActivityManager
+import android.app.IUserSwitchObserver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
 import android.content.pm.UserInfo
 import android.os.Handler
+import android.os.IRemoteCallback
 import android.os.UserHandle
 import android.os.UserManager
 import android.testing.AndroidTestingRunner
@@ -29,19 +32,20 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.util.mockito.capture
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -51,6 +55,10 @@
     private lateinit var context: Context
     @Mock
     private lateinit var userManager: UserManager
+    @Mock
+    private lateinit var iActivityManager: IActivityManager
+    @Mock
+    private lateinit var userSwitchingReply: IRemoteCallback
     @Mock(stubOnly = true)
     private lateinit var dumpManager: DumpManager
     @Mock(stubOnly = true)
@@ -76,7 +84,7 @@
             listOf(info)
         }
 
-        tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+        tracker = UserTrackerImpl(context, userManager, iActivityManager, dumpManager, handler)
     }
 
     @Test
@@ -125,8 +133,7 @@
         verify(context).registerReceiverForAllUsers(
                 eq(tracker), capture(captor), isNull(), eq(handler))
         with(captor.value) {
-            assertThat(countActions()).isEqualTo(7)
-            assertThat(hasAction(Intent.ACTION_USER_SWITCHED)).isTrue()
+            assertThat(countActions()).isEqualTo(6)
             assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
             assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
@@ -158,8 +165,10 @@
         tracker.initialize(0)
         val newID = 5
 
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
-        tracker.onReceive(context, intent)
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onUserSwitching(newID, userSwitchingReply)
+        verify(userSwitchingReply).sendResult(any())
 
         verify(userManager).getProfiles(newID)
 
@@ -272,6 +281,24 @@
     }
 
     @Test
+    fun testCallbackCalledOnUserChanging() {
+        tracker.initialize(0)
+        val callback = TestCallback()
+        tracker.addCallback(callback, executor)
+
+        val newID = 5
+
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onUserSwitching(newID, userSwitchingReply)
+        verify(userSwitchingReply).sendResult(any())
+
+        assertThat(callback.calledOnUserChanging).isEqualTo(1)
+        assertThat(callback.lastUser).isEqualTo(newID)
+        assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+    }
+
+    @Test
     fun testCallbackCalledOnUserChanged() {
         tracker.initialize(0)
         val callback = TestCallback()
@@ -279,8 +306,9 @@
 
         val newID = 5
 
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
-        tracker.onReceive(context, intent)
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onUserSwitchComplete(newID)
 
         assertThat(callback.calledOnUserChanged).isEqualTo(1)
         assertThat(callback.lastUser).isEqualTo(newID)
@@ -330,25 +358,36 @@
         tracker.addCallback(callback, executor)
         tracker.removeCallback(callback)
 
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, 5)
-        tracker.onReceive(context, intent)
+        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+        captor.value.onUserSwitching(newID, userSwitchingReply)
+        verify(userSwitchingReply).sendResult(any())
+        captor.value.onUserSwitchComplete(newID)
 
         val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
                 .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
 
         tracker.onReceive(context, intentProfiles)
 
+        assertThat(callback.calledOnUserChanging).isEqualTo(0)
         assertThat(callback.calledOnUserChanged).isEqualTo(0)
         assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
     }
 
     private class TestCallback : UserTracker.Callback {
+        var calledOnUserChanging = 0
         var calledOnUserChanged = 0
         var calledOnProfilesChanged = 0
         var lastUser: Int? = null
         var lastUserContext: Context? = null
         var lastUserProfiles = emptyList<UserInfo>()
 
+        override fun onUserChanging(newUser: Int, userContext: Context) {
+            calledOnUserChanging++
+            lastUser = newUser
+            lastUserContext = userContext
+        }
+
         override fun onUserChanged(newUser: Int, userContext: Context) {
             calledOnUserChanged++
             lastUser = newUser
@@ -360,4 +399,4 @@
             lastUserProfiles = profiles
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 4c1b219..cf69515 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -321,6 +321,10 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        // CentralSurfacesImpl's runtime flag check fails if the flag is absent.
+        // This value is unused, because test manifest is opted in.
+        mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
+
         IThermalService thermalService = mock(IThermalService.class);
         mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
                 Handler.createAsync(Looper.myLooper()));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index c7a0582..c0537a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -630,6 +630,7 @@
 
     @Test
     public void transitionToUnlocked() {
+        mScrimController.setClipsQsScrim(false);
         mScrimController.setRawPanelExpansionFraction(0f);
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
@@ -645,14 +646,21 @@
         ));
 
         // Back scrim should be visible after start dragging
-        mScrimController.setRawPanelExpansionFraction(0.3f);
+        mScrimController.setRawPanelExpansionFraction(0.29f);
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mNotificationsScrim, TRANSPARENT,
                 mScrimBehind, SEMI_TRANSPARENT));
 
+        // Back scrim should be opaque at 30%
+        mScrimController.setRawPanelExpansionFraction(0.3f);
+        assertScrimAlpha(Map.of(
+                mScrimInFront, TRANSPARENT,
+                mNotificationsScrim, TRANSPARENT,
+                mScrimBehind, OPAQUE));
+
         // Then, notification scrim should fade in
-        mScrimController.setRawPanelExpansionFraction(0.7f);
+        mScrimController.setRawPanelExpansionFraction(0.31f);
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mNotificationsScrim, SEMI_TRANSPARENT,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 6bfc2f1..7d0d57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -19,9 +19,12 @@
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -35,6 +38,10 @@
 import kotlinx.coroutines.flow.toList
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.yield
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -231,6 +238,170 @@
     }
 }
 
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ThrottleFlowTest : SysuiTestCase() {
+
+    @Test
+    fun doesNotAffectEmissions_whenDelayAtLeastEqualToPeriod() = runTest {
+        // Arrange
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<Int>()
+        val collectJob = backgroundScope.launch {
+            flow {
+                emit(1)
+                delay(1000)
+                emit(2)
+            }.throttle(1000, choreographer.fakeClock).toList(output)
+        }
+
+        // Act
+        choreographer.advanceAndRun(0)
+
+        // Assert
+        assertThat(output).containsExactly(1)
+
+        // Act
+        choreographer.advanceAndRun(999)
+
+        // Assert
+        assertThat(output).containsExactly(1)
+
+        // Act
+        choreographer.advanceAndRun(1)
+
+        // Assert
+        assertThat(output).containsExactly(1, 2)
+
+        // Cleanup
+        collectJob.cancel()
+    }
+
+    @Test
+    fun delaysEmissions_withShorterThanPeriodDelay_untilPeriodElapses() = runTest {
+        // Arrange
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<Int>()
+        val collectJob = backgroundScope.launch {
+            flow {
+                emit(1)
+                delay(500)
+                emit(2)
+            }.throttle(1000, choreographer.fakeClock).toList(output)
+        }
+
+        // Act
+        choreographer.advanceAndRun(0)
+
+        // Assert
+        assertThat(output).containsExactly(1)
+
+        // Act
+        choreographer.advanceAndRun(500)
+        choreographer.advanceAndRun(499)
+
+        // Assert
+        assertThat(output).containsExactly(1)
+
+        // Act
+        choreographer.advanceAndRun(1)
+
+        // Assert
+        assertThat(output).containsExactly(1, 2)
+
+        // Cleanup
+        collectJob.cancel()
+    }
+
+    @Test
+    fun filtersAllButLastEmission_whenMultipleEmissionsInPeriod() = runTest {
+        // Arrange
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<Int>()
+        val collectJob = backgroundScope.launch {
+            flow {
+                emit(1)
+                delay(500)
+                emit(2)
+                delay(500)
+                emit(3)
+            }.throttle(1000, choreographer.fakeClock).toList(output)
+        }
+
+        // Act
+        choreographer.advanceAndRun(0)
+
+        // Assert
+        assertThat(output).containsExactly(1)
+
+        // Act
+        choreographer.advanceAndRun(500)
+        choreographer.advanceAndRun(499)
+
+        // Assert
+        assertThat(output).containsExactly(1)
+
+        // Act
+        choreographer.advanceAndRun(1)
+
+        // Assert
+        assertThat(output).containsExactly(1, 3)
+
+        // Cleanup
+        collectJob.cancel()
+    }
+
+    @Test
+    fun filtersAllButLastEmission_andDelaysIt_whenMultipleEmissionsInShorterThanPeriod() = runTest {
+        // Arrange
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<Int>()
+        val collectJob = backgroundScope.launch {
+            flow {
+                emit(1)
+                delay(500)
+                emit(2)
+                delay(250)
+                emit(3)
+            }.throttle(1000, choreographer.fakeClock).toList(output)
+        }
+
+        // Act
+        choreographer.advanceAndRun(0)
+
+        // Assert
+        assertThat(output).containsExactly(1)
+
+        // Act
+        choreographer.advanceAndRun(500)
+        choreographer.advanceAndRun(250)
+        choreographer.advanceAndRun(249)
+
+        // Assert
+        assertThat(output).containsExactly(1)
+
+        // Act
+        choreographer.advanceAndRun(1)
+
+        // Assert
+        assertThat(output).containsExactly(1, 3)
+
+        // Cleanup
+        collectJob.cancel()
+    }
+
+    private fun createChoreographer(testScope: TestScope) = object {
+        val fakeClock = FakeSystemClock()
+
+        fun advanceAndRun(millis: Long) {
+            fakeClock.advanceTime(millis)
+            testScope.advanceTimeBy(millis)
+            testScope.runCurrent()
+        }
+    }
+}
+
 private fun <T> assertThatFlow(flow: Flow<T>) =
     object {
         suspend fun emitsExactly(vararg emissions: T) =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 0dd1fc7..251014f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -67,7 +67,10 @@
         _userHandle = UserHandle.of(_userId)
 
         val copy = callbacks.toList()
-        copy.forEach { it.onUserChanged(_userId, userContext) }
+        copy.forEach {
+            it.onUserChanging(_userId, userContext)
+            it.onUserChanged(_userId, userContext)
+        }
     }
 
     fun onProfileChanged() {
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index b20ff37..2ee9174 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -19,6 +19,7 @@
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.TAG;
 import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.UserBackupManagerService.WALLPAPER_PACKAGE;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -56,8 +57,8 @@
 public class BackupEligibilityRules {
     private static final boolean DEBUG = false;
     // List of system packages that are eligible for backup in non-system users.
-    private static final Set<String> systemPackagesAllowedForAllUsers =
-            Sets.newArraySet(PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME, WALLPAPER_PACKAGE);
+    private static final Set<String> systemPackagesAllowedForAllUsers = Sets.newArraySet(
+            PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME, WALLPAPER_PACKAGE, SETTINGS_PACKAGE);
 
     private final PackageManager mPackageManager;
     private final PackageManagerInternal mPackageManagerInternal;
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 67f5f1b..fce44f5 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1074,11 +1074,6 @@
         // register a package observer to detect updates to preloads
         mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() {
             @Override
-            public void onPackageAdded(String packageName, int uid) {
-
-            }
-
-            @Override
             public void onPackageChanged(String packageName, int uid) {
                 // check if the updated package is a preloaded app.
                 PackageManager pm = mContext.getPackageManager();
@@ -1094,11 +1089,6 @@
                 UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
                         BinaryTransparencyService.this);
             }
-
-            @Override
-            public void onPackageRemoved(String packageName, int uid) {
-
-            }
         });
 
         // TODO(b/264428429): Register observer for updates to APEXs.
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
index abaa8c7..31ea092 100644
--- a/services/core/java/com/android/server/am/ActivityManagerLocal.java
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -75,8 +75,9 @@
      * @param conn Receives information as the service is started and stopped.
      *        This must be a valid ServiceConnection object; it must not be null.
      * @param clientAppUid Uid of the app for which the sdk sandbox process needs to be spawned.
-     * @param clientApplicationThread ApplicationThread object of the app for which the sdk sandboox
-     *                                is spawned.
+     * @param clientAppProcessToken process token used to uniquely identify the client app
+     *        process binding to the SDK sandbox. This is obtained using
+     *        {@link Context#getProcessToken()}.
      * @param clientAppPackage Package of the app for which the sdk sandbox process needs to
      *        be spawned. This package must belong to the clientAppUid.
      * @param processName Unique identifier for the service instance. Each unique name here will
@@ -92,7 +93,7 @@
      */
     @SuppressLint("RethrowRemoteException")
     boolean bindSdkSandboxService(@NonNull Intent service, @NonNull ServiceConnection conn,
-            int clientAppUid, @NonNull IBinder clientApplicationThread,
+            int clientAppUid, @NonNull IBinder clientAppProcessToken,
             @NonNull String clientAppPackage, @NonNull String processName,
             @Context.BindServiceFlags int flags)
             throws RemoteException;
@@ -112,9 +113,9 @@
     /**
      * Kill an app process associated with an SDK sandbox.
      *
-     * @param clientApplicationThreadBinder binder value of the
-     *        {@link android.app.IApplicationThread} of a client app process associated with a
-     *        sandbox. This is obtained using {@link Context#getIApplicationThreadBinder()}.
+     * @param clientAppProcessToken process token used to uniquely identify the client app
+     *        process associated with an SDK sandbox. This is obtained using
+     *        {@link Context#getProcessToken()}.
      */
-    void killSdkSandboxClientAppProcess(@NonNull IBinder clientApplicationThreadBinder);
+    void killSdkSandboxClientAppProcess(@NonNull IBinder clientAppProcessToken);
 }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3df060b..67166b8 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -61,7 +61,6 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
-import android.app.Dialog;
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
 import android.app.KeyguardManager;
@@ -1677,6 +1676,7 @@
                         R.anim.screen_user_exit, R.anim.screen_user_enter);
                 t.traceEnd();
             }
+            dismissUserSwitchDialog(); // so that we don't hold a reference to mUserSwitchingDialog
 
             boolean needStart = false;
             boolean updateUmState = false;
@@ -1863,6 +1863,8 @@
         boolean success = startUser(targetUserId, USER_START_MODE_FOREGROUND);
         if (!success) {
             mInjector.getWindowManager().setSwitchingUser(false);
+            mTargetUserId = UserHandle.USER_NULL;
+            dismissUserSwitchDialog();
         }
     }
 
@@ -2011,6 +2013,10 @@
         return true;
     }
 
+    private void dismissUserSwitchDialog() {
+        mInjector.dismissUserSwitchingDialog();
+    }
+
     private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
         // The dialog will show and then initiate the user switch by calling startUserInForeground
         mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
@@ -3462,6 +3468,9 @@
         private UserManagerService mUserManager;
         private UserManagerInternal mUserManagerInternal;
         private Handler mHandler;
+        private final Object mUserSwitchingDialogLock = new Object();
+        @GuardedBy("mUserSwitchingDialogLock")
+        private UserSwitchingDialog mUserSwitchingDialog;
 
         Injector(ActivityManagerService service) {
             mService = service;
@@ -3637,6 +3646,15 @@
             mService.mCpHelper.installEncryptionUnawareProviders(userId);
         }
 
+        void dismissUserSwitchingDialog() {
+            synchronized (mUserSwitchingDialogLock) {
+                if (mUserSwitchingDialog != null) {
+                    mUserSwitchingDialog.dismiss();
+                    mUserSwitchingDialog = null;
+                }
+            }
+        }
+
         void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
                 String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
             if (mService.mContext.getPackageManager()
@@ -3647,10 +3665,13 @@
                 Slogf.w(TAG, "Showing user switch dialog on UserController, it could cause a race "
                         + "condition if it's shown by CarSystemUI as well");
             }
-            final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser,
-                    toUser, true /* above system */, switchingFromSystemUserMessage,
-                    switchingToSystemUserMessage);
-            d.show();
+            synchronized (mUserSwitchingDialogLock) {
+                dismissUserSwitchingDialog();
+                mUserSwitchingDialog = new UserSwitchingDialog(mService, mService.mContext,
+                        fromUser, toUser, true /* above system */, switchingFromSystemUserMessage,
+                        switchingToSystemUserMessage);
+                mUserSwitchingDialog.show();
+            }
         }
 
         void reportGlobalUsageEvent(int event) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9877ed3..21bd7bc 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1718,8 +1718,9 @@
                 mBrokerEventWakeLock.acquire(BROKER_WAKELOCK_TIMEOUT_MS);
             } catch (Exception e) {
                 Log.e(TAG, "Exception acquiring wakelock", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            Binder.restoreCallingIdentity(identity);
         }
 
         if (MESSAGES_MUTE_MUSIC.contains(msg)) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cb98c66..92a9f46 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -908,9 +908,6 @@
 
     private final SoundDoseHelper mSoundDoseHelper;
 
-    @GuardedBy("mSettingsLock")
-    private int mCurrentImeUid;
-
     private final Object mSupportedSystemUsagesLock = new Object();
     @GuardedBy("mSupportedSystemUsagesLock")
     private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -2856,10 +2853,14 @@
         super.getPreferredDevicesForStrategy_enforcePermission();
 
         List<AudioDeviceAttributes> devices = new ArrayList<>();
+        int status = AudioSystem.SUCCESS;
         final long identity = Binder.clearCallingIdentity();
-        final int status = AudioSystem.getDevicesForRoleAndStrategy(
-                strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
-        Binder.restoreCallingIdentity(identity);
+        try {
+            status = AudioSystem.getDevicesForRoleAndStrategy(
+                    strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
         if (status != AudioSystem.SUCCESS) {
             Log.e(TAG, String.format("Error %d in getPreferredDeviceForStrategy(%d)",
                     status, strategy));
@@ -3045,10 +3046,14 @@
         super.getPreferredDevicesForCapturePreset_enforcePermission();
 
         List<AudioDeviceAttributes> devices = new ArrayList<>();
+        int status = AudioSystem.SUCCESS;
         final long identity = Binder.clearCallingIdentity();
-        final int status = AudioSystem.getDevicesForRoleAndCapturePreset(
-                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
-        Binder.restoreCallingIdentity(identity);
+        try {
+            status = AudioSystem.getDevicesForRoleAndCapturePreset(
+                    capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
         if (status != AudioSystem.SUCCESS) {
             Log.e(TAG, String.format("Error %d in getPreferredDeviceForCapturePreset(%d)",
                     status, capturePreset));
@@ -3653,11 +3658,14 @@
         }
 
         final long identity = Binder.clearCallingIdentity();
-        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
-                isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC),
-                getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
-                isStreamMute(AudioSystem.STREAM_MUSIC));
-        Binder.restoreCallingIdentity(identity);
+        try {
+            mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
+                    isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC),
+                    getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
+                    isStreamMute(AudioSystem.STREAM_MUSIC));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     private int getNewRingerMode(int stream, int index, int flags) {
@@ -4985,26 +4993,26 @@
         if (getCurrentUserId() == userId || userId == android.os.Process.SYSTEM_UID) {
             final boolean currentMute = mAudioSystem.isMicrophoneMuted();
             final long identity = Binder.clearCallingIdentity();
-            final int ret = mAudioSystem.muteMicrophone(muted);
-
-            // update cache with the real state independently from what was set
-            mMicMuteFromSystemCached = mAudioSystem.isMicrophoneMuted();
-            if (ret != AudioSystem.AUDIO_STATUS_OK) {
-                Log.e(TAG, "Error changing mic mute state to " + muted + " current:"
-                        + mMicMuteFromSystemCached);
-            }
-
-            new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC)
-                    .setUid(userId)
-                    .set(MediaMetrics.Property.EVENT, "setMicrophoneMuteNoCallerCheck")
-                    .set(MediaMetrics.Property.MUTE, mMicMuteFromSystemCached
-                            ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
-                    .set(MediaMetrics.Property.REQUEST, muted
-                            ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE)
-                    .set(MediaMetrics.Property.STATUS, ret)
-                    .record();
-
             try {
+                final int ret = mAudioSystem.muteMicrophone(muted);
+
+                // update cache with the real state independently from what was set
+                mMicMuteFromSystemCached = mAudioSystem.isMicrophoneMuted();
+                if (ret != AudioSystem.AUDIO_STATUS_OK) {
+                    Log.e(TAG, "Error changing mic mute state to " + muted + " current:"
+                            + mMicMuteFromSystemCached);
+                }
+
+                new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC)
+                        .setUid(userId)
+                        .set(MediaMetrics.Property.EVENT, "setMicrophoneMuteNoCallerCheck")
+                        .set(MediaMetrics.Property.MUTE, mMicMuteFromSystemCached
+                                ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
+                        .set(MediaMetrics.Property.REQUEST, muted
+                                ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE)
+                        .set(MediaMetrics.Property.STATUS, ret)
+                        .record();
+
                 // send the intent even if there was a failure to change the actual mute state:
                 // the AudioManager.setMicrophoneMute API doesn't have a return value to
                 // indicate if the call failed to successfully change the mute state, and receiving
@@ -5614,9 +5622,13 @@
                     + mMode.get() + " requested mode: " + requestedMode);
         }
         if (mode != mMode.get() || force) {
+            int status = AudioSystem.SUCCESS;
             final long identity = Binder.clearCallingIdentity();
-            int status = mAudioSystem.setPhoneState(mode, uid);
-            Binder.restoreCallingIdentity(identity);
+            try {
+                status = mAudioSystem.setPhoneState(mode, uid);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
             if (status == AudioSystem.AUDIO_STATUS_OK) {
                 if (DEBUG_MODE) {
                     Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode);
@@ -6023,17 +6035,22 @@
         }
 
         final long ident = Binder.clearCallingIdentity();
-        boolean status =
-                mDeviceBroker.setCommunicationDevice(cb, pid, device, eventSource);
-        Binder.restoreCallingIdentity(ident);
-        return status;
+        try {
+            return mDeviceBroker.setCommunicationDevice(cb, pid, device, eventSource);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     /** @see AudioManager#getCommunicationDevice() */
     public int getCommunicationDevice() {
+        AudioDeviceInfo device = null;
         final long ident = Binder.clearCallingIdentity();
-        AudioDeviceInfo device = mDeviceBroker.getCommunicationDevice();
-        Binder.restoreCallingIdentity(ident);
+        try {
+            device = mDeviceBroker.getCommunicationDevice();
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
         if (device == null) {
             return 0;
         }
@@ -6083,8 +6100,11 @@
                         ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
                 .record();
         final long ident = Binder.clearCallingIdentity();
-        mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
-        Binder.restoreCallingIdentity(ident);
+        try {
+            mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     /** @see AudioManager#isSpeakerphoneOn() */
@@ -6224,8 +6244,11 @@
             return;
         }
         final long ident = Binder.clearCallingIdentity();
-        mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
-        Binder.restoreCallingIdentity(ident);
+        try {
+            mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
         mmi.record();
     }
 
@@ -6241,8 +6264,11 @@
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
         final long ident = Binder.clearCallingIdentity();
-        mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
-        Binder.restoreCallingIdentity(ident);
+        try {
+            mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
         new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                 .setUid(uid)
                 .setPid(pid)
@@ -6531,8 +6557,11 @@
                 (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
 
         final long ident = Binder.clearCallingIdentity();
-        IsInCall = telecomManager.isInCall();
-        Binder.restoreCallingIdentity(ident);
+        try {
+            IsInCall = telecomManager.isInCall();
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
 
         int mode = mMode.get();
         return (IsInCall
@@ -6672,10 +6701,13 @@
     private void queueMsgUnderWakeLock(Handler handler, int msg,
             int arg1, int arg2, Object obj, int delay) {
         final long ident = Binder.clearCallingIdentity();
-        // Always acquire the wake lock as AudioService because it is released by the
-        // message handler.
-        mAudioEventWakeLock.acquire();
-        Binder.restoreCallingIdentity(ident);
+        try {
+            // Always acquire the wake lock as AudioService because it is released by the
+            // message handler.
+            mAudioEventWakeLock.acquire();
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
         sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
     }
 
@@ -10075,6 +10107,55 @@
                 "com.android.server.audio", "AudioService");
     }
 
+    @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public float getRs2Value() {
+        super.getRs2Value_enforcePermission();
+        return mSoundDoseHelper.getRs2Value();
+    }
+
+    @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public void setRs2Value(float rs2Value) {
+        super.setRs2Value_enforcePermission();
+        mSoundDoseHelper.setRs2Value(rs2Value);
+    }
+
+    @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public float getCsd() {
+        super.getCsd_enforcePermission();
+        return mSoundDoseHelper.getCsd();
+    }
+
+    @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public void setCsd(float csd) {
+        super.setCsd_enforcePermission();
+        mSoundDoseHelper.setCsd(csd);
+    }
+
+    @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public void forceUseFrameworkMel(boolean useFrameworkMel) {
+        super.forceUseFrameworkMel_enforcePermission();
+        mSoundDoseHelper.forceUseFrameworkMel(useFrameworkMel);
+    }
+
+    @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
+        super.forceComputeCsdOnAllDevices_enforcePermission();
+        mSoundDoseHelper.forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
+    }
+
+    @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+    public boolean isCsdEnabled() {
+        super.isCsdEnabled_enforcePermission();
+        return mSoundDoseHelper.isCsdEnabled();
+    }
+
     //==========================================================================================
     // Hdmi CEC:
     // - System audio mode:
@@ -10426,7 +10507,6 @@
                         + " FromRestrictions=" + mMicMuteFromRestrictions
                         + " FromApi=" + mMicMuteFromApi
                         + " from system=" + mMicMuteFromSystemCached);
-        pw.print("  mCurrentImeUid="); pw.println(mCurrentImeUid);
         dumpAccessibilityServiceUids(pw);
         dumpAssistantServicesUids(pw);
 
@@ -10561,6 +10641,15 @@
     }
 
     @Override
+    @Nullable
+    public IVolumeController getVolumeController() {
+        enforceVolumeController("get the volume controller");
+        if (DEBUG_VOL) Log.d(TAG, "Volume controller: " + mVolumeController);
+
+        return mVolumeController.getController();
+    }
+
+    @Override
     public void notifyVolumeControllerVisible(final IVolumeController controller, boolean visible) {
         enforceVolumeController("notify about volume controller visibility");
 
@@ -10605,6 +10694,10 @@
             mVisible = false;
         }
 
+        public IVolumeController getController() {
+            return mController;
+        }
+
         public void loadSettings(ContentResolver cr) {
             mLongPressTimeout = mSettings.getSecureIntForUser(cr,
                     Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
@@ -11694,13 +11787,16 @@
         int callingUid = Binder.getCallingUid();
         int flags = AudioAttributes.capturePolicyToFlags(capturePolicy, 0x0);
         final long identity = Binder.clearCallingIdentity();
-        synchronized (mPlaybackMonitor) {
-            int result = mAudioSystem.setAllowedCapturePolicy(callingUid, flags);
-            if (result == AudioSystem.AUDIO_STATUS_OK) {
-                mPlaybackMonitor.setAllowedCapturePolicy(callingUid, capturePolicy);
+        try {
+            synchronized (mPlaybackMonitor) {
+                int result = mAudioSystem.setAllowedCapturePolicy(callingUid, flags);
+                if (result == AudioSystem.AUDIO_STATUS_OK) {
+                    mPlaybackMonitor.setAllowedCapturePolicy(callingUid, capturePolicy);
+                }
+                return result;
             }
+        } finally {
             Binder.restoreCallingIdentity(identity);
-            return result;
         }
     }
 
@@ -11711,9 +11807,11 @@
     public int getAllowedCapturePolicy() {
         int callingUid = Binder.getCallingUid();
         final long identity = Binder.clearCallingIdentity();
-        int capturePolicy = mPlaybackMonitor.getAllowedCapturePolicy(callingUid);
-        Binder.restoreCallingIdentity(identity);
-        return capturePolicy;
+        try {
+            return mPlaybackMonitor.getAllowedCapturePolicy(callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     //======================
@@ -11855,8 +11953,11 @@
                 }
             }
             final long identity = Binder.clearCallingIdentity();
-            mAudioSystem.registerPolicyMixes(mMixes, false);
-            Binder.restoreCallingIdentity(identity);
+            try {
+                mAudioSystem.registerPolicyMixes(mMixes, false);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
             synchronized (mAudioPolicies) {
                 mAudioPolicies.remove(mPolicyCallback.asBinder());
             }
@@ -11915,9 +12016,12 @@
 
         @AudioSystem.AudioSystemError int connectMixes() {
             final long identity = Binder.clearCallingIdentity();
-            int status = mAudioSystem.registerPolicyMixes(mMixes, true);
-            Binder.restoreCallingIdentity(identity);
-            return status;
+            try {
+                return mAudioSystem.registerPolicyMixes(mMixes, true);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+
         }
 
         int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 919b850..bc61b37 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -53,6 +53,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -70,9 +71,6 @@
     /*package*/ static final String ACTION_CHECK_MUSIC_ACTIVE =
             "com.android.server.audio.action.CHECK_MUSIC_ACTIVE";
 
-    /** Flag to enable/disable the sound dose computation. */
-    private static final boolean USE_CSD_FOR_SAFE_HEARING = false;
-
     // mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
     // It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
     // or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
@@ -96,8 +94,6 @@
     private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000;  // 1 minute polling interval
     private static final int REQUEST_CODE_CHECK_MUSIC_ACTIVE = 1;
 
-    private static final float CUSTOM_RS2_VALUE = 90;
-
     // timeouts for the CSD warnings, -1 means no timeout (dialog must be ack'd by user)
     private static final int CSD_WARNING_TIMEOUT_MS_DOSE_1X = 7000;
     private static final int CSD_WARNING_TIMEOUT_MS_DOSE_5X = 5000;
@@ -233,6 +229,94 @@
         initCsd();
     }
 
+    float getRs2Value() {
+        if (!mEnableCsd) {
+            return 0.f;
+        }
+
+        Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+        try {
+            return mSoundDose.getOutputRs2();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while getting the RS2 exposure value", e);
+            return 0.f;
+        }
+    }
+
+    void setRs2Value(float rs2Value) {
+        if (!mEnableCsd) {
+            return;
+        }
+
+        Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+        try {
+            mSoundDose.setOutputRs2(rs2Value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while setting the RS2 exposure value", e);
+        }
+    }
+
+    float getCsd() {
+        if (!mEnableCsd) {
+            return -1.f;
+        }
+
+        Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+        try {
+            return mSoundDose.getCsd();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while getting the CSD value", e);
+            return -1.f;
+        }
+    }
+
+    void setCsd(float csd) {
+        if (!mEnableCsd) {
+            return;
+        }
+
+        Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+        try {
+            final SoundDoseRecord record = new SoundDoseRecord();
+            record.timestamp = System.currentTimeMillis();
+            record.value = csd;
+            final SoundDoseRecord[] recordArray = new SoundDoseRecord[] { record };
+            mSoundDose.resetCsd(csd, recordArray);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while setting the CSD value", e);
+        }
+    }
+
+    void forceUseFrameworkMel(boolean useFrameworkMel) {
+        if (!mEnableCsd) {
+            return;
+        }
+
+        Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+        try {
+            mSoundDose.forceUseFrameworkMel(useFrameworkMel);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while forcing the internal MEL computation", e);
+        }
+    }
+
+    void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
+        if (!mEnableCsd) {
+            return;
+        }
+
+        Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized");
+        try {
+            mSoundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception while forcing CSD computation on all devices", e);
+        }
+    }
+
+    boolean isCsdEnabled() {
+        return mEnableCsd;
+    }
+
     /*package*/ int safeMediaVolumeIndex(int device) {
         if (!mSafeMediaVolumeDevices.contains(device)) {
             return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
@@ -293,7 +377,7 @@
                     || (AudioService.mStreamVolumeAlias[streamType] != AudioSystem.STREAM_MUSIC)
                     || (!mSafeMediaVolumeDevices.contains(device))
                     || (index <= safeMediaVolumeIndex(device))
-                    || USE_CSD_FOR_SAFE_HEARING;
+                    || mEnableCsd;
     }
 
     /*package*/ boolean willDisplayWarningAfterCheckVolume(int streamType, int index, int device,
@@ -329,7 +413,7 @@
     /*package*/ void scheduleMusicActiveCheck() {
         synchronized (mSafeMediaVolumeStateLock) {
             cancelMusicActiveCheck();
-            if (!USE_CSD_FOR_SAFE_HEARING) {
+            if (!mEnableCsd) {
                 mMusicActiveIntent = PendingIntent.getBroadcast(mContext,
                         REQUEST_CODE_CHECK_MUSIC_ACTIVE,
                         new Intent(ACTION_CHECK_MUSIC_ACTIVE),
@@ -460,14 +544,13 @@
 
     private void initCsd() {
         synchronized (mSafeMediaVolumeStateLock) {
-            if (USE_CSD_FOR_SAFE_HEARING) {
+            if (mEnableCsd) {
                 Log.v(TAG, "Initializing sound dose");
 
                 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
                 mSoundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback);
                 try {
                     if (mSoundDose != null && mSoundDose.asBinder().isBinderAlive()) {
-                        mSoundDose.setOutputRs2(CUSTOM_RS2_VALUE);
                         if (mCurrentCsd != 0.f) {
                             Log.d(TAG, "Resetting the saved sound dose value " + mCurrentCsd);
                             SoundDoseRecord[] records = mDoseRecords.toArray(
@@ -502,7 +585,7 @@
                 // The persisted state is either "disabled" or "active": this is the state applied
                 // next time we boot and cannot be "inactive"
                 int persistedState;
-                if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !USE_CSD_FOR_SAFE_HEARING) {
+                if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !mEnableCsd) {
                     persistedState = SAFE_MEDIA_VOLUME_ACTIVE;
                     // The state can already be "inactive" here if the user has forced it before
                     // the 30 seconds timeout for forced configuration. In this case we don't reset
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 06b99f8..d558e69 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -109,7 +109,6 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.IntArray;
@@ -261,13 +260,6 @@
     final SparseArray<Pair<IVirtualDevice, DisplayWindowPolicyController>>
             mDisplayWindowPolicyControllers = new SparseArray<>();
 
-    /**
-     *  Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
-     *  {@link DisplayDevice#mUniqueId}.
-     */
-    public final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
-            new ArrayMap<>();
-
     // List of all currently registered display adapters.
     private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
 
@@ -1648,16 +1640,7 @@
 
         DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
-            final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
-            if (device == null) {
-                Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
-                        + display.getDisplayIdLocked());
-                return;
-            }
-
-            final String uniqueId = device.getUniqueId();
-            HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
-            dpc.onDisplayChanged(hbmMetadata);
+            dpc.onDisplayChanged();
         }
     }
 
@@ -1715,15 +1698,7 @@
         final int displayId = display.getDisplayIdLocked();
         final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
-            final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
-            if (device == null) {
-                Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
-                        + display.getDisplayIdLocked());
-                return;
-            }
-            final String uniqueId = device.getUniqueId();
-            HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
-            dpc.onDisplayChanged(hbmMetadata);
+            dpc.onDisplayChanged();
         }
     }
 
@@ -2676,31 +2651,6 @@
         mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
     }
 
-    private HighBrightnessModeMetadata getHighBrightnessModeMetadata(LogicalDisplay display) {
-        final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
-        if (device == null) {
-            Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
-                    + display.getDisplayIdLocked());
-            return null;
-        }
-
-        // HBM brightness mode is only applicable to internal physical displays.
-        if (display.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
-            return null;
-        }
-
-        final String uniqueId = device.getUniqueId();
-
-        if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
-            return mHighBrightnessModeMetadataMap.get(uniqueId);
-        }
-
-        // HBM Time info not present. Create a new one for this physical display.
-        HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
-        mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
-        return hbmInfo;
-    }
-
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
     private void addDisplayPowerControllerLocked(LogicalDisplay display) {
         if (mPowerHandler == null) {
@@ -2716,23 +2666,17 @@
                 display, mSyncRoot);
         final DisplayPowerControllerInterface displayPowerController;
 
-        // If display is internal and has a HighBrightnessModeMetadata mapping, use that.
-        // Or create a new one and use that.
-        // We also need to pass a mapping of the HighBrightnessModeTimeInfoMap to
-        // displayPowerController, so the hbm info can be correctly associated
-        // with the corresponding displaydevice.
-        HighBrightnessModeMetadata hbmMetadata = getHighBrightnessModeMetadata(display);
         if (DeviceConfig.getBoolean("display_manager",
                 "use_newly_structured_display_power_controller", true)) {
             displayPowerController = new DisplayPowerController2(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata);
+                    () -> handleBrightnessChange(display));
         } else {
             displayPowerController = new DisplayPowerController(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata);
+                    () -> handleBrightnessChange(display));
         }
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 142ec68..cdaa3d0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -388,7 +388,6 @@
     private float[] mNitsRange;
 
     private final HighBrightnessModeController mHbmController;
-    private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
 
     private final BrightnessThrottler mBrightnessThrottler;
 
@@ -506,14 +505,13 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+            Runnable onBrightnessChangeRunnable) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
         mLogicalDisplay = logicalDisplay;
         mDisplayId = mLogicalDisplay.getDisplayIdLocked();
         mTag = "DisplayPowerController[" + mDisplayId + "]";
-        mHighBrightnessModeMetadata = hbmMetadata;
         mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
         mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
         mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
@@ -792,7 +790,7 @@
      * Make sure DisplayManagerService.mSyncRoot is held when this is called
      */
     @Override
-    public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
+    public void onDisplayChanged() {
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         if (device == null) {
             Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
@@ -814,7 +812,7 @@
                 mUniqueDisplayId = uniqueId;
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
-                loadFromDisplayDeviceConfig(token, info, hbmMetadata);
+                loadFromDisplayDeviceConfig(token, info);
 
                 /// Since the underlying display-device changed, we really don't know the
                 // last command that was sent to change it's state. Lets assume it is unknown so
@@ -866,8 +864,7 @@
         }
     }
 
-    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
-                                             HighBrightnessModeMetadata hbmMetadata) {
+    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
         // All properties that depend on the associated DisplayDevice and the DDC must be
         // updated here.
         loadBrightnessRampRates();
@@ -880,7 +877,6 @@
                     mBrightnessRampIncreaseMaxTimeMillis,
                     mBrightnessRampDecreaseMaxTimeMillis);
         }
-        mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
         mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                 mDisplayDeviceConfig.getHighBrightnessModeData(),
                 new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1965,7 +1961,7 @@
                     if (mAutomaticBrightnessController != null) {
                         mAutomaticBrightnessController.update();
                     }
-                }, mHighBrightnessModeMetadata, mContext);
+                }, mContext);
     }
 
     private BrightnessThrottler createBrightnessThrottlerLocked() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 96342f3..da59cca 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -328,7 +328,6 @@
     private float[] mNitsRange;
 
     private final HighBrightnessModeController mHbmController;
-    private final HighBrightnessModeMetadata mHighBrightnessModeMetadata;
 
     private final BrightnessThrottler mBrightnessThrottler;
 
@@ -433,7 +432,7 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+            Runnable onBrightnessChangeRunnable) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -449,7 +448,6 @@
         mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
                 mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
                 () -> updatePowerState(), mDisplayId, mSensorManager);
-        mHighBrightnessModeMetadata = hbmMetadata;
         mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController);
         mTag = "DisplayPowerController2[" + mDisplayId + "]";
 
@@ -709,7 +707,7 @@
      * Make sure DisplayManagerService.mSyncRoot lock is held when this is called
      */
     @Override
-    public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata) {
+    public void onDisplayChanged() {
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         if (device == null) {
             Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
@@ -723,7 +721,6 @@
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
         final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
-
         mHandler.post(() -> {
             boolean changed = false;
             if (mDisplayDevice != device) {
@@ -732,7 +729,7 @@
                 mUniqueDisplayId = uniqueId;
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
-                loadFromDisplayDeviceConfig(token, info, hbmMetadata);
+                loadFromDisplayDeviceConfig(token, info);
                 mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
 
                 // Since the underlying display-device changed, we really don't know the
@@ -781,8 +778,7 @@
         }
     }
 
-    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
-                                             HighBrightnessModeMetadata hbmMetadata) {
+    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
         // All properties that depend on the associated DisplayDevice and the DDC must be
         // updated here.
         loadBrightnessRampRates();
@@ -794,7 +790,6 @@
                     mBrightnessRampIncreaseMaxTimeMillis,
                     mBrightnessRampDecreaseMaxTimeMillis);
         }
-        mHbmController.setHighBrightnessModeMetadata(hbmMetadata);
         mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
                 mDisplayDeviceConfig.getHighBrightnessModeData(),
                 new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
@@ -1747,7 +1742,7 @@
                     if (mAutomaticBrightnessController != null) {
                         mAutomaticBrightnessController.update();
                     }
-                }, mHighBrightnessModeMetadata, mContext);
+                }, mContext);
     }
 
     private BrightnessThrottler createBrightnessThrottlerLocked() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 7b019846..e750ee2 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -31,14 +31,10 @@
 public interface DisplayPowerControllerInterface {
 
     /**
-     * Notified when the display is changed.
-     * We use this to apply any changes that might be needed
+     * Notified when the display is changed. We use this to apply any changes that might be needed
      * when displays get swapped on foldable devices.
-     * We also pass the High brightness mode metadata like
-     * remaining time and hbm events for the corresponding
-     * physical display, to update the values correctly.
      */
-    void onDisplayChanged(HighBrightnessModeMetadata hbmInfo);
+    void onDisplayChanged();
 
     /**
      * Unregisters all listeners and interrupts all running threads; halting future work.
diff --git a/services/core/java/com/android/server/display/HbmEvent.java b/services/core/java/com/android/server/display/HbmEvent.java
deleted file mode 100644
index 5675e2f..0000000
--- a/services/core/java/com/android/server/display/HbmEvent.java
+++ /dev/null
@@ -1,46 +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.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;
-
-
-/**
- * Represents an event in which High Brightness Mode was enabled.
- */
-class HbmEvent {
-    private long mStartTimeMillis;
-    private long mEndTimeMillis;
-
-    HbmEvent(long startTimeMillis, long endTimeMillis) {
-        this.mStartTimeMillis = startTimeMillis;
-        this.mEndTimeMillis = endTimeMillis;
-    }
-
-    public long getStartTimeMillis() {
-        return mStartTimeMillis;
-    }
-
-    public long getEndTimeMillis() {
-        return mEndTimeMillis;
-    }
-
-    @Override
-    public String toString() {
-        return "HbmEvent: {startTimeMillis:" + mStartTimeMillis + ", endTimeMillis: "
-                + mEndTimeMillis + "}, total: "
-                + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
-    }
-}
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 2c843a4..f98c7df 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -105,23 +105,30 @@
     private int mHbmStatsState = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
 
     /**
-     * If HBM is currently running, this is the start time and set of all events,
-     * for the current HBM session.
+     * If HBM is currently running, this is the start time for the current HBM session.
      */
-    private HighBrightnessModeMetadata mHighBrightnessModeMetadata = null;
+    private long mRunningStartTimeMillis = -1;
+
+    /**
+     * Queue of previous HBM-events ordered from most recent to least recent.
+     * Meant to store only the events that fall into the most recent
+     * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
+     */
+    private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
+
     HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
             String displayUniqueId, float brightnessMin, float brightnessMax,
             HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
-            Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
+            Runnable hbmChangeCallback, Context context) {
         this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
-            brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, hbmMetadata, context);
+            brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, context);
     }
 
     @VisibleForTesting
     HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
             IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
             HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
-            Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, Context context) {
+            Runnable hbmChangeCallback, Context context) {
         mInjector = injector;
         mContext = context;
         mClock = injector.getClock();
@@ -130,7 +137,6 @@
         mBrightnessMin = brightnessMin;
         mBrightnessMax = brightnessMax;
         mHbmChangeCallback = hbmChangeCallback;
-        mHighBrightnessModeMetadata = hbmMetadata;
         mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
         mSettingsObserver = new SettingsObserver(mHandler);
         mRecalcRunnable = this::recalculateTimeAllowance;
@@ -216,22 +222,19 @@
 
         // If we are starting or ending a high brightness mode session, store the current
         // session in mRunningStartTimeMillis, or the old one in mEvents.
-        final long runningStartTime = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
-        final boolean wasHbmDrainingAvailableTime = runningStartTime != -1;
+        final boolean wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1;
         final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint
                 && !mIsHdrLayerPresent;
         if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) {
             final long currentTime = mClock.uptimeMillis();
             if (shouldHbmDrainAvailableTime) {
-                mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
+                mRunningStartTimeMillis = currentTime;
             } else {
-                final HbmEvent hbmEvent = new HbmEvent(runningStartTime, currentTime);
-                mHighBrightnessModeMetadata.addHbmEvent(hbmEvent);
-                mHighBrightnessModeMetadata.setRunningStartTimeMillis(-1);
+                mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
+                mRunningStartTimeMillis = -1;
 
                 if (DEBUG) {
-                    Slog.d(TAG, "New HBM event: "
-                            + mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst());
+                    Slog.d(TAG, "New HBM event: " + mEvents.peekFirst());
                 }
             }
         }
@@ -257,10 +260,6 @@
         mSettingsObserver.stopObserving();
     }
 
-    void setHighBrightnessModeMetadata(HighBrightnessModeMetadata hbmInfo) {
-        mHighBrightnessModeMetadata = hbmInfo;
-    }
-
     void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
             HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg) {
         mWidth = width;
@@ -317,22 +316,20 @@
         pw.println("  mBrightnessMax=" + mBrightnessMax);
         pw.println("  remainingTime=" + calculateRemainingTime(mClock.uptimeMillis()));
         pw.println("  mIsTimeAvailable= " + mIsTimeAvailable);
-        pw.println("  mRunningStartTimeMillis="
-                + TimeUtils.formatUptime(mHighBrightnessModeMetadata.getRunningStartTimeMillis()));
+        pw.println("  mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
         pw.println("  mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
         pw.println("  mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
         pw.println("  width*height=" + mWidth + "*" + mHeight);
         pw.println("  mEvents=");
         final long currentTime = mClock.uptimeMillis();
         long lastStartTime = currentTime;
-        long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
-        if (runningStartTimeMillis != -1) {
-            lastStartTime = dumpHbmEvent(pw, new HbmEvent(runningStartTimeMillis, currentTime));
+        if (mRunningStartTimeMillis != -1) {
+            lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime));
         }
-        for (HbmEvent event : mHighBrightnessModeMetadata.getHbmEventQueue()) {
-            if (lastStartTime > event.getEndTimeMillis()) {
+        for (HbmEvent event : mEvents) {
+            if (lastStartTime > event.endTimeMillis) {
                 pw.println("    event: [normal brightness]: "
-                        + TimeUtils.formatDuration(lastStartTime - event.getEndTimeMillis()));
+                        + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis));
             }
             lastStartTime = dumpHbmEvent(pw, event);
         }
@@ -341,12 +338,12 @@
     }
 
     private long dumpHbmEvent(PrintWriter pw, HbmEvent event) {
-        final long duration = event.getEndTimeMillis() - event.getStartTimeMillis();
+        final long duration = event.endTimeMillis - event.startTimeMillis;
         pw.println("    event: ["
-                + TimeUtils.formatUptime(event.getStartTimeMillis()) + ", "
-                + TimeUtils.formatUptime(event.getEndTimeMillis()) + "] ("
+                + TimeUtils.formatUptime(event.startTimeMillis) + ", "
+                + TimeUtils.formatUptime(event.endTimeMillis) + "] ("
                 + TimeUtils.formatDuration(duration) + ")");
-        return event.getStartTimeMillis();
+        return event.startTimeMillis;
     }
 
     private boolean isCurrentlyAllowed() {
@@ -375,15 +372,13 @@
 
         // First, lets see how much time we've taken for any currently running
         // session of HBM.
-        long runningStartTimeMillis = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
-        if (runningStartTimeMillis > 0) {
-            if (runningStartTimeMillis > currentTime) {
+        if (mRunningStartTimeMillis > 0) {
+            if (mRunningStartTimeMillis > currentTime) {
                 Slog.e(TAG, "Start time set to the future. curr: " + currentTime
-                        + ", start: " + runningStartTimeMillis);
-                mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
-                runningStartTimeMillis = currentTime;
+                        + ", start: " + mRunningStartTimeMillis);
+                mRunningStartTimeMillis = currentTime;
             }
-            timeAlreadyUsed = currentTime - runningStartTimeMillis;
+            timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
         }
 
         if (DEBUG) {
@@ -392,19 +387,18 @@
 
         // Next, lets iterate through the history of previous sessions and add those times.
         final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
-        Iterator<HbmEvent> it = mHighBrightnessModeMetadata.getHbmEventQueue().iterator();
+        Iterator<HbmEvent> it = mEvents.iterator();
         while (it.hasNext()) {
             final HbmEvent event = it.next();
 
             // If this event ended before the current Timing window, discard forever and ever.
-            if (event.getEndTimeMillis() < windowstartTimeMillis) {
+            if (event.endTimeMillis < windowstartTimeMillis) {
                 it.remove();
                 continue;
             }
 
-            final long startTimeMillis = Math.max(event.getStartTimeMillis(),
-                            windowstartTimeMillis);
-            timeAlreadyUsed += event.getEndTimeMillis() - startTimeMillis;
+            final long startTimeMillis = Math.max(event.startTimeMillis, windowstartTimeMillis);
+            timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
         }
 
         if (DEBUG) {
@@ -431,18 +425,17 @@
         // Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or
         // brightness change doesn't happen before then.
         long nextTimeout = -1;
-        final ArrayDeque<HbmEvent> hbmEvents = mHighBrightnessModeMetadata.getHbmEventQueue();
         if (mBrightness > mHbmData.transitionPoint) {
             // if we're in high-lux now, timeout when we run out of allowed time.
             nextTimeout = currentTime + remainingTime;
-        } else if (!mIsTimeAvailable && hbmEvents.size() > 0) {
+        } else if (!mIsTimeAvailable && mEvents.size() > 0) {
             // If we are not allowed...timeout when the oldest event moved outside of the timing
             // window by at least minTime. Basically, we're calculating the soonest time we can
             // get {@code timeMinMillis} back to us.
             final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
-            final HbmEvent lastEvent = hbmEvents.peekLast();
+            final HbmEvent lastEvent = mEvents.peekLast();
             final long startTimePlusMinMillis =
-                    Math.max(windowstartTimeMillis, lastEvent.getStartTimeMillis())
+                    Math.max(windowstartTimeMillis, lastEvent.startTimeMillis)
                     + mHbmData.timeMinMillis;
             final long timeWhenMinIsGainedBack =
                     currentTime + (startTimePlusMinMillis - windowstartTimeMillis) - remainingTime;
@@ -466,10 +459,9 @@
                     + ", mUnthrottledBrightness: " + mUnthrottledBrightness
                     + ", mThrottlingReason: "
                         + BrightnessInfo.briMaxReasonToString(mThrottlingReason)
-                    + ", RunningStartTimeMillis: "
-                        + mHighBrightnessModeMetadata.getRunningStartTimeMillis()
+                    + ", RunningStartTimeMillis: " + mRunningStartTimeMillis
                     + ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
-                    + ", events: " + hbmEvents);
+                    + ", events: " + mEvents);
         }
 
         if (nextTimeout != -1) {
@@ -596,6 +588,25 @@
         }
     }
 
+    /**
+     * Represents an event in which High Brightness Mode was enabled.
+     */
+    private static class HbmEvent {
+        public long startTimeMillis;
+        public long endTimeMillis;
+
+        HbmEvent(long startTimeMillis, long endTimeMillis) {
+            this.startTimeMillis = startTimeMillis;
+            this.endTimeMillis = endTimeMillis;
+        }
+
+        @Override
+        public String toString() {
+            return "[Event: {" + startTimeMillis + ", " + endTimeMillis + "}, total: "
+                    + ((endTimeMillis - startTimeMillis) / 1000) + "]";
+        }
+    }
+
     @VisibleForTesting
     class HdrListener extends SurfaceControlHdrLayerInfoListener {
         @Override
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java b/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
deleted file mode 100644
index 37234ff..0000000
--- a/services/core/java/com/android/server/display/HighBrightnessModeMetadata.java
+++ /dev/null
@@ -1,58 +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.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import java.util.ArrayDeque;
-
-
-/**
- * Represents High Brightness Mode metadata associated
- * with a specific internal physical display.
- * Required for separately storing data like time information,
- * and related events when display was in HBM mode per
- * physical internal display.
- */
-class HighBrightnessModeMetadata {
-    /**
-     * Queue of previous HBM-events ordered from most recent to least recent.
-     * Meant to store only the events that fall into the most recent
-     * {@link HighBrightnessModeData#timeWindowMillis mHbmData.timeWindowMillis}.
-     */
-    private final ArrayDeque<HbmEvent> mEvents = new ArrayDeque<>();
-
-    /**
-     * If HBM is currently running, this is the start time for the current HBM session.
-     */
-    private long mRunningStartTimeMillis = -1;
-
-    public long getRunningStartTimeMillis() {
-        return mRunningStartTimeMillis;
-    }
-
-    public void setRunningStartTimeMillis(long setTime) {
-        mRunningStartTimeMillis = setTime;
-    }
-
-    public ArrayDeque<HbmEvent> getHbmEventQueue() {
-        return mEvents;
-    }
-
-    public void addHbmEvent(HbmEvent hbmEvent) {
-        mEvents.addFirst(hbmEvent);
-    }
-}
-
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index 60167b4..da65f27 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -59,13 +59,16 @@
     private static final long TIMEOUT_MS = 10_000;
 
     /** Handler for registering timeouts for live entries. */
-    private final Handler mHandler =
-            new Handler(Looper.myLooper(), null /* callback */, true /* async */);
+    private final Handler mHandler;
 
     /** Singleton instance of the History. */
     @GuardedBy("ImeTrackerService.this")
     private final History mHistory = new History();
 
+    ImeTrackerService(@NonNull Looper looper) {
+        mHandler = new Handler(looper, null /* callback */, true /* async */);
+    }
+
     @NonNull
     @Override
     public synchronized IBinder onRequestShow(int uid, @ImeTracker.Origin int origin,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a63b95c..2ced988 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -89,6 +89,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.LocaleList;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
 import android.os.Process;
@@ -1105,7 +1106,7 @@
             new SoftInputShowHideHistory();
 
     @NonNull
-    private final ImeTrackerService mImeTrackerService = new ImeTrackerService();
+    private final ImeTrackerService mImeTrackerService;
 
     class SettingsObserver extends ContentObserver {
         int mUserId;
@@ -1662,6 +1663,8 @@
                                 true /* allowIo */);
         thread.start();
         mHandler = Handler.createAsync(thread.getLooper(), this);
+        mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
+                ? serviceThreadForTesting.getLooper() : Looper.getMainLooper());
         // Note: SettingsObserver doesn't register observers in its constructor.
         mSettingsObserver = new SettingsObserver(mHandler);
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
@@ -4613,7 +4616,9 @@
                 info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName,
                 info.imeSurfaceParentName));
 
-        mImeTrackerService.onImmsUpdate(statsToken.mBinder, info.requestWindowName);
+        if (statsToken != null) {
+            mImeTrackerService.onImmsUpdate(statsToken.mBinder, info.requestWindowName);
+        }
     }
 
     @BinderThread
@@ -4655,13 +4660,10 @@
     }
 
     @VisibleForTesting
-    ImeVisibilityStateComputer getVisibilityStateComputer() {
-        return mVisibilityStateComputer;
-    }
-
-    @VisibleForTesting
     ImeVisibilityApplier getVisibilityApplier() {
-        return mVisibilityApplier;
+        synchronized (ImfLock.class) {
+            return mVisibilityApplier;
+        }
     }
 
     @GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/inputmethod/TEST_MAPPING b/services/core/java/com/android/server/inputmethod/TEST_MAPPING
index 0ccd75d..bd95c5b 100644
--- a/services/core/java/com/android/server/inputmethod/TEST_MAPPING
+++ b/services/core/java/com/android/server/inputmethod/TEST_MAPPING
@@ -2,6 +2,9 @@
  "imports": [
     {
       "path": "frameworks/base/core/java/android/view/inputmethod"
+    },
+    {
+      "path": "frameworks/base/services/tests/InputMethodSystemServerTests"
     }
   ]
 }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 7f6c2d6..8f65775 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -1155,6 +1155,34 @@
         return nanoappIds;
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+    /**
+     * Puts the context hub in and out of test mode. Test mode is a clean state
+     * where tests can be executed in the same environment. If enable is true,
+     * this will enable test mode by unloading all nanoapps. If enable is false,
+     * this will disable test mode and reverse the actions of enabling test mode
+     * by loading all preloaded nanoapps. This puts CHRE in a normal state.
+     *
+     * This should only be used for a test environment, either through a
+     * @TestApi or development tools. This should not be used in a production
+     * environment.
+     *
+     * @param enable If true, put the context hub in test mode. If false, disable
+     *               test mode.
+     * @return       If true, the operation was successful; false otherwise.
+     */
+    @Override
+    public boolean setTestMode(boolean enable) {
+        super.setTestMode_enforcePermission();
+        boolean status = mContextHubWrapper.setTestMode(enable);
+
+        // Query nanoapps to update service state after test mode state change.
+        for (int contextHubId: mDefaultClientMap.keySet()) {
+            queryNanoAppsInternal(contextHubId);
+        }
+        return status;
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 3581b02..ca184ee 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -382,6 +382,23 @@
      */
     public abstract void registerExistingCallback(int contextHubId) throws RemoteException;
 
+    /**
+     * Puts the context hub in and out of test mode. Test mode is a clean state
+     * where tests can be executed in the same environment. If enable is true,
+     * this will enable test mode by unloading all nanoapps. If enable is false,
+     * this will disable test mode and reverse the actions of enabling test mode
+     * by loading all preloaded nanoapps. This puts CHRE in a normal state.
+     *
+     * This should only be used for a test environment, either through a
+     * @TestApi or development tools. This should not be used in a production
+     * environment.
+     *
+     * @param enable If true, put the context hub in test mode. If false, disable
+     *               test mode.
+     * @return       If true, the operation was successful; false otherwise.
+     */
+    public abstract boolean setTestMode(boolean enable);
+
     private static class ContextHubWrapperAidl extends IContextHubWrapper
             implements IBinder.DeathRecipient {
         private android.hardware.contexthub.IContextHub mHub;
@@ -741,6 +758,22 @@
             registerExistingCallback(contextHubId);
         }
 
+        public boolean setTestMode(boolean enable) {
+            android.hardware.contexthub.IContextHub hub = getHub();
+            if (hub == null) {
+                return false;
+            }
+
+            try {
+                hub.setTestMode(enable);
+                return true;
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Exception while setting test mode (enable: "
+                        + (enable ? "true" : "false") + "): " + e.getMessage());
+                return false;
+            }
+        }
+
         private void onSettingChanged(byte setting, boolean enabled) {
             android.hardware.contexthub.IContextHub hub = getHub();
             if (hub == null) {
@@ -911,6 +944,10 @@
             mHub.registerCallback(contextHubId, callback);
         }
 
+        public boolean setTestMode(boolean enable) {
+            return false;
+        }
+
         public boolean supportsBtSettingNotifications() {
             return false;
         }
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 56390a9..15cfca5 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -274,20 +274,16 @@
             }
         }
 
-        final long endMillis = SystemClock.elapsedRealtime() + SERVICE_CONNECT_TIMEOUT_MS;
-        while (SystemClock.elapsedRealtime() <= endMillis) {
+        final long endMillis = SystemClock.uptimeMillis() + SERVICE_CONNECT_TIMEOUT_MS;
+        do {
             final IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
             if (binder != null) {
                 binder.linkToDeath(
                         () -> Slog.w(TAG, String.format("service '%s' died", IDMAP_SERVICE)), 0);
                 return binder;
             }
-
-            try {
-                Thread.sleep(SERVICE_CONNECT_INTERVAL_SLEEP_MS);
-            } catch (InterruptedException ignored) {
-            }
-        }
+            SystemClock.sleep(SERVICE_CONNECT_INTERVAL_SLEEP_MS);
+        } while (SystemClock.uptimeMillis() <= endMillis);
 
         throw new TimeoutException(
             String.format("Failed to connect to '%s' in %d milliseconds", IDMAP_SERVICE,
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 5c4c7c9..faa06f7 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -331,6 +331,7 @@
     /** Adds listener for package update */
     public void addPackagesUpdatedListener(PackagesUpdatedListener listener)
             throws LegacyDexoptDisabledException {
+        // TODO(b/251903639): Evaluate whether this needs to support ART Service or not.
         Installer.checkLegacyDexoptDisabled();
         synchronized (mLock) {
             mPackagesUpdatedListeners.add(listener);
@@ -629,6 +630,8 @@
 
     /** Gets the size of a package. */
     private long getPackageSize(@NonNull Computer snapshot, String pkg) {
+        // TODO(b/251903639): Make this in line with the calculation in
+        // `DexOptHelper.DexoptDoneHandler`.
         PackageInfo info = snapshot.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
         long size = 0;
         if (info != null && info.applicationInfo != null) {
@@ -723,6 +726,8 @@
             return optimizePackages(pkgs, lowStorageThreshold, updatedPackages, isPostBootUpdate);
         } finally {
             // Always let the pinner service know about changes.
+            // TODO(b/251903639): ART Service does this for all dexopts, while the code below only
+            // runs for background jobs. We should try to make them behave the same.
             notifyPinService(updatedPackages);
             // Only notify IORap the primary dex opt, because we don't want to
             // invalidate traces unnecessary due to b/161633001 and that it's
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 382ae26..de37080 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -57,8 +57,11 @@
 import android.util.Slog;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalManagerRegistry;
+import com.android.server.LocalServices;
+import com.android.server.PinnerService;
 import com.android.server.art.ArtManagerLocal;
 import com.android.server.art.DexUseManagerLocal;
 import com.android.server.art.ReasonMapping;
@@ -970,6 +973,35 @@
                 mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
                 mPm.mCompilerStats.maybeWriteAsync();
             }
+
+            if (result.getReason().equals(ReasonMapping.REASON_INACTIVE)) {
+                for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
+                    if (pkgRes.getStatus() == DexoptResult.DEXOPT_PERFORMED) {
+                        long pkgSizeBytes = 0;
+                        long pkgSizeBeforeBytes = 0;
+                        for (DexoptResult.DexContainerFileDexoptResult dexRes :
+                                pkgRes.getDexContainerFileDexoptResults()) {
+                            long dexContainerSize = new File(dexRes.getDexContainerFile()).length();
+                            pkgSizeBytes += dexRes.getSizeBytes() + dexContainerSize;
+                            pkgSizeBeforeBytes += dexRes.getSizeBeforeBytes() + dexContainerSize;
+                        }
+                        FrameworkStatsLog.write(FrameworkStatsLog.APP_DOWNGRADED,
+                                pkgRes.getPackageName(), pkgSizeBeforeBytes, pkgSizeBytes,
+                                false /* aggressive */);
+                    }
+                }
+            }
+
+            var updatedPackages = new ArraySet<String>();
+            for (DexoptResult.PackageDexoptResult pkgRes : result.getPackageDexoptResults()) {
+                if (pkgRes.hasUpdatedArtifacts()) {
+                    updatedPackages.add(pkgRes.getPackageName());
+                }
+            }
+            if (!updatedPackages.isEmpty()) {
+                LocalServices.getService(PinnerService.class)
+                        .update(updatedPackages, false /* force */);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9a15e11..94e96b0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2950,15 +2950,6 @@
                     }
                 }
                 if (doTrim) {
-                    if (!isFirstBoot()) {
-                        try {
-                            ActivityManager.getService().showBootMessage(
-                                    mContext.getResources().getString(
-                                            R.string.android_upgrading_fstrim),
-                                    true);
-                        } catch (RemoteException e) {
-                        }
-                    }
                     sm.runMaintenance();
                 }
             } else {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 328fc8b..3340e12 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1481,36 +1481,45 @@
     @Override
     public void setUserAdmin(@UserIdInt int userId) {
         checkManageUserAndAcrossUsersFullPermission("set user admin");
-
+        final long sessionId = logGrantAdminJourneyBegin(userId);
+        UserInfo info;
         synchronized (mPackagesLock) {
-            UserInfo info;
             synchronized (mUsersLock) {
                 info = getUserInfoLU(userId);
             }
             if (info == null || info.isAdmin()) {
                 // Exit if no user found with that id, or the user is already an Admin.
+                logUserJourneyError(sessionId,
+                        FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
+                        userId);
                 return;
             }
-
             info.flags ^= UserInfo.FLAG_ADMIN;
             writeUserLP(getUserDataLU(info.id));
         }
+        logGrantAdminJourneyFinish(sessionId, userId, info.userType, info.flags);
     }
 
     @Override
     public void revokeUserAdmin(@UserIdInt int userId) {
         checkManageUserAndAcrossUsersFullPermission("revoke admin privileges");
+        final long sessionId = logRevokeAdminJourneyBegin(userId);
+        UserData user;
         synchronized (mPackagesLock) {
             synchronized (mUsersLock) {
-                UserData user = getUserDataLU(userId);
+                user = getUserDataLU(userId);
                 if (user == null || !user.info.isAdmin()) {
                     // Exit if no user found with that id, or the user is not an Admin.
+                    logUserJourneyError(sessionId, FrameworkStatsLog
+                                    .USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
+                            userId);
                     return;
                 }
                 user.info.flags ^= UserInfo.FLAG_ADMIN;
                 writeUserLP(user);
             }
         }
+        logRevokeAdminJourneyFinish(sessionId, userId, user.info.userType, user.info.flags);
     }
 
     /**
@@ -5147,12 +5156,38 @@
                 userId, userType, flags, finish);
     }
 
+    private long logGrantAdminJourneyBegin(@UserIdInt int userId) {
+        return logUserJourneyBegin(
+                FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
+                userId);
+    }
+
+    private void logGrantAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType,
+            @UserInfoFlag int flags) {
+        logUserJourneyFinish(sessionId,
+                FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN,
+                userId, userType, flags, true);
+    }
+
+    private long logRevokeAdminJourneyBegin(@UserIdInt int userId) {
+        return logUserJourneyBegin(
+                FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
+                userId);
+    }
+
+    private void logRevokeAdminJourneyFinish(long sessionId, @UserIdInt int userId, String userType,
+            @UserInfoFlag int flags) {
+        logUserJourneyFinish(sessionId,
+                FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN,
+                userId, userType, flags, true);
+    }
+
     private void logUserJourneyFinish(long sessionId, int journey, @UserIdInt int userId,
             String userType, @UserInfoFlag int flags, boolean finish) {
 
         // log the journey atom with the user metadata
         FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
-                journey, /* origin_user= */ -1, userId,
+                journey, /* origin_user= */ getCurrentUserId(), userId,
                 UserManager.getUserTypeForStatsd(userType), flags);
 
         int event;
@@ -5163,6 +5198,12 @@
             case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE:
                 event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER;
                 break;
+            case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
+                event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
+                break;
+            case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
+                event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
+                break;
             default:
                 throw new IllegalArgumentException("Journey " + journey + " not expected.");
         }
@@ -5184,6 +5225,12 @@
             case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE:
                 event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER;
                 break;
+            case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
+                event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
+                break;
+            case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
+                event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
+                break;
             default:
                 throw new IllegalArgumentException("Journey " + journey + " not expected.");
         }
@@ -5193,6 +5240,27 @@
         return sessionId;
     }
 
+    private void logUserJourneyError(long sessionId, int journey, @UserIdInt int userId) {
+
+        // log the journey atom with the user metadata
+        FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
+                journey, /* origin_user= */ getCurrentUserId(), userId);
+
+        int event;
+        switch (journey) {
+            case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__GRANT_ADMIN:
+                event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__GRANT_ADMIN;
+                break;
+            case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN:
+                event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REVOKE_ADMIN;
+                break;
+            default:
+                throw new IllegalArgumentException("Journey " + journey + " not expected.");
+        }
+        FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+                event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__ERROR);
+    }
+
     /** Register callbacks for statsd pulled atoms. */
     private void registerStatsCallbacks() {
         final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 6c616e0..b3e915a 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -170,6 +170,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.IProcessStats;
 import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.app.procstats.StatsEventOutput;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelAllocationStats;
@@ -613,12 +614,19 @@
                         }
                     case FrameworkStatsLog.PROC_STATS:
                         synchronized (mProcStatsLock) {
-                            return pullProcStatsLocked(ProcessStats.REPORT_ALL, atomTag, data);
+                            return pullProcStatsLocked(atomTag, data);
                         }
                     case FrameworkStatsLog.PROC_STATS_PKG_PROC:
                         synchronized (mProcStatsLock) {
-                            return pullProcStatsLocked(ProcessStats.REPORT_PKG_PROC_STATS, atomTag,
-                                    data);
+                            return pullProcStatsLocked(atomTag, data);
+                        }
+                    case FrameworkStatsLog.PROCESS_STATE:
+                        synchronized (mProcStatsLock) {
+                            return pullProcessStateLocked(atomTag, data);
+                        }
+                    case FrameworkStatsLog.PROCESS_ASSOCIATION:
+                        synchronized (mProcStatsLock) {
+                            return pullProcessAssociationLocked(atomTag, data);
                         }
                     case FrameworkStatsLog.DISK_IO:
                         synchronized (mDiskIoLock) {
@@ -891,6 +899,8 @@
         registerNumFacesEnrolled();
         registerProcStats();
         registerProcStatsPkgProc();
+        registerProcessState();
+        registerProcessAssociation();
         registerDiskIO();
         registerPowerProfile();
         registerProcessCpuTime();
@@ -2884,59 +2894,138 @@
         );
     }
 
-    private int pullProcStatsLocked(int section, int atomTag, List<StatsEvent> pulledData) {
+    private void registerProcessState() {
+        int tagId = FrameworkStatsLog.PROCESS_STATE;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl);
+    }
+
+    private void registerProcessAssociation() {
+        int tagId = FrameworkStatsLog.PROCESS_ASSOCIATION;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl);
+    }
+
+    @GuardedBy("mProcStatsLock")
+    private ProcessStats getStatsFromProcessStatsService(int atomTag) {
         IProcessStats processStatsService = getIProcessStatsService();
         if (processStatsService == null) {
-            return StatsManager.PULL_SKIP;
+            return null;
         }
-
         final long token = Binder.clearCallingIdentity();
         try {
             // force procstats to flush & combine old files into one store
-            long lastHighWaterMark = readProcStatsHighWaterMark(section);
-
-            ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
-            for (int i = 0; i < protoStreams.length; i++) {
-                protoStreams[i] = new ProtoOutputStream();
-            }
-
+            long lastHighWaterMark = readProcStatsHighWaterMark(atomTag);
             ProcessStats procStats = new ProcessStats(false);
             // Force processStatsService to aggregate all in-storage and in-memory data.
-            long highWaterMark = processStatsService.getCommittedStatsMerged(
-                    lastHighWaterMark, section, true, null, procStats);
-            procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
-
-            for (int i = 0; i < protoStreams.length; i++) {
-                byte[] bytes = protoStreams[i].getBytes(); // cache the value
-                if (bytes.length > 0) {
-                    pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, bytes,
-                            // This is a shard ID, and is specified in the metric definition to be
-                            // a dimension. This will result in statsd using RANDOM_ONE_SAMPLE to
-                            // keep all the shards, as it thinks each shard is a different dimension
-                            // of data.
-                            i));
-                }
-            }
-
-            new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark)
+            long highWaterMark =
+                    processStatsService.getCommittedStatsMerged(
+                            lastHighWaterMark,
+                            ProcessStats.REPORT_ALL, // ignored since committedStats below is null.
+                            true,
+                            null, // committedStats
+                            procStats);
+            new File(
+                            mBaseDir.getAbsolutePath()
+                                    + "/"
+                                    + highWaterMarkFilePrefix(atomTag)
+                                    + "_"
+                                    + lastHighWaterMark)
                     .delete();
-            new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + highWaterMark)
+            new File(
+                            mBaseDir.getAbsolutePath()
+                                    + "/"
+                                    + highWaterMarkFilePrefix(atomTag)
+                                    + "_"
+                                    + highWaterMark)
                     .createNewFile();
+            return procStats;
         } catch (RemoteException | IOException e) {
             Slog.e(TAG, "Getting procstats failed: ", e);
-            return StatsManager.PULL_SKIP;
+            return null;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
+    }
+
+    @GuardedBy("mProcStatsLock")
+    private int pullProcStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+        ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+        if (procStats == null) {
+            return StatsManager.PULL_SKIP;
+        }
+        ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
+        for (int i = 0; i < protoStreams.length; i++) {
+            protoStreams[i] = new ProtoOutputStream();
+        }
+        procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
+        for (int i = 0; i < protoStreams.length; i++) {
+            byte[] bytes = protoStreams[i].getBytes(); // cache the value
+            if (bytes.length > 0) {
+                pulledData.add(
+                        FrameworkStatsLog.buildStatsEvent(
+                                atomTag,
+                                bytes,
+                                // This is a shard ID, and is specified in the metric definition to
+                                // be
+                                // a dimension. This will result in statsd using RANDOM_ONE_SAMPLE
+                                // to
+                                // keep all the shards, as it thinks each shard is a different
+                                // dimension
+                                // of data.
+                                i));
+            }
+        }
         return StatsManager.PULL_SUCCESS;
     }
 
+    @GuardedBy("mProcStatsLock")
+    private int pullProcessStateLocked(int atomTag, List<StatsEvent> pulledData) {
+        ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+        if (procStats == null) {
+            return StatsManager.PULL_SKIP;
+        }
+        procStats.dumpProcessState(atomTag, new StatsEventOutput(pulledData));
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    @GuardedBy("mProcStatsLock")
+    private int pullProcessAssociationLocked(int atomTag, List<StatsEvent> pulledData) {
+        ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+        if (procStats == null) {
+            return StatsManager.PULL_SKIP;
+        }
+        procStats.dumpProcessAssociation(atomTag, new StatsEventOutput(pulledData));
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private String highWaterMarkFilePrefix(int atomTag) {
+        // For backward compatibility, use the legacy ProcessStats enum value as the prefix for
+        // PROC_STATS and PROC_STATS_PKG_PROC.
+        if (atomTag == FrameworkStatsLog.PROC_STATS) {
+            return String.valueOf(ProcessStats.REPORT_ALL);
+        }
+        if (atomTag == FrameworkStatsLog.PROC_STATS_PKG_PROC) {
+            return String.valueOf(ProcessStats.REPORT_PKG_PROC_STATS);
+        }
+        return "atom-" + atomTag;
+    }
+
     // read high watermark for section
-    private long readProcStatsHighWaterMark(int section) {
+    private long readProcStatsHighWaterMark(int atomTag) {
         try {
-            File[] files = mBaseDir.listFiles((d, name) -> {
-                return name.toLowerCase().startsWith(String.valueOf(section) + '_');
-            });
+            File[] files =
+                    mBaseDir.listFiles(
+                            (d, name) -> {
+                                return name.toLowerCase()
+                                        .startsWith(highWaterMarkFilePrefix(atomTag) + '_');
+                            });
             if (files == null || files.length == 0) {
                 return 0;
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4f87b1a..90ac1aa 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6578,12 +6578,29 @@
         updateReportedVisibilityLocked();
     }
 
+    /**
+     * Sets whether something has been visible in the task and returns {@code true} if the state
+     * is changed from invisible to visible.
+     */
+    private boolean setTaskHasBeenVisible() {
+        final boolean wasTaskVisible = task.getHasBeenVisible();
+        if (wasTaskVisible) {
+            return false;
+        }
+        if (inTransition()) {
+            // The deferring will be canceled until transition is ready so it won't dispatch
+            // intermediate states to organizer.
+            task.setDeferTaskAppear(true);
+        }
+        task.setHasBeenVisible(true);
+        return true;
+    }
+
     void onStartingWindowDrawn() {
         boolean wasTaskVisible = false;
         if (task != null) {
             mSplashScreenStyleSolidColor = true;
-            wasTaskVisible = task.getHasBeenVisible();
-            task.setHasBeenVisible(true);
+            wasTaskVisible = !setTaskHasBeenVisible();
         }
 
         // The transition may not be executed if the starting process hasn't attached. But if the
@@ -6621,7 +6638,7 @@
         }
         finishLaunchTickingLocked();
         if (task != null) {
-            task.setHasBeenVisible(true);
+            setTaskHasBeenVisible();
         }
         // Clear indicated launch root task because there's no trampoline activity to expect after
         // the windows are drawn.
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index f916ee4..800fe09 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -197,6 +197,12 @@
     // Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
     private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled;
 
+    // Whether using display aspect ratio as a default aspect ratio for all letterboxed apps.
+    // mIsSplitScreenAspectRatioForUnresizableAppsEnabled and
+    // config_letterboxDefaultMinAspectRatioForUnresizableApps take priority over this for
+    // unresizable apps
+    private boolean mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox;
+
     // Whether letterboxing strategy is enabled for translucent activities. If {@value false}
     // all the feature is disabled
     private boolean mTranslucentLetterboxingEnabled;
@@ -288,6 +294,9 @@
                 R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
         mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
                 R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
+        mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources()
+                .getBoolean(R.bool
+                        .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled);
         mTranslucentLetterboxingEnabled = mContext.getResources().getBoolean(
                 R.bool.config_letterboxIsEnabledForTranslucentActivities);
         mIsCameraCompatTreatmentEnabled = mContext.getResources().getBoolean(
@@ -943,6 +952,13 @@
     }
 
     /**
+     * Whether using display aspect ratio as a default aspect ratio for all letterboxed apps.
+     */
+    boolean getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox() {
+        return mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox;
+    }
+
+    /**
      * Overrides whether using split screen aspect ratio as a default aspect ratio for unresizable
      * apps.
      */
@@ -951,6 +967,14 @@
     }
 
     /**
+     * Overrides whether using display aspect ratio as a default aspect ratio for all letterboxed
+     * apps.
+     */
+    void setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(boolean enabled) {
+        mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = enabled;
+    }
+
+    /**
      * Resets whether using split screen aspect ratio as a default aspect ratio for unresizable
      * apps {@link R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}.
      */
@@ -959,6 +983,16 @@
                 R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
     }
 
+    /**
+     * Resets whether using display aspect ratio as a default aspect ratio for all letterboxed
+     * apps {@link R.bool.config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled}.
+     */
+    void resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox() {
+        mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources()
+                .getBoolean(R.bool
+                        .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled);
+    }
+
     boolean isTranslucentLetterboxingEnabled() {
         return mTranslucentLetterboxingOverrideEnabled || (mTranslucentLetterboxingEnabled
                 && isTranslucentLetterboxingAllowed());
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9c43c1d..67e188f 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -584,7 +584,7 @@
                 ? getSplitScreenAspectRatio()
                 : mActivityRecord.shouldCreateCompatDisplayInsets()
                     ? getDefaultMinAspectRatioForUnresizableApps()
-                    : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+                    : getDefaultMinAspectRatio();
     }
 
     private float getDefaultMinAspectRatioForUnresizableApps() {
@@ -593,7 +593,7 @@
             return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
                     > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
                             ? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
-                            : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+                            : getDefaultMinAspectRatio();
         }
 
         return getSplitScreenAspectRatio();
@@ -621,6 +621,16 @@
         return computeAspectRatio(bounds);
     }
 
+    private float getDefaultMinAspectRatio() {
+        final DisplayContent displayContent = mActivityRecord.getDisplayContent();
+        if (displayContent == null
+                || !mLetterboxConfiguration
+                    .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
+            return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+        }
+        return computeAspectRatio(new Rect(displayContent.getBounds()));
+    }
+
     Resources getResources() {
         return mActivityRecord.mWmService.mContext.getResources();
     }
@@ -1014,6 +1024,9 @@
                 + mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps());
         pw.println(prefix + "  isSplitScreenAspectRatioForUnresizableAppsEnabled="
                 + mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
+        pw.println(prefix + "  isDisplayAspectRatioEnabledForFixedOrientationLetterbox="
+                + mLetterboxConfiguration
+                .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 15a5ebf..97e0b1e 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -310,6 +310,11 @@
         }
     }
 
+    @Override
+    public void performHapticFeedbackAsync(int effectId, boolean always) {
+        performHapticFeedback(effectId, always);
+    }
+
     /* Drag/drop */
 
     @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b7021c8..a76d836 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4148,21 +4148,28 @@
 
     void setHasBeenVisible(boolean hasBeenVisible) {
         mHasBeenVisible = hasBeenVisible;
-        if (hasBeenVisible) {
-            if (!mDeferTaskAppear) sendTaskAppeared();
-            if (!isRootTask()) {
-                getRootTask().setHasBeenVisible(true);
+        if (!hasBeenVisible || mDeferTaskAppear) {
+            return;
+        }
+        sendTaskAppeared();
+        for (WindowContainer<?> parent = getParent(); parent != null; parent = parent.getParent()) {
+            final Task parentTask = parent.asTask();
+            if (parentTask == null) {
+                break;
             }
+            parentTask.setHasBeenVisible(true);
         }
     }
 
+
     boolean getHasBeenVisible() {
         return mHasBeenVisible;
     }
 
     void setDeferTaskAppear(boolean deferTaskAppear) {
+        final boolean wasDeferred = mDeferTaskAppear;
         mDeferTaskAppear = deferTaskAppear;
-        if (!mDeferTaskAppear) {
+        if (wasDeferred && !deferTaskAppear) {
             sendTaskAppeared();
         }
     }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 8b1fb51..7d3367f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1156,12 +1156,15 @@
     private void commitVisibleActivities(SurfaceControl.Transaction transaction) {
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
-            if (ar == null || !ar.isVisibleRequested()) {
+            if (ar == null || ar.getTask() == null) {
                 continue;
             }
-            ar.commitVisibility(true /* visible */, false /* performLayout */,
-                    true /* fromTransition */);
-            ar.commitFinishDrawing(transaction);
+            if (ar.isVisibleRequested()) {
+                ar.commitVisibility(true /* visible */, false /* performLayout */,
+                        true /* fromTransition */);
+                ar.commitFinishDrawing(transaction);
+            }
+            ar.getTask().setDeferTaskAppear(false);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index e2c9c17..e931175 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -970,6 +970,10 @@
                     runSetBooleanFlag(pw, mLetterboxConfiguration
                             ::setIsSplitScreenAspectRatioForUnresizableAppsEnabled);
                     break;
+                case "--isDisplayAspectRatioEnabledForFixedOrientationLetterbox":
+                    runSetBooleanFlag(pw, mLetterboxConfiguration
+                            ::setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox);
+                    break;
                 case "--isTranslucentLetterboxingEnabled":
                     runSetBooleanFlag(pw, mLetterboxConfiguration
                             ::setTranslucentLetterboxingOverrideEnabled);
@@ -1045,6 +1049,10 @@
                         mLetterboxConfiguration
                                 .resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
                         break;
+                    case "IsDisplayAspectRatioEnabledForFixedOrientationLetterbox":
+                        mLetterboxConfiguration
+                                .resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
+                        break;
                     case "isTranslucentLetterboxingEnabled":
                         mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
                         break;
@@ -1155,6 +1163,7 @@
             mLetterboxConfiguration.resetDefaultPositionForVerticalReachability();
             mLetterboxConfiguration.resetIsEducationEnabled();
             mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+            mLetterboxConfiguration.resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
             mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
             mLetterboxConfiguration.resetCameraCompatRefreshEnabled();
             mLetterboxConfiguration.resetCameraCompatRefreshCycleThroughStopEnabled();
@@ -1202,7 +1211,9 @@
             pw.println("Is using split screen aspect ratio as aspect ratio for unresizable apps: "
                     + mLetterboxConfiguration
                             .getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
-
+            pw.println("Is using display aspect ratio as aspect ratio for all letterboxed apps: "
+                    + mLetterboxConfiguration
+                            .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
             pw.println("    Is activity \"refresh\" in camera compatibility treatment enabled: "
                     + mLetterboxConfiguration.isCameraCompatRefreshEnabled());
             pw.println("    Refresh using \"stopped -> resumed\" cycle: "
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ce67f3c..1406a396 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -30,6 +30,7 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_APP_STANDBY;
+import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
 import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
@@ -105,6 +106,7 @@
 import static android.app.admin.DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
 import static android.app.admin.DevicePolicyManager.STATUS_HAS_DEVICE_OWNER;
 import static android.app.admin.DevicePolicyManager.STATUS_HAS_PAIRED;
+import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
 import static android.app.admin.DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED;
 import static android.app.admin.DevicePolicyManager.STATUS_NONSYSTEM_USER_EXISTS;
 import static android.app.admin.DevicePolicyManager.STATUS_NOT_SYSTEM_USER;
@@ -140,6 +142,7 @@
 import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.GET_META_DATA;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
@@ -2929,7 +2932,7 @@
         final ActivityInfo ai = mInjector.binderWithCleanCallingIdentity(() -> {
             try {
                 return mIPackageManager.getReceiverInfo(adminName,
-                        PackageManager.GET_META_DATA
+                        GET_META_DATA
                         | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
                         | PackageManager.MATCH_DIRECT_BOOT_AWARE
                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
@@ -7957,13 +7960,11 @@
         Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
 
         synchronized (getLockObject()) {
-            if (mOwners.hasProfileOwner(userId) || mOwners.hasDeviceOwner()) {
-                final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
-                return admin.mNearbyNotificationStreamingPolicy;
-            }
+            final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
+            return admin != null
+                    ? admin.mNearbyNotificationStreamingPolicy
+                    : NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
         }
-
-        return NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
     }
 
     @Override
@@ -7998,13 +7999,11 @@
         Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
 
         synchronized (getLockObject()) {
-            if (mOwners.hasProfileOwner(userId) || mOwners.hasDeviceOwner()) {
-                final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
-                return admin.mNearbyAppStreamingPolicy;
-            }
+            final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
+            return admin != null
+                    ? admin.mNearbyAppStreamingPolicy
+                    : NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
         }
-
-        return NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
     }
 
     /**
@@ -14758,7 +14757,14 @@
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
 
-        return checkProvisioningPreconditionSkipPermission(action, packageName, caller.getUserId());
+        long originalId = mInjector.binderClearCallingIdentity();
+        try {
+            return checkProvisioningPreconditionSkipPermission(
+                    action, packageName, caller.getUserId());
+        } finally {
+            mInjector.binderRestoreCallingIdentity(originalId);
+        }
+
     }
     private int checkProvisioningPreconditionSkipPermission(String action,
             String packageName, int userId) {
@@ -14824,22 +14830,31 @@
             return STATUS_USER_HAS_PROFILE_OWNER;
         }
 
-        boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode();
-        // System user is always running in headless system user mode.
-        if (!isHeadlessSystemUserMode
-                && !mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
+        if (!mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
             return STATUS_USER_NOT_RUNNING;
         }
         if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
             return STATUS_HAS_PAIRED;
         }
 
+        boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode();
+
         if (isHeadlessSystemUserMode) {
             if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
                 Slogf.e(LOG_TAG, "In headless system user mode, "
                         + "device owner can only be set on headless system user.");
                 return STATUS_NOT_SYSTEM_USER;
             }
+
+            if (owner != null) {
+                DeviceAdminInfo adminInfo = findAdmin(
+                        owner, deviceOwnerUserId, /* throwForMissingPermission= */ false);
+
+                if (adminInfo.getHeadlessDeviceOwnerMode()
+                        != HEADLESS_DEVICE_OWNER_MODE_AFFILIATED) {
+                    return STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
+                }
+            }
         }
 
         if (isAdb) {
@@ -18971,11 +18986,12 @@
                         "Provisioning preconditions failed with result: " + result);
             }
             onProvisionFullyManagedDeviceStarted(provisioningParams);
+
+            // These properties are global so will apply on all users
             setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
             setLocale(provisioningParams.getLocale());
 
-            final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
-                    ? UserHandle.USER_SYSTEM : caller.getUserId();
+            int deviceOwnerUserId = UserHandle.USER_SYSTEM;
             if (!removeNonRequiredAppsForManagedDevice(
                     deviceOwnerUserId,
                     provisioningParams.isLeaveAllSystemAppsEnabled(),
@@ -19101,9 +19117,12 @@
     }
 
     private void disallowAddUser() {
-        if (mInjector.userManagerIsHeadlessSystemUserMode()) {
-            Slogf.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
-            return;
+        if (!isHeadlessFlagEnabled() || mIsAutomotive) {
+            // Auto still enables adding users due to the communal nature of those devices
+            if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+                Slogf.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
+                return;
+            }
         }
         for (UserInfo userInfo : mUserManager.getUsers()) {
             UserHandle userHandle = userInfo.getUserHandle();
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
index 694efbb..730cac9 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
@@ -440,7 +440,8 @@
                     Log.w(
                         LOG_TAG, "Ignoring permission $permissionName declared in system package" +
                             " $newPackageName: already declared in another system package" +
-                            " $oldPackageName")
+                            " $oldPackageName"
+                    )
                     return@forEachIndexed
                 }
             } else {
@@ -516,15 +517,20 @@
         if (packageState != null && androidPackage == null) {
             return
         }
-        // TODO: STOPSHIP: We may need to retain permission definitions by disabled system packages
-        //  to retain their permission state.
-
+        val disabledSystemPackage = systemState.disabledSystemPackageStates[packageName]
+            ?.androidPackage
+        // Unlike in the previous implementation, we now also retain permission trees defined by
+        // disabled system packages for consistency with permissions.
         val isPermissionTreeRemoved = systemState.permissionTrees.removeAllIndexed {
             _, permissionTreeName, permissionTree ->
             permissionTree.packageName == packageName && (
                 packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
                     it.isTree && it.name == permissionTreeName
                 }
+            ) && (
+                disabledSystemPackage?.permissions?.anyIndexed { _, it ->
+                    it.isTree && it.name == permissionTreeName
+                } != true
             )
         }
         if (isPermissionTreeRemoved) {
@@ -538,6 +544,10 @@
                 packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
                     !it.isTree && it.name == permissionName
                 }
+            ) && (
+                disabledSystemPackage?.permissions?.anyIndexed { _, it ->
+                    !it.isTree && it.name == permissionName
+                } != true
             )) {
                 // Different from the old implementation where we keep the permission state if the
                 // permission is declared by a disabled system package (ag/15189282), we now
@@ -574,8 +584,14 @@
     private fun MutateStateScope.trimPermissionStates(appId: Int) {
         val requestedPermissions = IndexedSet<String>()
         forEachPackageInAppId(appId) {
+            // Note that we still trim the permission states requested by disabled system packages.
+            // Because in the previous implementation:
+            // despite revokeSharedUserPermissionsForLeavingPackageInternal() retains permissions
+            // requested by disabled system packages, revokeUnusedSharedUserPermissionsLocked(),
+            // which is call upon app update installation, didn't do such preservation.
+            // Hence, permissions only requested by disabled system packages were still trimmed in
+            // the previous implementation.
             requestedPermissions += it.androidPackage!!.requestedPermissions
-            // TODO: STOPSHIP: Retain permissions requested by disabled system packages.
         }
         newState.userStates.forEachIndexed { _, userId, userState ->
             userState.uidPermissionFlags[appId]?.forEachReversedIndexed { _, permissionName, _ ->
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index 70a5c3f..05a8b11 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -28,7 +28,7 @@
     ],
 
     srcs: [
-        "src/server/**/*.java",
+        "src/com/android/server/inputmethod/**/*.java",
     ],
 
     static_libs: [
diff --git a/services/tests/InputMethodSystemServerTests/TEST_MAPPING b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
new file mode 100644
index 0000000..77e32a7
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksInputMethodSystemServerTests",
+      "options": [
+        {"include-filter": "com.android.server.inputmethod"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
+  ]
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 73d04c6..720f486 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -65,19 +65,24 @@
 
     @Test
     public void testPerformShowIme() throws Exception {
-        mVisibilityApplier.performShowIme(mWindowToken, null, null, SHOW_SOFT_INPUT);
+        synchronized (ImfLock.class) {
+            mVisibilityApplier.performShowIme(mWindowToken, null /* statsToken */,
+                    InputMethodManager.SHOW_IMPLICIT, null, SHOW_SOFT_INPUT);
+        }
         verifyShowSoftInput(false, true, InputMethodManager.SHOW_IMPLICIT);
     }
 
     @Test
     public void testPerformHideIme() throws Exception {
-        mVisibilityApplier.performHideIme(mWindowToken, null, null, HIDE_SOFT_INPUT);
+        synchronized (ImfLock.class) {
+            mVisibilityApplier.performHideIme(mWindowToken, null /* statsToken */, null,
+                    HIDE_SOFT_INPUT);
+        }
         verifyHideSoftInput(false, true);
     }
 
     @Test
     public void testApplyImeVisibility_throwForInvalidState() {
-        mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_INVALID);
         assertThrows(IllegalArgumentException.class,
                 () -> mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_INVALID));
     }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 8415fe1..a1b9b98 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -30,6 +30,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.inputmethod.InputMethodManager;
@@ -177,9 +178,28 @@
         assertThat(state.getImeDisplayId()).isEqualTo(FALLBACK_DISPLAY_ID);
     }
 
-    private void initImeTargetWindowState(IBinder windowToken) {
+    @Test
+    public void testComputeState_lastImeRequestedVisible_preserved_When_StateUnChanged() {
+        // Assume the last IME targeted window has requested IME visible
+        final IBinder lastImeTargetWindowToken = new Binder();
+        mInputMethodManagerService.mLastImeTargetWindow = lastImeTargetWindowToken;
+        mComputer.requestImeVisibility(lastImeTargetWindowToken, true);
+        final ImeTargetWindowState lastState = mComputer.getWindowStateOrNull(
+                lastImeTargetWindowToken);
+        assertThat(lastState.isRequestedImeVisible()).isTrue();
+
+        // Verify when focusing the next window with STATE_UNCHANGED flag, the last IME
+        // visibility state will be preserved to the current window state.
+        final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState(mWindowToken);
+        mComputer.computeState(stateWithUnChangedFlag, true /* allowVisible */);
+        assertThat(stateWithUnChangedFlag.isRequestedImeVisible()).isEqualTo(
+                lastState.isRequestedImeVisible());
+    }
+
+    private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) {
         final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
                 0, true, true, true);
         mComputer.setWindowState(windowToken, state);
+        return state;
     }
 }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 804bb49..dbdffd0 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -71,6 +71,8 @@
 
 /** Base class for testing {@link InputMethodManagerService}. */
 public class InputMethodManagerServiceTestBase {
+    private static final int NO_VERIFY_SHOW_FLAGS = -1;
+
     protected static final String TEST_SELECTED_IME_ID = "test.ime";
     protected static final String TEST_EDITOR_PKG_NAME = "test.editor";
     protected static final String TEST_FOCUSED_WINDOW_NAME = "test.editor/activity";
@@ -239,7 +241,7 @@
 
     protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput)
             throws RemoteException {
-        verifyShowSoftInput(setVisible, showSoftInput, anyInt());
+        verifyShowSoftInput(setVisible, showSoftInput, NO_VERIFY_SHOW_FLAGS);
     }
 
     protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput, int showFlags)
@@ -249,7 +251,8 @@
                     .setCurrentMethodVisible();
         }
         verify(mMockInputMethod, times(showSoftInput ? 1 : 0))
-                .showSoftInput(any(), any(), eq(showFlags), any());
+                .showSoftInput(any(), any(),
+                        showFlags != NO_VERIFY_SHOW_FLAGS ? eq(showFlags) : anyInt(), any());
     }
 
     protected void verifyHideSoftInput(boolean setNotVisible, boolean hideSoftInput)
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 2a790a1..f2cba40 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -80,8 +80,6 @@
     @Mock
     private DisplayBlanker mDisplayBlankerMock;
     @Mock
-    private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
-    @Mock
     private LogicalDisplay mLogicalDisplayMock;
     @Mock
     private DisplayDevice mDisplayDeviceMock;
@@ -171,7 +169,7 @@
                 mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
                 mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
-        }, mHighBrightnessModeMetadataMock);
+        });
 
         when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
         // send a display power request
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index d99ed78..4f8cb88 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -84,8 +84,6 @@
     @Mock
     private DisplayDevice mDisplayDeviceMock;
     @Mock
-    private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock;
-    @Mock
     private BrightnessTracker mBrightnessTrackerMock;
     @Mock
     private BrightnessSetting mBrightnessSettingMock;
@@ -153,7 +151,7 @@
                 mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
                 mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
-        }, mHighBrightnessModeMetadataMock);
+        });
 
         when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
         // send a display power request
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 210aeef..4998a6c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1085,27 +1085,6 @@
         // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
     }
 
-    /**
-     * TODO(b/174859111): move to automotive-only section
-     * Test for {@link DevicePolicyManager#setDeviceOwner} in headless system user mode.
-     */
-    @Test
-    public void testSetDeviceOwner_headlessSystemUserMode() throws Exception {
-        when(getServices().userManagerForMock.isHeadlessSystemUserMode()).thenReturn(true);
-        setDeviceOwner_headlessSystemUser();
-
-        // Try to set a profile owner on the same user, which should fail.
-        setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
-        dpm.setActiveAdmin(admin2, /* refreshing= */ true, CALLER_USER_HANDLE);
-        assertExpectException(IllegalStateException.class,
-                /* messageRegex= */ "profile owner is already set",
-                () -> dpm.setProfileOwner(admin2, CALLER_USER_HANDLE));
-
-        // DO admin can't be deactivated.
-        dpm.removeActiveAdmin(admin1);
-        assertThat(dpm.isAdminActive(admin1)).isTrue();
-    }
-
     private void setDeviceOwner() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java b/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
deleted file mode 100644
index 24fc348..0000000
--- a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static org.junit.Assert.assertEquals;
-
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class HbmEventTest {
-    private long mStartTimeMillis;
-    private long mEndTimeMillis;
-    private HbmEvent mHbmEvent;
-
-    @Before
-    public void setUp() {
-        mStartTimeMillis = 10;
-        mEndTimeMillis = 20;
-        mHbmEvent = new HbmEvent(mStartTimeMillis, mEndTimeMillis);
-    }
-
-    @Test
-    public void getCorrectValues() {
-        assertEquals(mHbmEvent.getStartTimeMillis(), mStartTimeMillis);
-        assertEquals(mHbmEvent.getEndTimeMillis(), mEndTimeMillis);
-    }
-
-    @Test
-    public void toStringGeneratesExpectedString() {
-        String actualString = mHbmEvent.toString();
-        String expectedString = "HbmEvent: {startTimeMillis:" + mStartTimeMillis
-                + ", endTimeMillis: " + mEndTimeMillis + "}, total: "
-                + ((mEndTimeMillis - mStartTimeMillis) / 1000) + "]";
-        assertEquals(actualString, expectedString);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 2655c3f..a1e5ce7 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -96,7 +96,6 @@
     private Binder mDisplayToken;
     private String mDisplayUniqueId;
     private Context mContextSpy;
-    private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
 
     @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -119,7 +118,6 @@
         mTestLooper = new TestLooper(mClock::now);
         mDisplayToken = null;
         mDisplayUniqueId = "unique_id";
-
         mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(resolver);
@@ -136,8 +134,7 @@
         initHandler(null);
         final HighBrightnessModeController hbmc = new HighBrightnessModeController(
                 mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
-                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
-                null, mContextSpy);
+                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
         assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
     }
@@ -147,8 +144,7 @@
         initHandler(null);
         final HighBrightnessModeController hbmc = new HighBrightnessModeController(
                 mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
-                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {},
-                null, mContextSpy);
+                mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
         hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
         hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
         assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -703,12 +699,9 @@
     // Creates instance with standard initialization values.
     private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
         initHandler(clock);
-        if (mHighBrightnessModeMetadata == null) {
-            mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
-        }
         return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
                 DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
-                DEFAULT_HBM_DATA, null, () -> {}, mHighBrightnessModeMetadata, mContextSpy);
+                DEFAULT_HBM_DATA, null, () -> {}, mContextSpy);
     }
 
     private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
deleted file mode 100644
index ede54e0..0000000
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static org.junit.Assert.assertEquals;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class HighBrightnessModeMetadataTest {
-    private HighBrightnessModeMetadata mHighBrightnessModeMetadata;
-
-    private long mRunningStartTimeMillis = -1;
-
-    @Before
-    public void setUp() {
-        mHighBrightnessModeMetadata = new HighBrightnessModeMetadata();
-    }
-
-    @Test
-    public void checkDefaultValues() {
-        assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
-                mRunningStartTimeMillis);
-        assertEquals(mHighBrightnessModeMetadata.getHbmEventQueue().size(), 0);
-    }
-
-    @Test
-    public void checkSetValues() {
-        mRunningStartTimeMillis = 10;
-        mHighBrightnessModeMetadata.setRunningStartTimeMillis(mRunningStartTimeMillis);
-        assertEquals(mHighBrightnessModeMetadata.getRunningStartTimeMillis(),
-                mRunningStartTimeMillis);
-        HbmEvent expectedHbmEvent = new HbmEvent(10, 20);
-        mHighBrightnessModeMetadata.addHbmEvent(expectedHbmEvent);
-        HbmEvent actualHbmEvent  = mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst();
-        assertEquals(expectedHbmEvent.toString(), actualHbmEvent.toString());
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 352e498..3b7b5eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1931,6 +1931,132 @@
     }
 
     @Test
+    public void testDisplayAspectRatioForResizablePortraitApps() {
+        // Set up a display in portrait and ignoring orientation request.
+        int displayWidth = 1400;
+        int displayHeight = 1600;
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
+
+        // Enable display aspect ratio to take precedence before
+        // fixedOrientationLetterboxAspectRatio
+        mWm.mLetterboxConfiguration
+                .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+        // Set up resizable app in portrait
+        prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, false /* isUnresizable */);
+
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+        organizer.mPrimary.setBounds(0, 0, displayWidth, getExpectedSplitSize(displayHeight));
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+        // App should launch in fixed orientation letterbox.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        // Checking that there is no size compat mode.
+        assertFitted();
+        // Check that the display aspect ratio is used by the app.
+        final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
+        final float delta = 0.01f;
+        assertEquals(targetMinAspectRatio, ActivityRecord
+                .computeAspectRatio(mActivity.getBounds()), delta);
+    }
+
+    @Test
+    public void testDisplayAspectRatioForResizableLandscapeApps() {
+        // Set up a display in landscape and ignoring orientation request.
+        int displayWidth = 1600;
+        int displayHeight = 1400;
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(2f);
+
+        // Enable display aspect ratio to take precedence before
+        // fixedOrientationLetterboxAspectRatio
+        mWm.mLetterboxConfiguration
+                .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+        // Set up resizable app in landscape
+        prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_LANDSCAPE, false /* isUnresizable */);
+
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+        organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(displayWidth), displayHeight);
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+        // App should launch in fixed orientation letterbox.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        // Checking that there is no size compat mode.
+        assertFitted();
+        // Check that the display aspect ratio is used by the app.
+        final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
+        final float delta = 0.01f;
+        assertEquals(targetMinAspectRatio, ActivityRecord
+                .computeAspectRatio(mActivity.getBounds()), delta);
+    }
+
+    @Test
+    public void testDisplayAspectRatioForUnresizableLandscapeApps() {
+        // Set up a display in portrait and ignoring orientation request.
+        int displayWidth = 1400;
+        int displayHeight = 1600;
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
+        // Enable display aspect ratio to take precedence before
+        // fixedOrientationLetterboxAspectRatio
+        mWm.mLetterboxConfiguration
+                .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        // App should launch in fixed orientation letterbox.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        // Checking that there is no size compat mode.
+        assertFitted();
+        // Check that the display aspect ratio is used by the app.
+        final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
+        final float delta = 0.01f;
+        assertEquals(targetMinAspectRatio, ActivityRecord
+                .computeAspectRatio(mActivity.getBounds()), delta);
+    }
+
+    @Test
+    public void testDisplayAspectRatioForUnresizablePortraitApps() {
+        // Set up a display in landscape and ignoring orientation request.
+        int displayWidth = 1600;
+        int displayHeight = 1400;
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
+        // Enable display aspect ratio to take precedence before
+        // fixedOrientationLetterboxAspectRatio
+        mWm.mLetterboxConfiguration
+                .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        // App should launch in fixed orientation letterbox.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        // Checking that there is no size compat mode.
+        assertFitted();
+        // Check that the display aspect ratio is used by the app.
+        final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
+        final float delta = 0.01f;
+        assertEquals(targetMinAspectRatio, ActivityRecord
+                .computeAspectRatio(mActivity.getBounds()), delta);
+    }
+
+    @Test
     public void
             testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
         // Set up a display in landscape and ignoring orientation request.
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 21135e0..9018138 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -566,6 +566,8 @@
         doReturn(mock(IBinder.class)).when(delegateProc.getThread()).asBinder();
         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true)
                 .setVisible(false).build();
+        final Task task = app.getTask();
+        task.setTaskOrganizer(mock(ITaskOrganizer.class), true /* skipTaskAppeared */);
         app.setVisibleRequested(true);
         final TransitionController controller = app.mTransitionController;
         final Transition transition = controller.createTransition(TRANSIT_OPEN);
@@ -576,7 +578,11 @@
         controller.requestStartTransition(transition, null /* startTask */, remoteTransition,
                 null /* displayChange */);
         testPlayer.startTransition();
+        app.onStartingWindowDrawn();
+        // The task appeared event should be deferred until transition ready.
+        assertFalse(task.taskAppearedReady());
         testPlayer.onTransactionReady(app.getSyncTransaction());
+        assertTrue(task.taskAppearedReady());
         assertTrue(playerProc.isRunningRemoteTransition());
         assertTrue(delegateProc.isRunningRemoteTransition());
         assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index bbdf621..e5efe05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -262,6 +262,12 @@
         // device form factors.
         mAtm.mWindowManager.mLetterboxConfiguration
                 .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(false);
+        // Ensure aspect ratio for al apps isn't overridden on any device target.
+        // {@link com.android.internal.R.bool
+        // .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled}, may be set on
+        // some device form factors.
+        mAtm.mWindowManager.mLetterboxConfiguration
+                .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(false);
 
         checkDeviceSpecificOverridesNotApplied();
     }
@@ -276,6 +282,8 @@
         mAtm.mWindowManager.mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
         mAtm.mWindowManager.mLetterboxConfiguration
                 .resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+        mAtm.mWindowManager.mLetterboxConfiguration
+                .resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
     }
 
     /**
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
index 0dcf1b6..775f1b8 100644
--- a/telecomm/TEST_MAPPING
+++ b/telecomm/TEST_MAPPING
@@ -25,14 +25,6 @@
       ]
     },
     {
-      "name": "CtsTelephonyTestCases",
-      "options": [
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
-    },
-    {
       "name": "CtsTelephony2TestCases",
       "options": [
         {
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index ceea94b..eac4d16 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -104,12 +104,15 @@
     private int mCarrierRestrictionDefault;
     @MultiSimPolicy
     private int mMultiSimPolicy;
+    @TelephonyManager.CarrierRestrictionStatus
+    private int mCarrierRestrictionStatus;
 
     private CarrierRestrictionRules() {
         mAllowedCarriers = new ArrayList<CarrierIdentifier>();
         mExcludedCarriers = new ArrayList<CarrierIdentifier>();
         mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED;
         mMultiSimPolicy = MULTISIM_POLICY_NONE;
+        mCarrierRestrictionStatus = TelephonyManager.CARRIER_RESTRICTION_STATUS_UNKNOWN;
     }
 
     private CarrierRestrictionRules(Parcel in) {
@@ -120,6 +123,7 @@
         in.readTypedList(mExcludedCarriers, CarrierIdentifier.CREATOR);
         mCarrierRestrictionDefault = in.readInt();
         mMultiSimPolicy = in.readInt();
+        mCarrierRestrictionStatus = in.readInt();
     }
 
     /**
@@ -289,6 +293,11 @@
         return true;
     }
 
+    /** @hide */
+    public int getCarrierRestrictionStatus() {
+        return mCarrierRestrictionStatus;
+    }
+
     /**
      * {@link Parcelable#writeToParcel}
      */
@@ -298,6 +307,7 @@
         out.writeTypedList(mExcludedCarriers);
         out.writeInt(mCarrierRestrictionDefault);
         out.writeInt(mMultiSimPolicy);
+        out.writeInt(mCarrierRestrictionStatus);
     }
 
     /**
@@ -399,5 +409,17 @@
             mRules.mMultiSimPolicy = multiSimPolicy;
             return this;
         }
+
+        /**
+         * Set the device's carrier restriction status
+         *
+         * @param carrierRestrictionStatus device restriction status
+         * @hide
+         */
+        public @NonNull
+        Builder setCarrierRestrictionStatus(int carrierRestrictionStatus) {
+            mRules.mCarrierRestrictionStatus = carrierRestrictionStatus;
+            return this;
+        }
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c47c67e..83b9098 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13162,6 +13162,81 @@
     }
 
     /**
+     * Carrier restriction status value is unknown, in case modem did not provide any
+     * information about carrier restriction status.
+     */
+    public static final int CARRIER_RESTRICTION_STATUS_UNKNOWN = 0;
+
+    /** The device is not restricted to a carrier */
+    public static final int CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED = 1;
+
+    /** The device is restricted to a carrier. */
+    public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED = 2;
+
+    /** The device is restricted to the carrier of the calling application. */
+    public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"CARRIER_RESTRICTION_STATUS_"}, value = {
+            CARRIER_RESTRICTION_STATUS_UNKNOWN,
+            CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED,
+            CARRIER_RESTRICTION_STATUS_RESTRICTED,
+            CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER
+    })
+
+    public @interface CarrierRestrictionStatus {
+    }
+
+    /**
+     * Get the carrier restriction status of the device.
+     * <p>To fetch the carrier restriction status of the device the calling application needs to be
+     * allowlisted to Android at <a href="https://android.googlesource.com/platform/packages/services/Telephony/+/master/assets/CarrierRestrictionOperatorDetails.json">here</a>.
+     * The calling application also needs the READ_PHONE_STATE permission.
+     * The return value of the API is as follows.
+     * <ul>
+     *      <li>return {@link #CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER} if the caller
+     *      and the device locked by the network are same</li>
+     *      <li>return {@link #CARRIER_RESTRICTION_STATUS_RESTRICTED} if the caller and the
+     *      device locked by the network are different</li>
+     *      <li>return {@link #CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the device is
+     *      not locked</li>
+     *      <li>return {@link #CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device locking
+     *      state is unavailable or radio does not supports the feature</li>
+     * </ul>
+     *
+     * @param executor The executor on which the result listener will be called.
+     * @param resultListener {@link Consumer} that will be called with the result fetched
+     *                       from the radio of type {@link CarrierRestrictionStatus}
+     * @throws SecurityException if the caller does not have the required permission/privileges or
+     *                           if the caller is not pre-registered.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public void getCarrierRestrictionStatus(@NonNull Executor executor,
+            @NonNull @CarrierRestrictionStatus
+                    Consumer<Integer> resultListener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+            @Override
+            public void accept(@CarrierRestrictionStatus int result) {
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(result)));
+            }
+        };
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.getCarrierRestrictionStatus(internalCallback, getOpPackageName());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCarrierRestrictionStatus: RemoteException = " + ex);
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
      * Used to enable or disable carrier data by the system based on carrier signalling or
      * carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to
      * user settings, carrier data on/off won't affect user settings but will bypass the
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c7ed347..6099cb1 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2687,4 +2687,9 @@
      * @return {@code true} if the domain selection service is supported.
      */
     boolean isDomainSelectionSupported();
+
+    /**
+     * Get the carrier restriction status of the device.
+     */
+     void getCarrierRestrictionStatus(IIntegerConsumer internalCallback, String packageName);
 }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 616f21cb..fdba9a6 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -1163,5 +1163,13 @@
                 <category android:name="com.android.test.hwui.TEST"/>
             </intent-filter>
         </activity>
+        <activity android:name="MeshLargeActivity"
+                             android:label="Mesh/LargeMesh"
+                             android:exported="true">
+        <intent-filter>
+            <action android:name="android.intent.action.MAIN"/>
+            <category android:name="com.android.test.hwui.TEST"/>
+        </intent-filter>
+    </activity>
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
new file mode 100644
index 0000000..f97d942
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
@@ -0,0 +1,175 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Mesh;
+import android.graphics.MeshSpecification;
+import android.graphics.MeshSpecification.Attribute;
+import android.graphics.MeshSpecification.Varying;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.View;
+
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+
+public class MeshLargeActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new MeshView(this));
+    }
+
+    static class MeshView extends View {
+        MeshView(Context c) {
+            super(c);
+            this.setOnTouchListener((v, event) -> {
+                invalidate();
+                return true;
+            });
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            MeshSpecification meshSpec = createMeshSpecification();
+            int numTriangles = 10000;
+            // number of triangles plus first 2 vertices
+            FloatBuffer vertexBuffer = FloatBuffer.allocate((numTriangles + 2) * 30);
+            ShortBuffer indexBuffer = ShortBuffer.allocate(numTriangles * 3);
+
+            float origin = 500.0f;
+            int radius = 200;
+
+            // origin
+            vertexBuffer.put(0, origin);
+            vertexBuffer.put(1, origin);
+            for (int i = 0; i < 7; i++) {
+                vertexBuffer.put(2 + (i * 4), 1.0f);
+                vertexBuffer.put(2 + (i * 4) + 1, 1.0f);
+                vertexBuffer.put(2 + (i * 4) + 2, 1.0f);
+                vertexBuffer.put(2 + (i * 4) + 3, 1.0f);
+            }
+
+            // first point
+            vertexBuffer.put(30, origin + radius);
+            vertexBuffer.put(31, origin);
+            for (int i = 0; i < 7; i++) {
+                vertexBuffer.put(32 + (i * 4), 1.0f);
+                vertexBuffer.put(32 + (i * 4) + 1, 1.0f);
+                vertexBuffer.put(32 + (i * 4) + 2, 1.0f);
+                vertexBuffer.put(32 + (i * 4) + 3, 1.0f);
+            }
+
+            int nVert = 2;
+            int nInd = 0;
+            for (int i = 2; i <= numTriangles + 1; i++) {
+                double angle = 2 * Math.PI * i / numTriangles;
+                double x = radius * Math.cos(angle);
+                double y = radius * Math.sin(angle);
+                // position
+                vertexBuffer.put(i * 30, origin + (float) x);
+                vertexBuffer.put(i * 30 + 1, origin + (float) y);
+
+                // test through test7
+                for (int j = 0; j < 7; j++) {
+                    vertexBuffer.put((i * 30 + 2) + (j * 4), 1.0f);
+                    vertexBuffer.put((i * 30 + 2) + (j * 4) + 1, 1.0f);
+                    vertexBuffer.put((i * 30 + 2) + (j * 4) + 2, 1.0f);
+                    vertexBuffer.put((i * 30 + 2) + (j * 4) + 3, 1.0f);
+                }
+
+                indexBuffer.put(nInd++, (short) 0);
+                indexBuffer.put(nInd++, (short) (nVert - 1));
+                indexBuffer.put(nInd++, (short) nVert);
+                nVert++;
+            }
+            vertexBuffer.rewind();
+            indexBuffer.rewind();
+            Mesh mesh = Mesh.makeIndexed(
+                    meshSpec, Mesh.TRIANGLES, vertexBuffer, numTriangles + 2, indexBuffer,
+                    new Rect(0, 0, 1000, 1000)
+            );
+            mesh.setFloatUniform("test", 1.0f, 2.0f);
+            Paint paint = new Paint();
+            paint.setColor(Color.BLUE);
+
+            canvas.drawMesh(mesh, BlendMode.SRC, paint);
+        }
+
+        private MeshSpecification createMeshSpecification() {
+            String vs = "Varyings main(const Attributes attributes) { "
+                    + "     Varyings varyings;"
+                    + "     varyings.position = attributes.position;"
+                    + "     return varyings;"
+                    + "}";
+            String fs = "uniform float2 test;"
+                    + "float2 main(const Varyings varyings, out float4 color) {\n"
+                    + "      color = vec4(1.0, 0.0, 0.0, 1.0);"
+                    + "      return varyings.position;\n"
+                    + "}";
+            ArrayList<Attribute> attList = new ArrayList<>();
+            attList.add(new Attribute(MeshSpecification.FLOAT2, 0, "position"));
+            attList.add(new Attribute(
+                    MeshSpecification.FLOAT4,
+                    8,
+                    "test"
+            ));
+            attList.add(new Attribute(
+                    MeshSpecification.FLOAT4,
+                    24,
+                    "test2"
+            ));
+            attList.add(new Attribute(
+                    MeshSpecification.FLOAT4,
+                    40,
+                    "test3"
+            ));
+            attList.add(new Attribute(
+                    MeshSpecification.FLOAT4,
+                    56,
+                    "test4"
+            ));
+            attList.add(new Attribute(
+                    MeshSpecification.FLOAT4,
+                    72,
+                    "test5"
+            ));
+            attList.add(new Attribute(
+                    MeshSpecification.FLOAT4,
+                    88,
+                    "test6"
+            ));
+            attList.add(new Attribute(
+                    MeshSpecification.FLOAT4,
+                    104,
+                    "test7"
+            ));
+            ArrayList<Varying> varyList = new ArrayList<>();
+            return MeshSpecification.make(attList, 120, varyList, vs, fs);
+        }
+    }
+}