Merge "Add aconfig flag for media refactor." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2047168..eed248b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -310,6 +310,8 @@
 aconfig_declarations {
     name: "android.os.flags-aconfig",
     package: "android.os",
+    exportable: true,
+    container: "system",
     srcs: ["core/java/android/os/*.aconfig"],
 }
 
@@ -326,6 +328,13 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+java_aconfig_library {
+    name: "android.os.flags-aconfig-java-export",
+    aconfig_declarations: "android.os.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    mode: "exported",
+}
+
 // VirtualDeviceManager
 cc_aconfig_library {
     name: "android.companion.virtualdevice.flags-aconfig-cc",
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index 3b9b4e7..bb0f3cb 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -9,3 +9,10 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "start_user_before_scheduled_alarms"
+    namespace: "multiuser"
+    description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
+    bug: "314907186"
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index e728a2c..d0a1b02 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -183,6 +183,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.zone.ZoneOffsetTransition;
+import java.time.zone.ZoneRules;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -194,6 +197,7 @@
 import java.util.TimeZone;
 import java.util.TreeSet;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 
 /**
@@ -234,8 +238,12 @@
 
     private static final long TEMPORARY_QUOTA_DURATION = INTERVAL_DAY;
 
-    // System property read on some device configurations to initialize time properly.
+    // System properties read on some device configurations to initialize time properly and
+    // perform DST transitions at the bootloader level.
     private static final String TIMEOFFSET_PROPERTY = "persist.sys.time.offset";
+    private static final String DST_TRANSITION_PROPERTY = "persist.sys.time.dst_transition";
+    private static final String DST_OFFSET_PROPERTY = "persist.sys.time.dst_offset";
+
 
     private final Intent mBackgroundIntent
             = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
@@ -2200,6 +2208,19 @@
 
             final int gmtOffset = newZone.getOffset(mInjector.getCurrentTimeMillis());
             SystemProperties.set(TIMEOFFSET_PROPERTY, String.valueOf(gmtOffset));
+
+            final ZoneRules rules = newZone.toZoneId().getRules();
+            final ZoneOffsetTransition transition = rules.nextTransition(Instant.now());
+            if (null != transition) {
+                // Get the offset between the time after the DST transition and before.
+                final long transitionOffset = TimeUnit.SECONDS.toMillis((
+                        transition.getOffsetAfter().getTotalSeconds()
+                        - transition.getOffsetBefore().getTotalSeconds()));
+                // Time when the next DST transition is programmed.
+                final long nextTransition = TimeUnit.SECONDS.toMillis(transition.toEpochSecond());
+                SystemProperties.set(DST_TRANSITION_PROPERTY, String.valueOf(nextTransition));
+                SystemProperties.set(DST_OFFSET_PROPERTY, String.valueOf(transitionOffset));
+            }
         }
 
         // Clear the default time zone in the system server process. This forces the next call
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 1f5b83c..c1add03 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -458,13 +458,21 @@
     libs: ["stub-annotations"],
 }
 
+java_defaults {
+    name: "android-non-updatable_everything_from_text_defaults",
+    defaults: [
+        "android-non-updatable_from_text_defaults",
+    ],
+    stubs_type: "everything",
+}
+
 java_api_library {
     name: "android-non-updatable.stubs.from-text",
     api_surface: "public",
     api_contributions: [
         "api-stubs-docs-non-updatable.api.contribution",
     ],
-    defaults: ["android-non-updatable_from_text_defaults"],
+    defaults: ["android-non-updatable_everything_from_text_defaults"],
     full_api_surface_stub: "android_stubs_current.from-text",
 }
 
@@ -475,7 +483,7 @@
         "api-stubs-docs-non-updatable.api.contribution",
         "system-api-stubs-docs-non-updatable.api.contribution",
     ],
-    defaults: ["android-non-updatable_from_text_defaults"],
+    defaults: ["android-non-updatable_everything_from_text_defaults"],
     full_api_surface_stub: "android_system_stubs_current.from-text",
 }
 
@@ -487,7 +495,7 @@
         "system-api-stubs-docs-non-updatable.api.contribution",
         "test-api-stubs-docs-non-updatable.api.contribution",
     ],
-    defaults: ["android-non-updatable_from_text_defaults"],
+    defaults: ["android-non-updatable_everything_from_text_defaults"],
     full_api_surface_stub: "android_test_stubs_current.from-text",
 }
 
@@ -499,7 +507,7 @@
         "system-api-stubs-docs-non-updatable.api.contribution",
         "module-lib-api-stubs-docs-non-updatable.api.contribution",
     ],
-    defaults: ["android-non-updatable_from_text_defaults"],
+    defaults: ["android-non-updatable_everything_from_text_defaults"],
     full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
 }
 
@@ -515,7 +523,7 @@
         "test-api-stubs-docs-non-updatable.api.contribution",
         "module-lib-api-stubs-docs-non-updatable.api.contribution",
     ],
-    defaults: ["android-non-updatable_from_text_defaults"],
+    defaults: ["android-non-updatable_everything_from_text_defaults"],
     full_api_surface_stub: "android_test_module_lib_stubs_current.from-text",
 
     // This module is only used for hiddenapi, and other modules should not
@@ -836,6 +844,7 @@
     ],
     visibility: ["//visibility:public"],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 java_api_library {
@@ -852,6 +861,7 @@
     ],
     visibility: ["//visibility:public"],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 java_api_library {
@@ -870,6 +880,7 @@
     ],
     visibility: ["//visibility:public"],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 java_api_library {
@@ -888,6 +899,7 @@
         "system-api-stubs-docs-non-updatable.api.contribution",
     ],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 java_api_library {
@@ -908,6 +920,7 @@
     ],
     visibility: ["//visibility:public"],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 java_api_library {
@@ -922,6 +935,7 @@
     ],
     visibility: ["//visibility:public"],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 java_api_library {
@@ -947,6 +961,7 @@
         "//visibility:private",
     ],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 java_api_library {
@@ -964,6 +979,7 @@
     ],
     visibility: ["//visibility:public"],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 ////////////////////////////////////////////////////////////////////////
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index caddf5a..af40c3d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2031,41 +2031,6 @@
     method public android.media.PlaybackParams setAudioStretchMode(int);
   }
 
-  @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public final class RingtoneSelection {
-    method @NonNull public static android.media.RingtoneSelection fromUri(@Nullable android.net.Uri, int);
-    method public int getSoundSource();
-    method @Nullable public android.net.Uri getSoundUri();
-    method public int getVibrationSource();
-    method @Nullable public android.net.Uri getVibrationUri();
-    method public static boolean isRingtoneSelectionUri(@Nullable android.net.Uri);
-    method @NonNull public android.net.Uri toUri();
-    field public static final String DEFAULT_SELECTION_URI_STRING = "content://media/ringtone";
-    field public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3; // 0x3
-    field public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1; // 0x1
-    field public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2; // 0x2
-    field public static final int SOUND_SOURCE_OFF = 1; // 0x1
-    field public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
-    field public static final int SOUND_SOURCE_UNSPECIFIED = 0; // 0x0
-    field public static final int SOUND_SOURCE_URI = 2; // 0x2
-    field public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; // 0x4
-    field public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; // 0xa
-    field public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11; // 0xb
-    field public static final int VIBRATION_SOURCE_OFF = 1; // 0x1
-    field public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
-    field public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; // 0x0
-    field public static final int VIBRATION_SOURCE_URI = 2; // 0x2
-  }
-
-  @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public static final class RingtoneSelection.Builder {
-    ctor public RingtoneSelection.Builder();
-    ctor public RingtoneSelection.Builder(@NonNull android.media.RingtoneSelection);
-    method @NonNull public android.media.RingtoneSelection build();
-    method @NonNull public android.media.RingtoneSelection.Builder setSoundSource(int);
-    method @NonNull public android.media.RingtoneSelection.Builder setSoundSource(@NonNull android.net.Uri);
-    method @NonNull public android.media.RingtoneSelection.Builder setVibrationSource(int);
-    method @NonNull public android.media.RingtoneSelection.Builder setVibrationSource(@NonNull android.net.Uri);
-  }
-
   public static final class VolumeShaper.Configuration.Builder {
     method @NonNull public android.media.VolumeShaper.Configuration.Builder setOptionFlags(int);
   }
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 658ddbf..685ea63 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -2055,14 +2055,8 @@
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
 UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
-UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_SYSTEM_DEFAULT:
-    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_SYSTEM_DEFAULT
-UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_UNSPECIFIED:
-    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_UNSPECIFIED
 UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_DEFAULT:
-    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_DEFAULT
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
@@ -2073,10 +2067,6 @@
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_SYSTEM_DEFAULT:
-    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_SYSTEM_DEFAULT
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_UNSPECIFIED:
-    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_UNSPECIFIED
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
 UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 57c67be..63cafdc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -9601,9 +9601,9 @@
      * Specifies whether the activities below this one in the task can also start other activities
      * or finish the task.
      * <p>
-     * Starting from Target SDK Level {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, apps
-     * are blocked from starting new activities or finishing their task unless the top activity of
-     * such task belong to the same UID for security reasons.
+     * Starting from Target SDK Level {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, apps
+     * may be blocked from starting new activities or finishing their task unless the top activity
+     * of such task belong to the same UID for security reasons.
      * <p>
      * Setting this flag to {@code true} will allow the launching app to ignore the restriction if
      * this activity is on top. Apps matching the UID of this activity are always exempt.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 083705b..b25ebf6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -14071,7 +14071,7 @@
     public void setAuditLogEnabled(boolean enabled) {
         throwIfParentInstance("setAuditLogEnabled");
         try {
-            mService.setAuditLogEnabled(mContext.getPackageName(), true);
+            mService.setAuditLogEnabled(mContext.getPackageName(), enabled);
         } catch (RemoteException re) {
             re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 48ea846..6317725 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -40,6 +40,7 @@
 public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
 
     private Configuration mConfiguration;
+    private ActivityWindowInfo mActivityWindowInfo;
 
     @Override
     public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -55,8 +56,7 @@
         // TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
         client.handleActivityConfigurationChanged(r, mConfiguration, INVALID_DISPLAY,
-                // TODO(b/287582673): add ActivityWindowInfo
-                new ActivityWindowInfo());
+                mActivityWindowInfo);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
@@ -73,7 +73,7 @@
     /** Obtain an instance initialized with provided params. */
     @NonNull
     public static ActivityConfigurationChangeItem obtain(@NonNull IBinder activityToken,
-            @NonNull Configuration config) {
+            @NonNull Configuration config, @NonNull ActivityWindowInfo activityWindowInfo) {
         ActivityConfigurationChangeItem instance =
                 ObjectPool.obtain(ActivityConfigurationChangeItem.class);
         if (instance == null) {
@@ -81,6 +81,7 @@
         }
         instance.setActivityToken(activityToken);
         instance.mConfiguration = new Configuration(config);
+        instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
 
         return instance;
     }
@@ -89,6 +90,7 @@
     public void recycle() {
         super.recycle();
         mConfiguration = null;
+        mActivityWindowInfo = null;
         ObjectPool.recycle(this);
     }
 
@@ -100,12 +102,14 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeTypedObject(mConfiguration, flags);
+        dest.writeTypedObject(mActivityWindowInfo, flags);
     }
 
     /** Read from Parcel. */
     private ActivityConfigurationChangeItem(@NonNull Parcel in) {
         super(in);
         mConfiguration = in.readTypedObject(Configuration.CREATOR);
+        mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
     }
 
     public static final @NonNull Creator<ActivityConfigurationChangeItem> CREATOR =
@@ -128,7 +132,8 @@
             return false;
         }
         final ActivityConfigurationChangeItem other = (ActivityConfigurationChangeItem) o;
-        return Objects.equals(mConfiguration, other.mConfiguration);
+        return Objects.equals(mConfiguration, other.mConfiguration)
+                && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo);
     }
 
     @Override
@@ -136,12 +141,14 @@
         int result = 17;
         result = 31 * result + super.hashCode();
         result = 31 * result + Objects.hashCode(mConfiguration);
+        result = 31 * result + Objects.hashCode(mActivityWindowInfo);
         return result;
     }
 
     @Override
     public String toString() {
         return "ActivityConfigurationChange{" + super.toString()
-                + ",config=" + mConfiguration + "}";
+                + ",config=" + mConfiguration
+                + ",activityWindowInfo=" + mActivityWindowInfo + "}";
     }
 }
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index ecffe9e..faa2c70 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -392,8 +392,6 @@
             return;
         }
 
-        Log.i(TAG, walFile.getAbsolutePath() + " " + size + " bytes: Bigger than "
-                + threshold + "; truncating");
         try {
             executeForString("PRAGMA wal_checkpoint(TRUNCATE)", null, null);
             mConfiguration.shouldTruncateWalFile = false;
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 5e523c0..78c8954 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -377,8 +377,7 @@
                     if (writable) {
                         throw ex;
                     }
-                    Log.e(TAG, "Couldn't open " + mName
-                            + " for writing (will try read-only):", ex);
+                    Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
                     params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
                     db = SQLiteDatabase.openDatabase(filePath, params);
                 }
@@ -425,11 +424,6 @@
             }
 
             onOpen(db);
-
-            if (db.isReadOnly()) {
-                Log.w(TAG, "Opened " + mName + " in read-only mode");
-            }
-
             mDatabase = db;
             return db;
         } finally {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 04e3dab..7119730 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -772,6 +772,16 @@
                     "CameraDeviceSetup is not supported for Camera ID: " + cameraId);
         }
 
+        return getCameraDeviceSetupUnsafe(cameraId);
+
+    }
+
+    /**
+     * Creates and returns a {@link CameraDeviceSetup} instance without any error checking. To
+     * be used (carefully) by callers who are sure that CameraDeviceSetup instance can be legally
+     * created and don't want to pay the latency cost of calling {@link #getCameraDeviceSetup}.
+     */
+    private CameraDevice.CameraDeviceSetup getCameraDeviceSetupUnsafe(@NonNull String cameraId) {
         return new CameraDeviceSetupImpl(cameraId, /*cameraManager=*/ this, mContext);
     }
 
@@ -857,8 +867,9 @@
 
             ICameraDeviceUser cameraUser = null;
             CameraDevice.CameraDeviceSetup cameraDeviceSetup = null;
-            if (Flags.cameraDeviceSetup() && isCameraDeviceSetupSupported(cameraId)) {
-                cameraDeviceSetup = getCameraDeviceSetup(cameraId);
+            if (Flags.cameraDeviceSetup()
+                    && CameraDeviceSetupImpl.isCameraDeviceSetupSupported(characteristics)) {
+                cameraDeviceSetup = getCameraDeviceSetupUnsafe(cameraId);
             }
 
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 95526a8..8aacd5e 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1379,7 +1379,6 @@
                     mSurfaceType != other.mSurfaceType ||
                     mIsDeferredConfig != other.mIsDeferredConfig ||
                     mIsShared != other.mIsShared ||
-                    mConfiguredFormat != other.mConfiguredFormat ||
                     mConfiguredDataspace != other.mConfiguredDataspace ||
                     mConfiguredGenerationId != other.mConfiguredGenerationId ||
                     !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 63ae28f..2a11835 100644
--- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -20,3 +20,10 @@
     description: "Enable reporting USB data compliance warnings from HAL when set"
     bug: "296119135"
 }
+
+flag {
+    name: "enable_usb_data_signal_staking"
+    namespace: "preload_safety"
+    description: "Enables signal API with staking"
+    bug: "296119135"
+}
\ No newline at end of file
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 3950c25..2fe115f 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -623,14 +623,14 @@
                 // Message is to be inserted at tail or middle of queue. Usually we don't have to
                 // wake up the event queue unless there is a barrier at the head of the queue and
                 // the message is the earliest asynchronous message in the queue.
-                //
+                needWake = mBlocked && p.target == null && msg.isAsynchronous();
+
                 // For readability, we split this portion of the function into two blocks based on
                 // whether tail tracking is enabled. This has a minor implication for the case
                 // where tail tracking is disabled. See the comment below.
                 if (Flags.messageQueueTailTracking()) {
-                    needWake = mBlocked && p.target == null && msg.isAsynchronous()
-                        && mAsyncMessageCount == 0;
                     if (when >= mLast.when) {
+                        needWake = needWake && mAsyncMessageCount == 0;
                         msg.next = null;
                         mLast.next = msg;
                         mLast = msg;
@@ -643,6 +643,9 @@
                             if (p == null || when < p.when) {
                                 break;
                             }
+                            if (needWake && p.isAsynchronous()) {
+                                needWake = false;
+                            }
                         }
                         if (p == null) {
                             /* Inserting at tail of queue */
@@ -652,7 +655,6 @@
                         prev.next = msg;
                     }
                 } else {
-                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                     Message prev;
                     for (;;) {
                         prev = p;
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index abfa4e3..d9400ac 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -1,4 +1,5 @@
 package: "android.os"
+container: "system"
 
 flag {
     name: "android_os_build_vanilla_ice_cream"
@@ -40,6 +41,7 @@
     namespace: "profile_experiences"
     description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
     bug: "299069460"
+    is_exported: true
 }
 
 flag {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 8495f37..fa9f03d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -247,8 +247,6 @@
 
     private final LegacyPermissionManager mLegacyPermissionManager;
 
-    private final VirtualDeviceManager mVirtualDeviceManager;
-
     private final ArrayMap<PackageManager.OnPermissionsChangedListener,
             IOnPermissionsChangeListener> mPermissionListeners = new ArrayMap<>();
     private PermissionUsageHelper mUsageHelper;
@@ -269,7 +267,6 @@
         mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
                 "permissionmgr"));
         mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
-        mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class);
     }
 
     /**
@@ -1918,14 +1915,18 @@
         if (deviceId == Context.DEVICE_ID_DEFAULT) {
             persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
         } else if (android.companion.virtual.flags.Flags.vdmPublicApis()) {
-            VirtualDevice virtualDevice = mVirtualDeviceManager.getVirtualDevice(deviceId);
-            if (virtualDevice == null) {
-                Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
-                return null;
-            }
-            persistentDeviceId = virtualDevice.getPersistentDeviceId();
-            if (persistentDeviceId == null) {
-                Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
+            VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
+                    VirtualDeviceManager.class);
+            if (virtualDeviceManager != null) {
+                VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
+                if (virtualDevice == null) {
+                    Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
+                    return null;
+                }
+                persistentDeviceId = virtualDevice.getPersistentDeviceId();
+                if (persistentDeviceId == null) {
+                    Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
+                }
             }
         } else {
             Slog.e(LOG_TAG, "vdmPublicApis flag is not enabled when device Id " + deviceId
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index c13dd36..c03dc71 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -406,6 +406,7 @@
          * Builder for the add-call parameters.
          */
         public static final class AddCallParametersBuilder {
+            public static final int MAX_NUMBER_OF_CHARACTERS = 256;
             private CallerInfo mCallerInfo;
             private String mNumber;
             private String mPostDialDigits;
@@ -431,7 +432,7 @@
             private Uri mPictureUri;
             private int mIsPhoneAccountMigrationPending;
             private boolean mIsBusinessCall;
-            private String mBusinessName;
+            private String mAssertedDisplayName;
 
             /**
              * @param callerInfo the CallerInfo object to get the target contact from.
@@ -659,11 +660,18 @@
             }
 
             /**
-             * @param businessName should be set if the caller is a business call
+             * @param assertedDisplayName the asserted display name associated with the business
+             *                            call
+             * @throws IllegalArgumentException if the assertedDisplayName is over 256 characters
              */
             @FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER)
-            public @NonNull AddCallParametersBuilder setBusinessName(String businessName) {
-                mBusinessName = businessName;
+            public @NonNull AddCallParametersBuilder setAssertedDisplayName(
+                    String assertedDisplayName) {
+                if (assertedDisplayName.length() > MAX_NUMBER_OF_CHARACTERS) {
+                    throw new IllegalArgumentException("assertedDisplayName exceeds the character"
+                            + " limit of " + MAX_NUMBER_OF_CHARACTERS + ".");
+                }
+                mAssertedDisplayName = assertedDisplayName;
                 return this;
             }
 
@@ -678,7 +686,7 @@
                             mCallBlockReason,
                             mCallScreeningAppName, mCallScreeningComponentName, mMissedReason,
                             mPriority, mSubject, mLatitude, mLongitude, mPictureUri,
-                            mIsPhoneAccountMigrationPending, mIsBusinessCall, mBusinessName);
+                            mIsPhoneAccountMigrationPending, mIsBusinessCall, mAssertedDisplayName);
                 } else {
                     return new AddCallParams(mCallerInfo, mNumber, mPostDialDigits, mViaNumber,
                             mPresentation, mCallType, mFeatures, mAccountHandle, mStart, mDuration,
@@ -716,7 +724,7 @@
         private Uri mPictureUri;
         private int mIsPhoneAccountMigrationPending;
         private boolean mIsBusinessCall;
-        private String mBusinessName;
+        private String mAssertedDisplayName;
 
         private AddCallParams(CallerInfo callerInfo, String number, String postDialDigits,
                 String viaNumber, int presentation, int callType, int features,
@@ -761,7 +769,8 @@
                 CharSequence callScreeningAppName, String callScreeningComponentName,
                 long missedReason,
                 int priority, String subject, double latitude, double longitude, Uri pictureUri,
-                int isPhoneAccountMigrationPending, boolean isBusinessCall, String businessName) {
+                int isPhoneAccountMigrationPending, boolean isBusinessCall,
+                String assertedDisplayName) {
             mCallerInfo = callerInfo;
             mNumber = number;
             mPostDialDigits = postDialDigits;
@@ -787,7 +796,7 @@
             mPictureUri = pictureUri;
             mIsPhoneAccountMigrationPending = isPhoneAccountMigrationPending;
             mIsBusinessCall = isBusinessCall;
-            mBusinessName = businessName;
+            mAssertedDisplayName = assertedDisplayName;
         }
 
     }
@@ -1833,7 +1842,7 @@
             values.put(IS_PHONE_ACCOUNT_MIGRATION_PENDING, params.mIsPhoneAccountMigrationPending);
             if (Flags.businessCallComposer()) {
                 values.put(IS_BUSINESS_CALL, Integer.valueOf(params.mIsBusinessCall ? 1 : 0));
-                values.put(ASSERTED_DISPLAY_NAME, params.mBusinessName);
+                values.put(ASSERTED_DISPLAY_NAME, params.mAssertedDisplayName);
             }
             if ((params.mCallerInfo != null) && (params.mCallerInfo.getContactId() > 0)) {
                 // Update usage information for the number associated with the contact ID.
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index d8c44ad..f626149 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -92,7 +92,6 @@
     private Executor mExecutor;
     private Context mContext;
 
-    private final KeyStore mKeyStore = KeyStore.getInstance();
     private AndroidProtectedConfirmation mProtectedConfirmation;
 
     private AndroidProtectedConfirmation getService() {
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index f1054ec..c171c1b 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -26,7 +26,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
-import android.security.KeyStore;
 import android.security.KeyStore2;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.security.keystore2.AndroidKeyStoreProvider;
@@ -272,11 +271,9 @@
     public static final int ERROR_KEY_NOT_FOUND = 30;
 
     private final ILockSettings mBinder;
-    private final KeyStore mKeyStore;
 
-    private RecoveryController(ILockSettings binder, KeyStore keystore) {
+    private RecoveryController(ILockSettings binder) {
         mBinder = binder;
-        mKeyStore = keystore;
     }
 
     /**
@@ -296,7 +293,7 @@
         // lockSettings may be null.
         ILockSettings lockSettings =
                 ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
-        return new RecoveryController(lockSettings, KeyStore.getInstance());
+        return new RecoveryController(lockSettings);
     }
 
     /**
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index b1bca96..cedba85 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -8,7 +8,7 @@
 }
 
 flag {
-    name: "perfetto_protolog"
+    name: "perfetto_protolog_tracing"
     namespace: "windowing_tools"
     description: "Migrate protolog to Perfetto"
     bug: "276432490"
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 00657ea..66655fc 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -188,8 +188,7 @@
                 // check whether the stylus we are tracking goes up.
                 if (mState != null) {
                     mState.mShouldInitHandwriting = false;
-                    if (!mState.mHasInitiatedHandwriting
-                            && !mState.mHasPreparedHandwritingDelegation) {
+                    if (!mState.mHandled) {
                         // The user just did a click, long click or another stylus gesture,
                         // show hover icon again for the connected view.
                         mShowHoverIconForConnectedView = true;
@@ -204,16 +203,14 @@
                 // Either we've already tried to initiate handwriting, or the ongoing MotionEvent
                 // sequence is considered to be tap, long-click or other gestures.
                 if (!mState.mShouldInitHandwriting || mState.mExceedHandwritingSlop) {
-                    return mState.mHasInitiatedHandwriting
-                            || mState.mHasPreparedHandwritingDelegation;
+                    return mState.mHandled;
                 }
 
                 final long timeElapsed =
                         motionEvent.getEventTime() - mState.mStylusDownTimeInMillis;
                 if (timeElapsed > mHandwritingTimeoutInMillis) {
                     mState.mShouldInitHandwriting = false;
-                    return mState.mHasInitiatedHandwriting
-                            || mState.mHasPreparedHandwritingDelegation;
+                    return mState.mHandled;
                 }
 
                 final int pointerIndex = motionEvent.findPointerIndex(mState.mStylusPointerId);
@@ -223,7 +220,7 @@
                     mState.mExceedHandwritingSlop = true;
                     View candidateView = findBestCandidateView(mState.mStylusDownX,
                             mState.mStylusDownY, /* isHover */ false);
-                    if (candidateView != null) {
+                    if (candidateView != null && candidateView.isEnabled()) {
                         if (candidateView == getConnectedOrFocusedView()) {
                             if (!mInitiateWithoutConnection && !candidateView.hasFocus()) {
                                 requestFocusWithoutReveal(candidateView);
@@ -246,7 +243,7 @@
                         }
                     }
                 }
-                return mState.mHasInitiatedHandwriting || mState.mHasPreparedHandwritingDelegation;
+                return mState.mHandled;
         }
         return false;
     }
@@ -382,7 +379,7 @@
     @VisibleForTesting
     public void startHandwriting(@NonNull View view) {
         mImm.startStylusHandwriting(view);
-        mState.mHasInitiatedHandwriting = true;
+        mState.mHandled = true;
         mState.mShouldInitHandwriting = false;
         mShowHoverIconForConnectedView = false;
         if (view instanceof TextView) {
@@ -402,13 +399,12 @@
             mImm.startConnectionlessStylusHandwritingForDelegation(
                     view, getCursorAnchorInfoForConnectionless(view), delegatePackageName,
                     view::post, new DelegationCallback(view, delegatePackageName));
-            mState.mHasInitiatedHandwriting = true;
             mState.mShouldInitHandwriting = false;
         } else {
             mImm.prepareStylusHandwritingDelegation(view, delegatePackageName);
             view.getHandwritingDelegatorCallback().run();
-            mState.mHasPreparedHandwritingDelegation = true;
         }
+        mState.mHandled = true;
     }
 
     /**
@@ -455,7 +451,7 @@
 
     private void onDelegationAccepted(View view) {
         if (mState != null) {
-            mState.mHasInitiatedHandwriting = true;
+            mState.mHandled = true;
             mState.mShouldInitHandwriting = false;
         }
         if (view instanceof TextView) {
@@ -795,12 +791,12 @@
          * This boolean will be set to false, and it won't request to start handwriting.
          */
         private boolean mShouldInitHandwriting;
-        /**
-         * Whether handwriting mode has already been initiated for the current MotionEvent sequence.
-         */
-        private boolean mHasInitiatedHandwriting;
 
-        private boolean mHasPreparedHandwritingDelegation;
+        /**
+         * Whether the current MotionEvent sequence has been handled by the handwriting initiator,
+         * either by initiating handwriting mode, or by preparing handwriting delegation.
+         */
+        private boolean mHandled;
 
         /**
          * Whether the current ongoing stylus MotionEvent sequence already exceeds the
@@ -838,8 +834,7 @@
             mStylusDownY = motionEvent.getY(actionIndex);
 
             mShouldInitHandwriting = true;
-            mHasInitiatedHandwriting = false;
-            mHasPreparedHandwritingDelegation = false;
+            mHandled = false;
             mExceedHandwritingSlop = false;
         }
     }
@@ -1052,8 +1047,6 @@
                     // Fall back to the old delegation flow
                     mImm.prepareStylusHandwritingDelegation(mView, mDelegatePackageName);
                     mView.getHandwritingDelegatorCallback().run();
-                    mState.mHasInitiatedHandwriting = false;
-                    mState.mHasPreparedHandwritingDelegation = true;
                     break;
             }
         }
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index f5f4fd9..9099f98 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -428,13 +428,11 @@
 
     private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources,
             VectorDrawable vectorDrawable) {
-        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
-                vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-        // BitmapDrawables and Bitmap have a default density of DisplayMetrics.DENSITY_DEVICE,
-        // (which is deprecated in favor of DENSITY_DEVICE_STABLE/resources.densityDpi). In
-        // rare cases when device density differs from the resource density, the bitmap will
-        // scale as the BitmapDrawable is created. Avoid by explicitly setting density here.
-        bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
+        // Ensure we pass the display metrics into the Bitmap constructor so that it is initialized
+        // with the correct density.
+        Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(),
+                vectorDrawable.getIntrinsicWidth(),
+                vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */);
         Canvas canvas = new Canvas(bitmap);
         vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
         vectorDrawable.draw(canvas);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 042af1f..a2faabc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16815,10 +16815,15 @@
             mAttachInfo.mViewRootImpl.getWindowVisibleDisplayFrame(outRect);
             return;
         }
-        // The view is not attached to a display so we don't have a context.
-        // Make a best guess about the display size.
-        Display d = DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
-        d.getRectSize(outRect);
+        // TODO (b/327559224): Refine the behavior to better reflect the window environment with API
+        //  doc updates.
+        final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+        final WindowMetrics metrics = windowManager.getMaximumWindowMetrics();
+        final Insets insets = metrics.getWindowInsets().getInsets(
+                WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout());
+        outRect.set(metrics.getBounds());
+        outRect.inset(insets);
+        outRect.offsetTo(0, 0);
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fcc8344..68940d6 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1153,6 +1153,7 @@
                     }
                     final boolean startInput;
                     synchronized (mH) {
+                        mImeDispatcher.clear();
                         if (getBindSequenceLocked() != sequence) {
                             return;
                         }
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 51890ec..94c72c6 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -46,5 +46,5 @@
     name: "bal_respect_app_switch_state_when_check_bound_by_foreground_uid"
     namespace: "responsible_apis"
     description: "Prevent BAL based on it is bound by foreground Uid but the app switch is stopped."
-    bug: "171459802"
+    bug: "283801068"
 }
diff --git a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
index d9ac5a9..30de546 100644
--- a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
@@ -61,7 +61,7 @@
     private static final int PER_CHUNK_SIZE = 1024;
     private static final String TAG = "ProtoLog";
     private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
-    static final String PROTOLOG_VERSION = "1.0.0";
+    static final String PROTOLOG_VERSION = "2.0.0";
     private static final int DEFAULT_PER_CHUNK_SIZE = 0;
 
     private final File mLogFile;
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 78bed94..8965385 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -98,7 +98,7 @@
      */
     public static synchronized IProtoLog getSingleInstance() {
         if (sServiceInstance == null) {
-            if (android.tracing.Flags.perfettoProtolog()) {
+            if (android.tracing.Flags.perfettoProtologTracing()) {
                 sServiceInstance =
                         new PerfettoProtoLogImpl(sViewerConfigPath);
             } else {
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index c87b7cd..5e3e1b0 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -84,7 +84,7 @@
          CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
          If 0, the device always switch to the higher score SIM.
          If < 0, the network type and signal strength based auto switch is disabled. -->
-    <integer name="auto_data_switch_score_tolerance">4000</integer>
+    <integer name="auto_data_switch_score_tolerance">-1</integer>
     <java-symbol type="integer" name="auto_data_switch_score_tolerance" />
 
     <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 64c17bd..d115bf3 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -247,7 +247,7 @@
             newConfig.smallestScreenWidthDp++;
             transaction = newTransaction(activityThread);
             transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
-                    activity.getActivityToken(), newConfig));
+                    activity.getActivityToken(), newConfig, new ActivityWindowInfo()));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
@@ -455,11 +455,11 @@
 
         transaction = newTransaction(activityThread);
         transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
-                activity.getActivityToken(), activityConfigLandscape));
+                activity.getActivityToken(), activityConfigLandscape, new ActivityWindowInfo()));
         transaction.addTransactionItem(ConfigurationChangeItem.obtain(
                 processConfigPortrait, DEVICE_ID_INVALID));
         transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
-                activity.getActivityToken(), activityConfigPortrait));
+                activity.getActivityToken(), activityConfigPortrait, new ActivityWindowInfo()));
         appThread.scheduleTransaction(transaction);
 
         activity.mTestLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
@@ -883,7 +883,7 @@
     private static ClientTransaction newActivityConfigTransaction(@NonNull Activity activity,
             @NonNull Configuration config) {
         final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
-                activity.getActivityToken(), config);
+                activity.getActivityToken(), config, new ActivityWindowInfo());
 
         final ClientTransaction transaction = newTransaction(activity);
         transaction.addTransactionItem(item);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
index 85a1b4e..4db5d1b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -107,7 +107,7 @@
     @Test
     public void testActivityConfigurationChangeItem_getContextToUpdate() {
         final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
-                .obtain(mActivityToken, mConfiguration);
+                .obtain(mActivityToken, mConfiguration, new ActivityWindowInfo());
         final Context context = item.getContextToUpdate(mHandler);
 
         assertEquals(mActivity, context);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 906558f..31ea675 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -82,7 +82,8 @@
 
     @Test
     public void testRecycleActivityConfigurationChangeItem() {
-        testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config()));
+        testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config(),
+                new ActivityWindowInfo()));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index dbb090f..75347bf 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -95,8 +95,11 @@
     @Test
     public void testActivityConfigChange() {
         // Write to parcel
+        final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+        activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000),
+                new Rect(0, 0, 500, 500));
         ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
-                mActivityToken, config());
+                mActivityToken, config(), activityWindowInfo);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
@@ -300,7 +303,7 @@
         // Write to parcel
         NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
-                mActivityToken, config());
+                mActivityToken, config(), new ActivityWindowInfo());
 
         StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
                 78 /* configChanges */);
@@ -327,7 +330,7 @@
         // Write to parcel
         NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
-                mActivityToken, config());
+                mActivityToken, config(), new ActivityWindowInfo());
 
         ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         transaction.addTransactionItem(callback1);
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 51eb41c..b60b806f 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -472,6 +472,26 @@
     }
 
     @Test
+    public void onTouchEvent_doesNothing_viewDisabled() {
+        mTestView1.setEnabled(false);
+
+        final int x1 = (sHwArea1.left + sHwArea1.right) / 2;
+        final int y1 = (sHwArea1.top + sHwArea1.bottom) / 2;
+        MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+        final int x2 = x1 + mHandwritingSlop * 2;
+        final int y2 = y1;
+
+        MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+        mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+        // HandwritingInitiator will not request focus if it is disabled.
+        verify(mTestView1, never()).requestFocus();
+        verify(mHandwritingInitiator, never()).startHandwriting(mTestView1);
+    }
+
+    @Test
     public void onTouchEvent_focusView_inputConnectionAlreadyBuilt_stylusMoveOnce_withinHWArea() {
         if (!mInitiateWithoutConnection) {
             mHandwritingInitiator.onInputConnectionCreated(mTestView1);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index e8c7a53..0231d3a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1,5 +1,5 @@
 {
-  "version": "1.0.0",
+  "version": "2.0.0",
   "messages": {
     "7286191062634870297": {
       "message": "Binding proc %s with config %s",
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 62fe54f..ef03d3a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -19,9 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.security.KeyStore;
 
 import java.io.IOException;
+import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
@@ -47,13 +47,13 @@
     }
 
     /**
-     * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+     * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
      * primitive.
      *
      * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
      *
-     * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
-     *         is not in progress.
+     * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
+     *         KeyStore operation is not in progress.
      *
      * @throws IllegalArgumentException if the provided primitive is not supported or is not backed
      *         by AndroidKeyStore provider.
@@ -67,10 +67,10 @@
     }
 
     /**
-     * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
-     * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
-     * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN)
-     * all of which are system.
+     * Returns an {@code AndroidKeyStore} {@link KeyStore} of the specified UID. The {@code
+     * KeyStore} contains keys and certificates owned by that UID. Such cross-UID access is
+     * permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN) all of which
+     * are system.
      *
      * <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is
      * no need to invoke {@code load} on it.
@@ -84,12 +84,12 @@
      */
     @SystemApi
     @NonNull
-    public static java.security.KeyStore getKeyStoreForUid(int uid)
+    public static KeyStore getKeyStoreForUid(int uid)
             throws KeyStoreException, NoSuchProviderException {
-        final java.security.KeyStore.LoadStoreParameter loadParameter =
+        final KeyStore.LoadStoreParameter loadParameter =
                 new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
                         KeyProperties.legacyUidToNamespace(uid));
-        java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME);
+        KeyStore result = KeyStore.getInstance(PROVIDER_NAME);
         try {
             result.load(loadParameter);
         } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 244fe30..7aecfd8 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -910,7 +910,7 @@
     /**
      * Returns whether this key is critical to the device encryption flow.
      *
-     * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+     * @see Builder#setCriticalToDeviceEncryption(boolean)
      * @hide
      */
     public boolean isCriticalToDeviceEncryption() {
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 2495d1a..31b4a5e 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -569,7 +569,7 @@
     /**
      * Return whether this key is critical to the device encryption flow.
      *
-     * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+     * @see Builder#setCriticalToDeviceEncryption(boolean)
      * @hide
      */
     public boolean isCriticalToDeviceEncryption() {
@@ -1105,9 +1105,10 @@
          * Set whether this key is critical to the device encryption flow
          *
          * This is a special flag only available to system servers to indicate the current key
-         * is part of the device encryption flow.
+         * is part of the device encryption flow. Setting this flag causes the key to not
+         * be cryptographically bound to the LSKF even if the key is otherwise authentication
+         * bound.
          *
-         * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
          * @hide
          */
         public Builder setCriticalToDeviceEncryption(boolean critical) {
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
index 2c709ae..c42c9e4 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
@@ -16,18 +16,16 @@
 
 package android.security.keystore;
 
-import android.security.KeyStore;
-
 /**
- * Cryptographic operation backed by {@link KeyStore}.
+ * Cryptographic operation backed by Android KeyStore.
  *
  * @hide
  */
 public interface KeyStoreCryptoOperation {
     /**
-     * Gets the KeyStore operation handle of this crypto operation.
+     * Gets the Android KeyStore operation handle of this crypto operation.
      *
-     * @return handle or {@code 0} if the KeyStore operation is not in progress.
+     * @return handle or {@code 0} if the Android KeyStore operation is not in progress.
      */
     long getOperationHandle();
 }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
index a8dd7f3..8eca67f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
@@ -16,7 +16,6 @@
 
 package android.security.keystore2;
 
-import android.security.KeyStore;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyInfo;
 
@@ -39,8 +38,6 @@
  */
 public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
 
-    private final KeyStore mKeyStore = KeyStore.getInstance();
-
     @Override
     protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
             throws InvalidKeySpecException {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d204f13..99100de 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -17,7 +17,6 @@
 package android.security.keystore2;
 
 import android.annotation.NonNull;
-import android.security.KeyStore;
 import android.security.KeyStore2;
 import android.security.KeyStoreSecurityLevel;
 import android.security.keymaster.KeymasterDefs;
@@ -161,13 +160,13 @@
     }
 
     /**
-     * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+     * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
      * primitive.
      *
      * <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}.
      *
-     * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
-     *         is not in progress.
+     * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
+     *         KeyStore operation is not in progress.
      *
      * @throws IllegalArgumentException if the provided primitive is not supported or is not backed
      *         by AndroidKeyStore provider.
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 2682eb6..2223091 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.security.GateKeeper;
-import android.security.KeyStore;
 import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyGenParameterSpec;
@@ -46,8 +45,6 @@
  */
 public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
 
-    private final KeyStore mKeyStore = KeyStore.getInstance();
-
     @Override
     protected KeySpec engineGetKeySpec(SecretKey key,
             @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
index 07d6a69..5bd98bc 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
@@ -16,12 +16,11 @@
 
 package android.security.keystore2;
 
-import android.security.KeyStore;
 import android.security.KeyStoreException;
 
 /**
- * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
- * {@code update} and {@code finish} operations.
+ * Helper for streaming a crypto operation's input and output via KeyStore service's {@code update}
+ * and {@code finish} operations.
  *
  * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
  * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 08b7bb8..39cface 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -201,7 +201,7 @@
             return null;
         }
         return new SplitInfo(primaryActivityStack, secondaryActivityStack,
-                mCurrentSplitAttributes, mToken);
+                mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken));
     }
 
     static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index ae3a854..038d008 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -35,6 +35,7 @@
 import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
 import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
 
+import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_ACTIVITY_STACK_TOKEN;
 import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
 import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
 import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -112,10 +113,6 @@
     static final boolean ENABLE_SHELL_TRANSITIONS =
             SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
 
-    // TODO(b/295993745): remove after prebuilt library is updated.
-    private static final String KEY_ACTIVITY_STACK_TOKEN =
-            "androidx.window.extensions.embedding.ActivityStackToken";
-
     @VisibleForTesting
     @GuardedBy("mLock")
     final SplitPresenter mPresenter;
@@ -554,7 +551,7 @@
     }
 
     @Override
-    public void updateActivityStackAttributes(@NonNull IBinder activityStackToken,
+    public void updateActivityStackAttributes(@NonNull ActivityStack.Token activityStackToken,
                                               @NonNull ActivityStackAttributes attributes) {
         if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
             return;
@@ -563,7 +560,7 @@
         Objects.requireNonNull(attributes);
 
         synchronized (mLock) {
-            final TaskFragmentContainer container = getContainer(activityStackToken);
+            final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken());
             if (container == null) {
                 Log.w(TAG, "Cannot find TaskFragmentContainer for token:" + activityStackToken);
                 return;
@@ -583,13 +580,14 @@
 
     @Override
     @Nullable
-    public ParentContainerInfo getParentContainerInfo(@NonNull IBinder activityStackToken) {
+    public ParentContainerInfo getParentContainerInfo(
+            @NonNull ActivityStack.Token activityStackToken) {
         if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
             return null;
         }
         Objects.requireNonNull(activityStackToken);
         synchronized (mLock) {
-            final TaskFragmentContainer container = getContainer(activityStackToken);
+            final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken());
             if (container == null) {
                 return null;
             }
@@ -601,7 +599,7 @@
 
     @Override
     @Nullable
-    public IBinder getActivityStackToken(@NonNull String tag) {
+    public ActivityStack.Token getActivityStackToken(@NonNull String tag) {
         if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
             return null;
         }
@@ -612,7 +610,8 @@
             if (taskFragmentContainer == null) {
                 return null;
             }
-            return taskFragmentContainer.getTaskFragmentToken();
+            return ActivityStack.Token.createFromBinder(taskFragmentContainer
+                    .getTaskFragmentToken());
         }
     }
 
@@ -2761,8 +2760,10 @@
             // TODO(b/232042367): Consolidate the activity create handling so that we can handle
             // cross-process the same as normal.
 
-            IBinder activityStackToken = options.getBinder(KEY_ACTIVITY_STACK_TOKEN);
-            if (activityStackToken != null) {
+            final Bundle bundle = options.getBundle(KEY_ACTIVITY_STACK_TOKEN);
+            if (bundle != null) {
+                final IBinder activityStackToken = ActivityStack.Token.readFromBundle(bundle)
+                        .getRawToken();
                 // Put activityStack token to #KEY_LAUNCH_TASK_FRAGMENT_TOKEN to launch the activity
                 // into the taskFragment associated with the token.
                 options.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN, activityStackToken);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 6fe8e50..a6bf99d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -367,7 +367,8 @@
         if (activities == null) {
             return null;
         }
-        return new ActivityStack(activities, isEmpty(), mToken, mOverlayTag);
+        return new ActivityStack(activities, isEmpty(),
+                ActivityStack.Token.createFromBinder(mToken), mOverlayTag);
     }
 
     /** Adds the activity that will be reparented to this container. */
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 34d43ad..28fbadb 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -399,7 +399,8 @@
                         new ActivityStackAttributes.Builder().build()));
 
         assertThrows(NullPointerException.class, () ->
-                mSplitController.updateActivityStackAttributes(new Binder(), null));
+                mSplitController.updateActivityStackAttributes(
+                        ActivityStack.Token.createFromBinder(new Binder()), null));
 
         verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
     }
@@ -408,7 +409,8 @@
     public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
         final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
                 mActivity.getTaskId());
-        mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+        mSplitController.updateActivityStackAttributes(
+                ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()),
                 new ActivityStackAttributes.Builder().build());
 
         verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
@@ -418,7 +420,8 @@
     public void testUpdateActivityStackAttributes_notOverlay_earlyReturn() {
         final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
 
-        mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+        mSplitController.updateActivityStackAttributes(
+                ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()),
                 new ActivityStackAttributes.Builder().build());
 
         verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
@@ -431,7 +434,8 @@
         final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder().build();
         final IBinder token = container.getTaskFragmentToken();
 
-        mSplitController.updateActivityStackAttributes(token, attrs);
+        mSplitController.updateActivityStackAttributes(ActivityStack.Token.createFromBinder(token),
+                attrs);
 
         verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs),
                 any());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index b60943a..00f8b59 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1437,7 +1437,7 @@
     @Test
     public void testUpdateSplitAttributes_nullParams_throwException() {
         assertThrows(NullPointerException.class,
-                () -> mSplitController.updateSplitAttributes(null, SPLIT_ATTRIBUTES));
+                () -> mSplitController.updateSplitAttributes((IBinder) null, SPLIT_ATTRIBUTES));
 
         final SplitContainer splitContainer = mock(SplitContainer.class);
         final IBinder token = new Binder();
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index d1b1af3..490f088 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -16,6 +16,7 @@
   -->
 <com.android.wm.shell.windowdecor.WindowDecorLinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/desktop_mode_caption"
     android:layout_width="match_parent"
@@ -27,6 +28,8 @@
         android:id="@+id/open_menu_button"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
+        android:tint="?androidprv:attr/materialColorOnSurface"
+        android:background="?android:selectableItemBackground"
         android:orientation="horizontal"
         android:clickable="true"
         android:focusable="true"
@@ -78,7 +81,9 @@
         android:id="@+id/maximize_button_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="end"/>
+        android:layout_gravity="end"
+        android:clickable="true"
+        android:focusable="true" />
 
     <ImageButton
         android:id="@+id/close_window"
@@ -86,9 +91,10 @@
         android:layout_height="40dp"
         android:padding="4dp"
         android:layout_marginEnd="8dp"
+        android:tint="?androidprv:attr/materialColorOnSurface"
+        android:background="?android:selectableItemBackgroundBorderless"
         android:contentDescription="@string/close_button_text"
         android:src="@drawable/decor_close_button_dark"
         android:scaleType="fitCenter"
-        android:gravity="end"
-        android:background="@null"/>
+        android:gravity="end"/>
 </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
index bb6efce..e0057fe 100644
--- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -14,7 +14,8 @@
   ~ limitations under the License.
   -->
 
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <ProgressBar
         android:id="@+id/progress_bar"
         style="?android:attr/progressBarStyleHorizontal"
@@ -30,7 +31,8 @@
         android:layout_height="40dp"
         android:padding="9dp"
         android:contentDescription="@string/maximize_button_text"
+        android:tint="?androidprv:attr/materialColorOnSurface"
+        android:background="?android:selectableItemBackgroundBorderless"
         android:src="@drawable/decor_desktop_mode_maximize_button_dark"
-        android:scaleType="fitCenter"
-        android:background="@drawable/rounded_button"/>
+        android:scaleType="fitCenter" />
 </merge>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
index 93893e3..ef9bf00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
@@ -51,7 +51,7 @@
         final ILogger logger = pw::println;
         switch (args[0]) {
             case "status": {
-                if (android.tracing.Flags.perfettoProtolog()) {
+                if (android.tracing.Flags.perfettoProtologTracing()) {
                     pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
                     return false;
                 }
@@ -59,7 +59,7 @@
                 return true;
             }
             case "start": {
-                if (android.tracing.Flags.perfettoProtolog()) {
+                if (android.tracing.Flags.perfettoProtologTracing()) {
                     pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
                     return false;
                 }
@@ -67,7 +67,7 @@
                 return true;
             }
             case "stop": {
-                if (android.tracing.Flags.perfettoProtolog()) {
+                if (android.tracing.Flags.perfettoProtologTracing()) {
                     pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
                     return false;
                 }
@@ -101,7 +101,7 @@
                 return mShellProtoLog.stopLoggingToLogcat(groups, logger) == 0;
             }
             case "save-for-bugreport": {
-                if (android.tracing.Flags.perfettoProtolog()) {
+                if (android.tracing.Flags.perfettoProtologTracing()) {
                     pw.println("(Deprecated) legacy command");
                     return false;
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 15350fb..96aaf02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1799,11 +1799,12 @@
         @Override
         public void removeBubble(Bubble removedBubble) {
             if (mLayerView != null) {
-                mLayerView.removeBubble(removedBubble);
-                if (!mBubbleData.hasBubbles() && !isStackExpanded()) {
-                    mLayerView.setVisibility(INVISIBLE);
-                    removeFromWindowManagerMaybe();
-                }
+                mLayerView.removeBubble(removedBubble, () -> {
+                    if (!mBubbleData.hasBubbles() && !isStackExpanded()) {
+                        mLayerView.setVisibility(INVISIBLE);
+                        removeFromWindowManagerMaybe();
+                    }
+                });
             }
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 78a41f7..42799d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -242,13 +242,17 @@
     }
 
     /** Removes the given {@code bubble}. */
-    public void removeBubble(Bubble bubble) {
+    public void removeBubble(Bubble bubble, Runnable endAction) {
+        Runnable cleanUp = () -> {
+            bubble.cleanupViews();
+            endAction.run();
+        };
         if (mBubbleData.getBubbles().isEmpty()) {
             // we're removing the last bubble. collapse the expanded view and cleanup bubble views
             // at the end.
-            collapse(bubble::cleanupViews);
+            collapse(cleanUp);
         } else {
-            bubble.cleanupViews();
+            cleanUp.run();
         }
     }
 
@@ -264,6 +268,9 @@
      */
     public void collapse(@Nullable Runnable endAction) {
         if (!mIsExpanded) {
+            if (endAction != null) {
+                endAction.run();
+            }
             return;
         }
         mIsExpanded = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index f801b0d..a87116e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -75,7 +75,6 @@
     private SurfaceControlViewHost mViewHost;
     private DividerHandleView mHandle;
     private DividerRoundedCorner mCorners;
-    private View mBackground;
     private int mTouchElevation;
 
     private VelocityTracker mVelocityTracker;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 2b10377..194eb47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -277,7 +277,7 @@
                 }
 
                 @Override
-                public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
+                public void onAnimationEnd(@NonNull Animator animation) {
                     mRunningAnimationCount--;
                     animT.remove(mScreenshot);
                     animT.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index e421356..1c54754 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -163,14 +163,14 @@
     /**
      * Adds a split pair. This call does not validate the taskIds, only that they are not the same.
      */
-    public void addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
+    public boolean addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
         if (taskId1 == taskId2) {
-            return;
+            return false;
         }
         if (mSplitTasks.get(taskId1, INVALID_TASK_ID) == taskId2
                 && mTaskSplitBoundsMap.get(taskId1).equals(splitBounds)) {
             // If the two tasks are already paired and the bounds are the same, then skip updating
-            return;
+            return false;
         }
         // Remove any previous pairs
         removeSplitPair(taskId1);
@@ -185,6 +185,7 @@
         notifyRecentTasksChanged();
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Add split pair: %d, %d, %s",
                 taskId1, taskId2, splitBounds);
+        return true;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index e52235f..64e26db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -16,11 +16,14 @@
 
 package com.android.wm.shell.splitscreen;
 
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+
 import android.content.Context;
 import android.view.SurfaceSession;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.SyncTransactionQueue;
@@ -50,6 +53,8 @@
 
     void activate(WindowContainerTransaction wct, boolean includingTopTask) {
         if (mIsActive) return;
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b",
+                includingTopTask);
 
         if (includingTopTask) {
             reparentTopTask(wct);
@@ -64,6 +69,8 @@
 
     void deactivate(WindowContainerTransaction wct, boolean toTop) {
         if (!mIsActive) return;
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s",
+                toTop, mRootTaskInfo);
         mIsActive = false;
 
         if (mRootTaskInfo == null) return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 9903113..f5fbae5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,12 +16,15 @@
 
 package com.android.wm.shell.splitscreen;
 
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+
 import android.app.ActivityManager;
 import android.content.Context;
 import android.view.SurfaceSession;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.SyncTransactionQueue;
@@ -47,6 +50,8 @@
     }
 
     boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
+                mChildrenTaskInfo.size(), toTop);
         if (mChildrenTaskInfo.size() == 0) return false;
         wct.reparentTasks(
                 mRootTaskInfo.token,
@@ -59,6 +64,8 @@
 
     boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
         final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
+                task != null);
         if (task == null) return false;
         wct.reparent(task.token, newParent, false /* onTop */);
         return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index b60e361..1d9fdeb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -25,6 +25,8 @@
 import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
 import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
 import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
@@ -101,6 +103,7 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
             @NonNull WindowContainerToken topRoot) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playAnimation: transition=%d", info.getDebugId());
         initTransition(transition, finishTransaction, finishCallback);
 
         final TransitSession pendingTransition = getPendingTransition(transition);
@@ -123,10 +126,12 @@
         playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot);
     }
 
-    /** Internal funcation of playAnimation. */
+    /** Internal function of playAnimation. */
     private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
             @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playInternalAnimation: transition=%d",
+                info.getDebugId());
         // Play some place-holder fade animations
         final boolean isEnter = isPendingEnter(transition);
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -220,6 +225,8 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull WindowContainerToken toTopRoot, @NonNull SplitDecorManager toTopDecor,
             @NonNull WindowContainerToken topRoot) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playDragDismissAnimation: transition=%d",
+                info.getDebugId());
         initTransition(transition, finishTransaction, finishCallback);
 
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -259,6 +266,7 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
             @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playResizeAnimation: transition=%d", info.getDebugId());
         initTransition(transition, finishTransaction, finishCallback);
 
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -312,13 +320,15 @@
     @Nullable
     private TransitSession getPendingTransition(IBinder transition) {
         if (isPendingEnter(transition)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved enter transition");
             return mPendingEnter;
         } else if (isPendingDismiss(transition)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved dismiss transition");
             return mPendingDismiss;
         } else if (isPendingResize(transition)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
             return mPendingResize;
         }
-
         return null;
     }
 
@@ -339,7 +349,7 @@
             Transitions.TransitionHandler handler,
             int extraTransitType, boolean resizeAnim) {
         if (mPendingEnter != null) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
+            ProtoLog.v(WM_SHELL_TRANSITIONS, "  splitTransition "
                     + " skip to start enter split transition since it already exist. ");
             return null;
         }
@@ -355,8 +365,10 @@
         mPendingEnter = new EnterSession(
                 transition, remoteTransition, extraTransitType, resizeAnim);
 
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "  splitTransition "
                 + " deduced Enter split screen");
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setEnterTransition: transitType=%d resize=%b",
+                extraTransitType, resizeAnim);
     }
 
     /** Starts a transition to dismiss split. */
@@ -364,7 +376,7 @@
             Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
             @SplitScreenController.ExitReason int reason) {
         if (mPendingDismiss != null) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
+            ProtoLog.v(WM_SHELL_TRANSITIONS, "  splitTransition "
                     + " skip to start dismiss split transition since it already exist. reason to "
                     + " dismiss = %s", exitReasonToString(reason));
             return null;
@@ -381,9 +393,11 @@
             @SplitScreenController.ExitReason int reason) {
         mPendingDismiss = new DismissSession(transition, reason, dismissTop);
 
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "  splitTransition "
                         + " deduced Dismiss due to %s. toTop=%s",
                 exitReasonToString(reason), stageTypeToString(dismissTop));
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setDismissTransition: reason=%s dismissTop=%s",
+                exitReasonToString(reason), stageTypeToString(dismissTop));
     }
 
     IBinder startResizeTransition(WindowContainerTransaction wct,
@@ -405,8 +419,9 @@
             @Nullable TransitionConsumedCallback consumedCallback,
             @Nullable TransitionFinishedCallback finishCallback) {
         mPendingResize = new TransitSession(transition, consumedCallback, finishCallback);
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "  splitTransition "
                 + " deduced Resize split screen");
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition");
     }
 
     void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
@@ -444,12 +459,15 @@
 
             mPendingEnter.onConsumed(aborted);
             mPendingEnter = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for enter transition");
         } else if (isPendingDismiss(transition)) {
             mPendingDismiss.onConsumed(aborted);
             mPendingDismiss = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for dismiss transition");
         } else if (isPendingResize(transition)) {
             mPendingResize.onConsumed(aborted);
             mPendingResize = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
         }
 
         // TODO: handle transition consumed for active remote handler
@@ -462,12 +480,15 @@
         if (isPendingEnter(mAnimatingTransition)) {
             mPendingEnter.onFinished(wct, mFinishTransaction);
             mPendingEnter = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for enter transition");
         } else if (isPendingDismiss(mAnimatingTransition)) {
             mPendingDismiss.onFinished(wct, mFinishTransaction);
             mPendingDismiss = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for dismiss transition");
         } else if (isPendingResize(mAnimatingTransition)) {
             mPendingResize.onFinished(wct, mFinishTransaction);
             mPendingResize = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
         }
 
         mActiveRemoteHandler = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2933cf4..7a1595f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -44,6 +44,7 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString;
 import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
 import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
@@ -138,6 +139,7 @@
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.splitscreen.SplitScreenController.SplitEnterReason;
 import com.android.wm.shell.transition.DefaultMixedHandler;
 import com.android.wm.shell.transition.LegacyTransitions;
 import com.android.wm.shell.transition.Transitions;
@@ -313,6 +315,7 @@
 
         taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
 
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
         mMainStage = new MainStage(
                 mContext,
                 mTaskOrganizer,
@@ -454,6 +457,8 @@
 
     boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
             WindowContainerTransaction wct) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveToStage: task=%d position=%d", task.taskId,
+                stagePosition);
         prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */);
         if (ENABLE_SHELL_TRANSITIONS) {
             mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct,
@@ -474,6 +479,7 @@
     }
 
     boolean removeFromSideStage(int taskId) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
         /**
@@ -498,11 +504,15 @@
             enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo, splitPosition,
                     taskBounds);
         }
-        if (enteredSplitSelect) mTaskOrganizer.applyTransaction(wct);
+        if (enteredSplitSelect) {
+            mTaskOrganizer.applyTransaction(wct);
+        }
     }
 
     void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
             Bundle options, UserHandle user) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startShortcut: pkg=%s id=%s position=%d user=%d",
+                packageName, shortcutId, position, user.getIdentifier());
         final boolean isEnteringSplit = !isSplitActive();
 
         IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -564,6 +574,7 @@
 
     /** Use this method to launch an existing Task via a taskId */
     void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position);
         mSplitRequest = new SplitRequest(taskId, position);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
@@ -595,6 +606,8 @@
     /** Launches an activity into split. */
     void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
             @Nullable Bundle options) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(),
+                position);
         mSplitRequest = new SplitRequest(intent.getIntent(), position);
         if (!ENABLE_SHELL_TRANSITIONS) {
             startIntentLegacy(intent, fillInIntent, position, options);
@@ -690,6 +703,9 @@
     void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2,
             @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                "startTasks: task1=%d task2=%d position=%d snapPosition=%d",
+                taskId1, taskId2, splitPosition, snapPosition);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (taskId2 == INVALID_TASK_ID) {
             if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
@@ -718,6 +734,9 @@
             @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
             @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                "startIntentAndTask: intent=%s task1=%d position=%d snapPosition=%d",
+                pendingIntent.getIntent(), taskId, splitPosition, snapPosition);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (taskId == INVALID_TASK_ID) {
             options1 = options1 != null ? options1 : new Bundle();
@@ -740,6 +759,9 @@
             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
             @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
             InstanceId instanceId) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                "startShortcutAndTask: shortcut=%s task1=%d position=%d snapPosition=%d",
+                shortcutInfo, taskId, splitPosition, snapPosition);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (taskId == INVALID_TASK_ID) {
             options1 = options1 != null ? options1 : new Bundle();
@@ -801,6 +823,10 @@
             @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
             @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                "startIntents: intent1=%s intent2=%s position=%d snapPosition=%d",
+                pendingIntent1.getIntent(), pendingIntent2.getIntent(), splitPosition,
+                snapPosition);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (pendingIntent2 == null) {
             options1 = options1 != null ? options1 : new Bundle();
@@ -1302,6 +1328,7 @@
     }
 
     void switchSplitPosition(String reason) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "switchSplitPosition");
         final SurfaceControl.Transaction t = mTransactionPool.acquire();
         mTempRect1.setEmpty();
         final StageTaskListener topLeftStage =
@@ -1343,7 +1370,7 @@
                     });
                 });
 
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
+        ProtoLog.v(WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
         mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
                 getSideStagePosition(), mSideStage.getTopChildTaskUid(),
                 mSplitLayout.isLeftRightSplit());
@@ -1376,11 +1403,12 @@
         if (!mMainStage.isActive()) {
             return;
         }
-
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onKeyguardVisibilityChanged: showing=%b", showing);
         setDividerVisibility(!mKeyguardShowing, null);
     }
 
     void onFinishedWakingUp() {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinishedWakingUp");
         if (!mMainStage.isActive()) {
             return;
         }
@@ -1421,6 +1449,8 @@
     }
 
     void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: topTaskId=%d reason=%s active=%b",
+                toTopTaskId, exitReasonToString(exitReason), mMainStage.isActive());
         if (!mMainStage.isActive()) return;
 
         StageTaskListener childrenToTop = null;
@@ -1439,6 +1469,8 @@
 
     private void exitSplitScreen(@Nullable StageTaskListener childrenToTop,
             @ExitReason int exitReason) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b",
+                childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive());
         if (!mMainStage.isActive()) return;
 
         final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -1447,6 +1479,8 @@
 
     private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop,
             WindowContainerTransaction wct, @ExitReason int exitReason) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s",
+                exitReasonToString(exitReason));
         if (!mMainStage.isActive() || mIsExiting) return;
 
         onSplitScreenExit();
@@ -1502,7 +1536,6 @@
             }
         });
 
-        Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
         // Log the exit
         if (childrenToTop != null) {
             logExitToStage(exitReason, childrenToTop == mMainStage);
@@ -1527,6 +1560,7 @@
      * Exits the split screen by finishing one of the tasks.
      */
     protected void exitStage(@SplitPosition int stageToClose) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitStage: stageToClose=%d", stageToClose);
         mSplitLayout.flingDividerToDismiss(stageToClose == SPLIT_POSITION_BOTTOM_OR_RIGHT,
                 EXIT_REASON_APP_FINISHED);
     }
@@ -1540,12 +1574,13 @@
         try {
             activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
         } catch (RemoteException | NullPointerException e) {
-            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+            ProtoLog.e(WM_SHELL_SPLIT_SCREEN,
                     "Unable to update focus on the chosen stage: %s", e.getMessage());
         }
     }
 
     private void clearRequestIfPresented() {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
         if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
                 && mMainStageListener.mVisible && mSideStageListener.mHasChildren) {
             mSplitRequest = null;
@@ -1581,6 +1616,8 @@
     void clearSplitPairedInRecents(@ExitReason int exitReason) {
         if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return;
 
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: reason=%s",
+                exitReasonToString(exitReason));
         mRecentTasks.ifPresent(recentTasks -> {
             // Notify recents if we are exiting in a way that breaks the pair, and disable further
             // updates to splits in the recents until we enter split again
@@ -1597,11 +1634,13 @@
     void prepareExitSplitScreen(@StageType int stageToTop,
             @NonNull WindowContainerTransaction wct) {
         if (!mMainStage.isActive()) return;
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop);
         mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
         mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
     }
 
     private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen");
         prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED,
                 !mIsDropEntering);
     }
@@ -1613,6 +1652,8 @@
     void prepareEnterSplitScreen(WindowContainerTransaction wct,
             @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
             boolean resizeAnim) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen: position=%d resize=%b",
+                startPosition, resizeAnim);
         onSplitScreenEnter();
         // Preemptively reset the reparenting behavior if we know that we are entering, as starting
         // split tasks with activity trampolines can inadvertently trigger the task to be
@@ -1629,6 +1670,8 @@
     private void prepareBringSplit(WindowContainerTransaction wct,
             @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
             boolean resizeAnim) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareBringSplit: task=%d isSplitVisible=%b",
+                taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
         if (taskInfo != null) {
             wct.startTask(taskInfo.taskId,
                     resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct));
@@ -1649,6 +1692,8 @@
     private void prepareActiveSplit(WindowContainerTransaction wct,
             @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
             boolean resizeAnim) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareActiveSplit: task=%d isSplitVisible=%b",
+                taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
         if (!ENABLE_SHELL_TRANSITIONS) {
             // Legacy transition we need to create divider here, shell transition case we will
             // create it on #finishEnterSplitScreen
@@ -1667,6 +1712,7 @@
     }
 
     private void prepareSplitLayout(WindowContainerTransaction wct, boolean resizeAnim) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareSplitLayout: resize=%b", resizeAnim);
         if (resizeAnim) {
             mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
         } else {
@@ -1686,6 +1732,7 @@
     }
 
     void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen");
         mSplitLayout.update(finishT, true /* resetImePosition */);
         mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
                 getMainStageBounds());
@@ -1835,12 +1882,20 @@
                     leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
             if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
                 // Update the pair for the top tasks
-                recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
+                boolean added = recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId,
+                        splitBounds);
+                if (added) {
+                    ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                            "updateRecentTasksSplitPair: adding split pair ltTask=%d rbTask=%d",
+                            leftTopTaskId, rightBottomTaskId);
+                }
             }
         });
     }
 
     private void sendSplitVisibilityChanged() {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "sendSplitVisibilityChanged: dividerVisible=%b",
+                mDividerVisible);
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             final SplitScreen.SplitScreenListener l = mListeners.get(i);
             l.onSplitVisibilityChanged(mDividerVisible);
@@ -1855,6 +1910,7 @@
             throw new IllegalArgumentException(this + "\n Unknown task appeared: " + taskInfo);
         }
 
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%s", taskInfo);
         mRootTaskInfo = taskInfo;
         mRootTaskLeash = leash;
 
@@ -1880,6 +1936,8 @@
         if (mSplitLayout != null
                 && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
                 && mMainStage.isActive()) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating",
+                    taskInfo.taskId);
             // Clear the divider remote animating flag as the divider will be re-rendered to apply
             // the new rotation config.  Don't reset the IME state since those updates are not in
             // sync with task info changes.
@@ -1892,6 +1950,7 @@
     @Override
     @CallSuper
     public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%s", taskInfo);
         if (mRootTaskInfo == null) {
             throw new IllegalArgumentException(this + "\n Unknown task vanished: " + taskInfo);
         }
@@ -1911,6 +1970,8 @@
 
     @VisibleForTesting
     void onRootTaskAppeared() {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b",
+                mRootTaskInfo, mMainStageListener.mHasRootTask, mSideStageListener.mHasRootTask);
         // Wait unit all root tasks appeared.
         if (mRootTaskInfo == null
                 || !mMainStageListener.mHasRootTask
@@ -1937,6 +1998,8 @@
      * #onStageHasChildrenChanged because this would be called every time child task appeared.
      * NOTICE: This only be called on legacy transition. */
     private void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onChildTaskAppeared: isMainStage=%b task=%d",
+                stageListener == mMainStageListener, taskId);
         // Handle entering split screen while there is a split pair running in the background.
         if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
                 && mSplitRequest == null) {
@@ -1960,6 +2023,7 @@
     }
 
     private void onRootTaskVanished() {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskVanished");
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mLaunchAdjacentController.clearLaunchAdjacentRoot();
         applyExitSplitScreen(null /* childrenToTop */, wct, EXIT_REASON_ROOT_TASK_VANISHED);
@@ -1990,6 +2054,8 @@
             return;
         }
 
+        // TODO Protolog
+
         // Check if it needs to dismiss split screen when both stage invisible.
         if (!mainStageVisible && mExitSplitScreenOnHide) {
             exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
@@ -2020,14 +2086,14 @@
             return;
         }
 
-        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
-                "Request to %s divider bar from %s.",
-                (visible ? "show" : "hide"), Debug.getCaller());
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                "setDividerVisibility: visible=%b keyguardShowing=%b dividerAnimating=%b caller=%s",
+                visible, mKeyguardShowing, mIsDividerRemoteAnimating, Debug.getCaller());
 
         // Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard
         // dismissing animation.
         if (visible && mKeyguardShowing) {
-            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
                     "   Defer showing divider bar due to keyguard showing.");
             return;
         }
@@ -2036,7 +2102,7 @@
         sendSplitVisibilityChanged();
 
         if (mIsDividerRemoteAnimating) {
-            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
                     "   Skip animating divider bar due to it's remote animating.");
             return;
         }
@@ -2050,12 +2116,12 @@
     private void applyDividerVisibility(@Nullable SurfaceControl.Transaction t) {
         final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
         if (dividerLeash == null) {
-            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
                     "   Skip animating divider bar due to divider leash not ready.");
             return;
         }
         if (mIsDividerRemoteAnimating) {
-            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
                     "   Skip animating divider bar due to it's remote animating.");
             return;
         }
@@ -2119,6 +2185,8 @@
     /** Callback when split roots have child or haven't under it.
      * NOTICE: This only be called on legacy transition. */
     private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b",
+                stageListener == mMainStageListener);
         final boolean hasChildren = stageListener.mHasChildren;
         final boolean isSideStage = stageListener == mSideStageListener;
         if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
@@ -2170,13 +2238,15 @@
     }
 
     @Override
-    public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
+    public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s",
+                bottomOrRight, exitReasonToString(exitReason));
         final boolean mainStageToTop =
                 bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
                         : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
         final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
         if (!ENABLE_SHELL_TRANSITIONS) {
-            exitSplitScreen(toTopStage, reason);
+            exitSplitScreen(toTopStage, exitReason);
             return;
         }
 
@@ -2219,6 +2289,7 @@
 
     @Override
     public void onLayoutSizeChanged(SplitLayout layout) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onLayoutSizeChanged");
         // Reset this flag every time onLayoutSizeChanged.
         mShowDecorImmediately = false;
 
@@ -2278,8 +2349,11 @@
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
         final StageTaskListener bottomRightStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
-        return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
+        boolean updated = layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
                 bottomRightStage.mRootTaskInfo);
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateWindowBounds: topLeftStage=%s bottomRightStage=%s",
+                layout.getBounds1(), layout.getBounds2());
+        return updated;
     }
 
     void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
@@ -2291,6 +2365,9 @@
         (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
                 bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
                 applyResizingOffset);
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                "updateSurfaceBounds: topLeftStage=%s bottomRightStage=%s",
+                layout.getBounds1(), layout.getBounds2());
     }
 
     @Override
@@ -2329,6 +2406,8 @@
 
     @Override
     public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLayoutOffsetTarget: x=%d y=%d",
+                offsetX, offsetY);
         final StageTaskListener topLeftStage =
                 mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
         final StageTaskListener bottomRightStage =
@@ -2343,6 +2422,7 @@
         if (displayId != DEFAULT_DISPLAY) {
             return;
         }
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDisplayAdded: display=%d", displayId);
         mDisplayController.addDisplayChangingController(this::onDisplayChange);
     }
 
@@ -2357,8 +2437,14 @@
 
     private void onDisplayChange(int displayId, int fromRotation, int toRotation,
             @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
-        if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) return;
+        if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) {
+            return;
+        }
 
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                "onDisplayChange: display=%d fromRot=%d toRot=%d config=%s",
+                displayId, fromRotation, toRotation,
+                newDisplayAreaInfo != null ? newDisplayAreaInfo.configuration : null);
         mSplitLayout.rotateTo(toRotation);
         if (newDisplayAreaInfo != null) {
             mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration);
@@ -2369,6 +2455,7 @@
 
     @VisibleForTesting
     void onFoldedStateChanged(boolean folded) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFoldedStateChanged: folded=%b", folded);
         mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
         if (!folded) return;
 
@@ -2439,6 +2526,8 @@
         final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
         if (triggerTask == null) {
             if (isSplitActive()) {
+                ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation",
+                        request.getDebugId());
                 // Check if the display is rotating.
                 final TransitionRequestInfo.DisplayChange displayChange =
                         request.getDisplayChange();
@@ -2467,6 +2556,8 @@
         }
 
         if (isSplitActive()) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d split active",
+                    request.getDebugId());
             // Try to handle everything while in split-screen, so return a WCT even if it's empty.
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  split is active so using split"
                             + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
@@ -2541,6 +2632,8 @@
             return null;
         } else {
             if (isOpening && getStageOfTask(triggerTask) != null) {
+                ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split",
+                        request.getDebugId());
                 // One task is appearing into split, prepare to enter split screen.
                 out = new WindowContainerTransaction();
                 prepareEnterSplitScreen(out);
@@ -2557,6 +2650,8 @@
      */
     public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request,
             @NonNull WindowContainerTransaction outWCT) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addEnterOrExitIfNeeded: transition=%d",
+                request.getDebugId());
         final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
         if (triggerTask != null && triggerTask.displayId != mDisplayId) {
             // Skip handling task on the other display.
@@ -2591,6 +2686,7 @@
     public void mergeAnimation(IBinder transition, TransitionInfo info,
             SurfaceControl.Transaction t, IBinder mergeTarget,
             Transitions.TransitionFinishCallback finishCallback) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "mergeAnimation: transition=%d", info.getDebugId());
         mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
     }
 
@@ -2602,6 +2698,7 @@
     @Override
     public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
             @Nullable SurfaceControl.Transaction finishT) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed");
         mSplitTransitions.onTransitionConsumed(transition, aborted, finishT);
     }
 
@@ -2617,6 +2714,7 @@
             // If we're not in split-mode, just abort so something else can handle it.
             if (!mMainStage.isActive()) return false;
 
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId());
             mSplitLayout.setFreezeDividerWindow(false);
             final StageChangeRecord record = new StageChangeRecord();
             final int transitType = info.getType();
@@ -2727,6 +2825,8 @@
             if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
                     startTransaction, finishTransaction, finishCallback)) {
                 if (mSplitTransitions.isPendingResize(transition)) {
+                    ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                            "startAnimation: transition=%d display change", info.getDebugId());
                     // Only need to update in resize because divider exist before transition.
                     mSplitLayout.update(startTransaction, true /* resetImePosition */);
                     startTransaction.apply();
@@ -2797,6 +2897,8 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingAnimation: transition=%d",
+                info.getDebugId());
         boolean shouldAnimate = true;
         if (mSplitTransitions.isPendingEnter(transition)) {
             shouldAnimate = startPendingEnterAnimation(transition,
@@ -2830,6 +2932,7 @@
 
     /** Called to clean-up state and do house-keeping after the animation is done. */
     public void onTransitionAnimationComplete() {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete");
         // If still playing, let it finish.
         if (!mMainStage.isActive() && !mIsExiting) {
             // Update divider state after animation so that it is still around and positioned
@@ -2842,6 +2945,8 @@
             @NonNull SplitScreenTransitions.EnterSession enterTransition,
             @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
             @NonNull SurfaceControl.Transaction finishT) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingEnterAnimation: enterTransition=%s",
+                enterTransition);
         // First, verify that we actually have opened apps in both splits.
         TransitionInfo.Change mainChild = null;
         TransitionInfo.Change sideChild = null;
@@ -2959,6 +3064,7 @@
     }
 
     public void goToFullscreenFromSplit() {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "goToFullscreenFromSplit");
         // If main stage is focused, toEnd = true if
         // mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT. Otherwise toEnd = false
         // If side stage is focused, toEnd = true if
@@ -2974,6 +3080,7 @@
 
     /** Move the specified task to fullscreen, regardless of focus state. */
     public void moveTaskToFullscreen(int taskId, int exitReason) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveTaskToFullscreen");
         boolean leftOrTop;
         if (mMainStage.containsTask(taskId)) {
             leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
@@ -2994,6 +3101,7 @@
      */
     public void onPipExpandToSplit(WindowContainerTransaction wct,
             ActivityManager.RunningTaskInfo taskInfo) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onPipExpandToSplit: task=%s", taskInfo);
         prepareEnterSplitScreen(wct, taskInfo, getActivateSplitPosition(taskInfo),
                 false /*resizeAnim*/);
 
@@ -3040,6 +3148,9 @@
     public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
             @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
             @NonNull SurfaceControl.Transaction finishT) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, 
+                "prepareDismissAnimation: transition=%d toStage=%d reason=%s",
+                info.getDebugId(), toStage, exitReasonToString(dismissReason));
         // Make some noise if things aren't totally expected. These states shouldn't effect
         // transitions locally, but remotes (like Launcher) may get confused if they were
         // depending on listener callbacks. This can happen because task-organizer callbacks
@@ -3126,6 +3237,9 @@
             @NonNull SplitScreenTransitions.DismissSession dismissTransition,
             @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
             @NonNull SurfaceControl.Transaction finishT) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                "startPendingDismissAnimation: transition=%d dismissTransition=%s",
+                info.getDebugId(), dismissTransition);
         prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info,
                 t, finishT);
         if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
@@ -3146,6 +3260,8 @@
 
     /** Call this when starting the open-recents animation while split-screen is active. */
     public void onRecentsInSplitAnimationStart(TransitionInfo info) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationStart: transition=%d",
+                info.getDebugId());
         if (isSplitScreenVisible()) {
             // Cache tasks on live tile.
             for (int i = 0; i < info.getChanges().size(); ++i) {
@@ -3178,6 +3294,7 @@
     /** Call this when the recents animation during split-screen finishes. */
     public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
             SurfaceControl.Transaction finishT) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish");
         mPausingTasks.clear();
         // Check if the recent transition is finished by returning to the current
         // split, so we can restore the divider bar.
@@ -3203,6 +3320,7 @@
 
     /** Call this when the recents animation finishes by doing pair-to-pair switch. */
     public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsPairToPairAnimationFinish");
         // Pair-to-pair switch happened so here should evict the live tile from its stage.
         // Otherwise, the task will remain in stage, and occluding the new task when next time
         // user entering recents.
@@ -3284,6 +3402,7 @@
      * handled.
      */
     private void setSplitsVisible(boolean visible) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible);
         mMainStageListener.mVisible = mSideStageListener.mVisible = visible;
         mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
     }
@@ -3292,6 +3411,7 @@
      * Sets drag info to be logged when splitscreen is next entered.
      */
     public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDroppedToSplit: position=%d", position);
         if (!isSplitScreenVisible()) {
             mIsDropEntering = true;
             mSkipEvictingMainStageChildren = true;
@@ -3308,7 +3428,8 @@
     /**
      * Sets info to be logged when splitscreen is next entered.
      */
-    public void onRequestToSplit(InstanceId sessionId, int enterReason) {
+    public void onRequestToSplit(InstanceId sessionId, @SplitEnterReason int enterReason) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRequestToSplit: reason=%d", enterReason);
         if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) {
             // If split running background, exit split first.
             // Skip this on shell transition due to we could evict existing tasks on transition
@@ -3384,6 +3505,7 @@
 
         @Override
         public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
             if (mMainStage.isActive()) {
                 final boolean isMainStage = mMainStageListener == this;
                 if (!ENABLE_SHELL_TRANSITIONS) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index af7bf36..f33ab33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -25,6 +25,7 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
 
 import android.annotation.CallSuper;
@@ -44,6 +45,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -175,6 +177,9 @@
     @Override
     @CallSuper
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%d taskParent=%d rootTask=%d",
+                taskInfo.taskId, taskInfo.parentTaskId,
+                mRootTaskInfo != null ? mRootTaskInfo.taskId : -1);
         if (mRootTaskInfo == null) {
             mRootLeash = leash;
             mRootTaskInfo = taskInfo;
@@ -225,6 +230,9 @@
                     || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
                     || !ArrayUtils.contains(CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
                     taskInfo.getWindowingMode())) {
+                ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                        "onTaskInfoChanged: task=%d no longer supports multiwindow",
+                        taskInfo.taskId);
                 // Leave split screen if the task no longer supports multi window or have
                 // uncontrolled task.
                 mCallbacks.onNoLongerSupportMultiWindow(taskInfo);
@@ -251,6 +259,7 @@
     @Override
     @CallSuper
     public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d", taskInfo.taskId);
         final int taskId = taskInfo.taskId;
         if (mRootTaskInfo.taskId == taskId) {
             mCallbacks.onRootTaskVanished();
@@ -333,6 +342,7 @@
     }
 
     void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addTask: task=%d", task.taskId);
         // Clear overridden bounds and windowing mode to make sure the child task can inherit
         // windowing mode and bounds from split root.
         wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
@@ -342,6 +352,7 @@
     }
 
     void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "reorderChild: task=%d onTop=%b", taskId, onTop);
         if (!containsTask(taskId)) {
             return;
         }
@@ -357,6 +368,7 @@
 
     /** Collects all the current child tasks and prepares transaction to evict them to display. */
     void evictAllChildren(WindowContainerTransaction wct) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evicting all children");
         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
             wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
@@ -367,11 +379,13 @@
         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
             if (taskId == taskInfo.taskId) continue;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict other child: task=%d", taskId);
             wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
         }
     }
 
     void evictNonOpeningChildren(RemoteAnimationTarget[] apps, WindowContainerTransaction wct) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "evictNonOpeningChildren");
         final SparseArray<ActivityManager.RunningTaskInfo> toBeEvict = mChildrenTaskInfo.clone();
         for (int i = 0; i < apps.length; i++) {
             if (apps[i].mode == MODE_OPENING) {
@@ -380,6 +394,7 @@
         }
         for (int i = toBeEvict.size() - 1; i >= 0; i--) {
             final ActivityManager.RunningTaskInfo taskInfo = toBeEvict.valueAt(i);
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict non-opening child: task=%d", taskInfo.taskId);
             wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
         }
     }
@@ -388,12 +403,15 @@
         for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
             final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
             if (!taskInfo.isVisible) {
+                ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict invisible child: task=%d",
+                        taskInfo.taskId);
                 wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
             }
         }
     }
 
     void evictChildren(WindowContainerTransaction wct, int taskId) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict child: task=%d", taskId);
         final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.get(taskId);
         if (taskInfo != null) {
             wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index c1406d0..caa894f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -22,8 +22,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_HOVER_ENTER;
 import static android.view.MotionEvent.ACTION_HOVER_EXIT;
+import static android.view.MotionEvent.ACTION_UP;
 import static android.view.WindowInsets.Type.statusBars;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -59,7 +61,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -321,8 +322,8 @@
         private final GestureDetector mGestureDetector;
 
         private boolean mIsDragging;
+        private boolean mTouchscreenInUse;
         private boolean mHasLongClicked;
-        private boolean mShouldClick;
         private int mDragPointerId = -1;
         private final Runnable mCloseMaximizeWindowRunnable;
 
@@ -343,6 +344,10 @@
 
         @Override
         public void onClick(View v) {
+            if (mIsDragging) {
+                mIsDragging = false;
+                return;
+            }
             final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
             final int id = v.getId();
             if (id == R.id.close_window) {
@@ -421,6 +426,10 @@
         @Override
         public boolean onTouch(View v, MotionEvent e) {
             final int id = v.getId();
+            if ((e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN) {
+                mTouchscreenInUse = e.getActionMasked() != ACTION_UP
+                        && e.getActionMasked() != ACTION_CANCEL;
+            }
             if (id != R.id.caption_handle && id != R.id.desktop_mode_caption
                     && id != R.id.open_menu_button && id != R.id.close_window
                     && id != R.id.maximize_window) {
@@ -432,31 +441,19 @@
             if (!mHasLongClicked && id != R.id.maximize_window) {
                 decoration.closeMaximizeMenuIfNeeded(e);
             }
-
-            final long eventDuration = e.getEventTime() - e.getDownTime();
-            final boolean isTouchScreen =
-                    (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
-            final boolean shouldLongClick = isTouchScreen && id == R.id.maximize_window
-                    && !mIsDragging && !mHasLongClicked
-                    && eventDuration >= ViewConfiguration.getLongPressTimeout();
-            if (shouldLongClick) {
-                v.performLongClick();
-                mHasLongClicked = true;
-                return true;
-            }
-
             return mDragDetector.onMotionEvent(v, e);
         }
 
         @Override
         public boolean onLongClick(View v) {
             final int id = v.getId();
-            if (id == R.id.maximize_window) {
+            if (id == R.id.maximize_window && mTouchscreenInUse) {
                 final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
                 moveTaskToFront(decoration.mTaskInfo);
                 if (decoration.isMaximizeMenuActive()) {
                     decoration.closeMaximizeMenu();
                 } else {
+                    mHasLongClicked = true;
                     decoration.createMaximizeMenu();
                 }
                 return true;
@@ -515,11 +512,9 @@
             if (mGestureDetector.onTouchEvent(e)) {
                 return true;
             }
-            if (e.getActionMasked() == MotionEvent.ACTION_CANCEL) {
-                // If a motion event is cancelled, reset mShouldClick so a click is not accidentally
-                // performed.
-                mShouldClick = false;
-            }
+            final int id = v.getId();
+            final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window
+                    || id == R.id.open_menu_button);
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
                     mDragPointerId = e.getPointerId(0);
@@ -527,12 +522,12 @@
                             0 /* ctrlType */, e.getRawX(0),
                             e.getRawY(0));
                     mIsDragging = false;
-                    mShouldClick = true;
                     mHasLongClicked = false;
-                    return true;
+                    // Do not consume input event if a button is touched, otherwise it would
+                    // prevent the button's ripple effect from showing.
+                    return !touchingButton;
                 }
                 case MotionEvent.ACTION_MOVE: {
-                    mShouldClick = false;
                     // If a decor's resize drag zone is active, don't also try to reposition it.
                     if (decoration.isHandlingDragResize()) break;
                     decoration.closeMaximizeMenu();
@@ -553,11 +548,6 @@
                 case MotionEvent.ACTION_CANCEL: {
                     final boolean wasDragging = mIsDragging;
                     if (!wasDragging) {
-                        if (mShouldClick && v != null && !mHasLongClicked) {
-                            v.performClick();
-                            mShouldClick = false;
-                            return true;
-                        }
                         return false;
                     }
                     if (e.findPointerIndex(mDragPointerId) == -1) {
@@ -576,8 +566,15 @@
                             position,
                             new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
                             newTaskBounds));
-                    mIsDragging = false;
-                    return true;
+                    if (touchingButton && !mHasLongClicked) {
+                        // We need the input event to not be consumed here to end the ripple
+                        // effect on the touched button. We will reset drag state in the ensuing
+                        // onClick call that results.
+                        return false;
+                    } else {
+                        mIsDragging = false;
+                        return true;
+                    }
                 }
             }
             return true;
diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java
deleted file mode 100644
index b7c3721..0000000
--- a/media/java/android/media/RingtoneSelection.java
+++ /dev/null
@@ -1,742 +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 android.media;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.FlaggedApi;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.vibrator.Flags;
-import android.provider.MediaStore;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Immutable representation a desired ringtone, usually originating from a user preference.
- * Unlike sound-only Uris, a "silent" setting is an explicit selection value, rather than null.
- *
- * <p>This representation can be converted into (or from) a URI form for storing within a string
- * preference or when using the ringtone picker via {@link RingtoneManager#ACTION_RINGTONE_PICKER}.
- * It does not carry any actual media data - it only references the components that make
- * up the preference. Initial selections can be built using {@link RingtoneSelection.Builder}.
- *
- * <p>A RingtoneSelection is typically played by passing into a {@link Ringtone.Builder}, and
- * supplementing with contextual defaults from the application. Bad Uris are handled by the
- * {@link Ringtone} class - the RingtoneSelection doesn't validate the target of the Uri.
- *
- * <p>When a RingtoneSelection is created/loaded, the values of its properties are modified
- * to be internally consistent and reflect effective values - with the exception of not verifying
- * the actual URI content. For example, loading a selection Uri that sets a sound source to
- * {@link #SOUND_SOURCE_URI}, but doesn't also have a sound Uri set, will result in this class
- * instead returning {@link #SOUND_SOURCE_UNSPECIFIED} from {@link #getSoundSource}.
- *
- * <h2>Storing preferences</h2>
- *
- * <p>A ringtone preference can have several states: either unset, set to a ringtone selection Uri,
- * or, from prior to the introduction of {@code RingtoneSelection}, set to a sound-only Uri or
- * explicitly set to null to indicate silent.
- *
- * @hide
- */
-@TestApi
-@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
-public final class RingtoneSelection {
-
-    /**
-     * The sound source was specified but its value was not recognized. This value is used
-     * internally for not stripping unrecognised (possibly future) values during processing.
-     * @hide
-     */
-    public static final int SOUND_SOURCE_UNKNOWN = -1;
-
-    /**
-     * The sound source is not explicitly specified, so it can follow default behavior for its
-     * context.
-     */
-    public static final int SOUND_SOURCE_UNSPECIFIED = 0;
-
-    /**
-     * Sound is explicitly disabled, such as the user having selected "Silent" in the sound picker.
-     */
-    public static final int SOUND_SOURCE_OFF = 1;
-
-    /**
-     * The sound Uri should be used as the source of sound.
-     */
-    public static final int SOUND_SOURCE_URI = 2;
-
-    /**
-     * The sound should explicitly use the system default.
-     *
-     * <p>This value isn't valid within the system default itself.
-     */
-    public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3;
-
-    // Note: Value 4 reserved for possibility of SOURCE_SOURCE_APPLICATION_DEFAULT.
-
-    /**
-     * Directive for how to make sound.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "SOUND_SOURCE_", value = {
-            SOUND_SOURCE_UNKNOWN,
-            SOUND_SOURCE_UNSPECIFIED,
-            SOUND_SOURCE_OFF,
-            SOUND_SOURCE_URI,
-            SOUND_SOURCE_SYSTEM_DEFAULT,
-    })
-    public @interface SoundSource {}
-
-    /**
-     * The vibration source was specified but its value was not recognized.
-     * This value is used internally for not stripping unrecognised (possibly
-     * future) values during processing.
-     * @hide
-     */
-    public static final int VIBRATION_SOURCE_UNKNOWN = -1;
-
-    /**
-     * Vibration source is not explicitly specified. If vibration is enabled, this will use the
-     * first available of {@link #VIBRATION_SOURCE_AUDIO_CHANNEL},
-     * {@link #VIBRATION_SOURCE_APPLICATION_DEFAULT}, or {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
-     */
-    public static final int VIBRATION_SOURCE_UNSPECIFIED = 0;
-
-    /** Specifies that vibration is explicitly disabled for this ringtone. */
-    public static final int VIBRATION_SOURCE_OFF = 1;
-
-    /** The vibration Uri should be used as the source of vibration. */
-    public static final int VIBRATION_SOURCE_URI = 2;
-
-    /**
-     * The vibration should explicitly use the system default.
-     *
-     * <p>This value isn't valid within the system default itself.
-     */
-    public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3;
-
-    /**
-     * Specifies that vibration should use the vibration provided by the application. This is
-     * typically the application's own default for the use-case, provided via
-     * {@link Ringtone.Builder#setVibrationEffect}. For notification channels, this is the vibration
-     * effect saved on the notification channel.
-     *
-     * <p>If no vibration is specified by the application, this value behaves if the source was
-     * {@link #VIBRATION_SOURCE_UNSPECIFIED}.
-     *
-     * <p>This value isn't valid within the system default.
-     */
-    public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4;
-
-    /**
-     * Specifies that vibration should use haptic audio channels from the
-     * sound Uri. If the sound URI doesn't have haptic channels, then reverts to the order specified
-     * by {@link #VIBRATION_SOURCE_UNSPECIFIED}.
-     */
-    // Numeric gap from VIBRATION_SOURCE_APPLICATION_DEFAULT in case we want other common elements.
-    public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10;
-
-    /**
-     * Specifies that vibration should generate haptic audio channels from the
-     * audio tracks of the sound Uri.
-     *
-     * If the sound Uri already has haptic channels, then behaves as though
-     * {@link #VIBRATION_SOURCE_AUDIO_CHANNEL} was specified instead.
-     */
-    public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11;
-
-    /**
-     * Directive for how to vibrate.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "VIBRATION_SOURCE_", value = {
-            VIBRATION_SOURCE_UNKNOWN,
-            VIBRATION_SOURCE_UNSPECIFIED,
-            VIBRATION_SOURCE_OFF,
-            VIBRATION_SOURCE_URI,
-            VIBRATION_SOURCE_APPLICATION_DEFAULT,
-            VIBRATION_SOURCE_AUDIO_CHANNEL,
-            VIBRATION_SOURCE_HAPTIC_GENERATOR,
-    })
-    public @interface VibrationSource {}
-
-    /**
-     * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the sound Uri
-     * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF},
-     * and symbolic default URIs ({@link RingtoneManager#getDefaultUri}) meaning
-     * {@link #SOUND_SOURCE_SYSTEM_DEFAULT}.
-     *
-     * <p>This behavior is particularly suited to loading values from older settings that may
-     * contain a raw sound Uri or null for silent.
-     *
-     * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
-     */
-    public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1;
-
-    /**
-     * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the vibration
-     * Uri for the returned {@link RingtoneSelection}, with null meaning
-     * {@link #VIBRATION_SOURCE_OFF} and symbolic default URIs
-     * ({@link RingtoneManager#getDefaultUri}) meaning {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
-     *
-     * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
-     */
-    public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2;
-
-    /**
-     * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as an invalid
-     * value. Null or an invalid values will revert to default behavior correspnoding to
-     * {@link #DEFAULT_SELECTION_URI_STRING}. Symbolic default URIs
-     * ({@link RingtoneManager#getDefaultUri}) will set both
-     * {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
-     *
-     * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false,
-     * which include {@code null}.
-     */
-    public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3;
-
-    /**
-     * How to treat values in {@link #fromUri}.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "FROM_URI_", value = {
-            FROM_URI_RINGTONE_SELECTION_OR_SOUND,
-            FROM_URI_RINGTONE_SELECTION_OR_VIBRATION,
-            FROM_URI_RINGTONE_SELECTION_ONLY
-    })
-    public @interface FromUriBehavior {}
-
-    private static final String BASE_RINGTONE_URI = "content://media/ringtone";
-    /**
-     * String representation of a RingtoneSelection Uri that says to use defaults (equivalent
-     * to {@code new RingtoneSelection.Builder().build()}).
-     */
-    public static final String DEFAULT_SELECTION_URI_STRING = BASE_RINGTONE_URI;
-
-    private static final String MEDIA_URI_RINGTONE_PATH = "/ringtone";
-
-    /* Query param keys. */
-    private static final String URI_PARAM_SOUND_URI = "su";
-    private static final String URI_PARAM_SOUND_SOURCE = "ss";
-    private static final String URI_PARAM_VIBRATION_URI = "vu";
-    private static final String URI_PARAM_VIBRATION_SOURCE = "vs";
-
-    /* Common param values */
-    private static final String SOURCE_OFF_STRING = "off";
-    private static final String SOURCE_SYSTEM_DEFAULT_STRING = "sys";
-
-    /* Vibration source param values. */
-    private static final String VIBRATION_SOURCE_AUDIO_CHANNEL_STRING = "ac";
-    private static final String VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING = "app";
-    private static final String VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING = "hg";
-
-    @Nullable
-    private final Uri mSoundUri;
-    @SoundSource
-    private final int mSoundSource;
-
-    @Nullable
-    private final Uri mVibrationUri;
-    @VibrationSource
-    private final int mVibrationSource;
-
-    private RingtoneSelection(@Nullable Uri soundUri, @SoundSource int soundSource,
-            @Nullable Uri vibrationUri, @VibrationSource int vibrationSource) {
-        // Enforce guarantees on the source values: revert to unspecified if they depend on
-        // something that's not set.
-        //
-        // The non-public "unknown" value can't appear in a getter result, it's just a reserved
-        // "null" value and should be treated the same as an unrecognized value. This can be seen
-        // in Uri parsing. For this and other unrecognized values, we either revert them to the URI
-        // source, if a Uri was included, or the "unspecified" source otherwise. This can be
-        // seen in action in the Uri parsing.
-        //
-        // The "unspecified" source is a public value meaning that there is no specific
-        // behavior indicated, and the defaults and fallbacks should be applied. For example, an
-        // vibration source value of "system default" means to explicitly use the system default
-        // vibration. However, an "unspecified" vibration source will first see if audio coupled
-        // or application-default vibrations are available.
-        mSoundSource = switch (soundSource) {
-            // Supported explicit values that don't have a Uri.
-            case SOUND_SOURCE_OFF, SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_SYSTEM_DEFAULT ->
-                    soundSource;
-            // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
-            // unspecified.
-            default ->
-                soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_UNSPECIFIED;
-        };
-        mVibrationSource = switch (vibrationSource) {
-            // Enforce vibration sources that require a sound Uri.
-            case VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR ->
-                    soundUri != null ? vibrationSource : VIBRATION_SOURCE_UNSPECIFIED;
-            // Supported explicit values that don't rely on any Uri.
-            case VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_UNSPECIFIED,
-                    VIBRATION_SOURCE_SYSTEM_DEFAULT, VIBRATION_SOURCE_APPLICATION_DEFAULT ->
-                    vibrationSource;
-            // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
-            // unspecified.
-            default ->
-                    vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_UNSPECIFIED;
-        };
-        // Clear Uri values if they're un-used by the source.
-        mSoundUri = mSoundSource == SOUND_SOURCE_URI ? soundUri : null;
-        mVibrationUri = mVibrationSource == VIBRATION_SOURCE_URI ? vibrationUri : null;
-    }
-
-    /**
-     * Returns the stored sound behavior.
-     */
-    @SoundSource
-    public int getSoundSource() {
-        return mSoundSource;
-    }
-
-    /**
-     * Returns the sound Uri for this selection. This is guaranteed to be non-null if
-     * {@link #getSoundSource} returns {@link #SOUND_SOURCE_URI}.
-     */
-    @Nullable
-    public Uri getSoundUri() {
-        return mSoundUri;
-    }
-
-    /**
-     * Returns the selected vibration behavior.
-     */
-    @VibrationSource
-    public int getVibrationSource() {
-        return mVibrationSource;
-    }
-
-    /**
-     * Returns the vibration Uri for this selection. This is guaranteed to be non-null if
-     * {@link #getVibrationSource} returns {@link #SOUND_SOURCE_URI}.
-     */
-    @Nullable
-    public Uri getVibrationUri() {
-        return mVibrationUri;
-    }
-
-    /**
-     * Converts the ringtone selection into a Uri-form, suitable for storing as a user preference
-     * or returning as a result.
-     */
-    @NonNull
-    public Uri toUri() {
-        Uri.Builder builder = new Uri.Builder()
-                .scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(MediaStore.AUTHORITY)
-                .path(MEDIA_URI_RINGTONE_PATH);
-        if (mSoundUri != null) {
-            builder.appendQueryParameter(URI_PARAM_SOUND_URI, mSoundUri.toString());
-        }
-        // Only off is explicit for sound sources
-        String soundSourceStr = soundSourceToString(mSoundSource);
-        if (soundSourceStr != null) {
-            builder.appendQueryParameter(URI_PARAM_SOUND_SOURCE, soundSourceStr);
-        }
-        if (mVibrationUri != null) {
-            builder.appendQueryParameter(URI_PARAM_VIBRATION_URI, mVibrationUri.toString());
-        }
-        String vibrationSourceStr = vibrationSourceToString(mVibrationSource);
-        if (vibrationSourceStr != null) {
-            builder.appendQueryParameter(URI_PARAM_VIBRATION_SOURCE, vibrationSourceStr);
-        }
-        return builder.build();
-    }
-
-    /**
-     * Returns true if the Uri is an encoded {@link RingtoneSelection}. This method doesn't
-     * validate the parameters of the selection.
-     *
-     * @see #fromUri
-     * @see #toUri
-     */
-    public static boolean isRingtoneSelectionUri(@Nullable Uri uri) {
-        if (uri == null) {
-            return false;
-        }
-        // Any URI content://media/ringtone
-        return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
-                && MediaStore.AUTHORITY.equals(
-                        ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()))
-                && MEDIA_URI_RINGTONE_PATH.equals(uri.getPath());
-    }
-
-    /**
-     * Strip the specified userId from internal Uris. Non-stripped userIds will typically be
-     * for work profiles referencing system ringtones from the host user.
-     *
-     * This is only for use in RingtoneManager.
-     * @hide
-     */
-    @VisibleForTesting
-    public RingtoneSelection getWithoutUserId(int userIdToStrip) {
-        if (mSoundSource != SOUND_SOURCE_URI && mVibrationSource != VIBRATION_SOURCE_URI) {
-            return this;
-        }
-
-        // Ok if uri is null. We only replace explicit references to the specified (current) userId.
-        int soundUserId = ContentProvider.getUserIdFromUri(mSoundUri, UserHandle.USER_NULL);
-        int vibrationUserId = ContentProvider.getUserIdFromUri(mVibrationUri, UserHandle.USER_NULL);
-        boolean needToChangeSound =
-                soundUserId != UserHandle.USER_NULL && soundUserId == userIdToStrip;
-        boolean needToChangeVibration =
-                vibrationUserId != UserHandle.USER_NULL && vibrationUserId == userIdToStrip;
-
-        // Anything to do?
-        if (!needToChangeSound && !needToChangeVibration) {
-            return this;
-        }
-
-        RingtoneSelection.Builder updated = new Builder(this);
-        // The relevant uris can't be null, because they contain userIdToStrip.
-        if (needToChangeSound) {
-            // mSoundUri is not null, so the result of getUriWithoutUserId won't be null.
-            updated.setSoundSource(ContentProvider.getUriWithoutUserId(mSoundUri));
-        }
-        if (needToChangeVibration) {
-            updated.setVibrationSource(ContentProvider.getUriWithoutUserId(mVibrationUri));
-        }
-        return updated.build();
-    }
-
-    @Override
-    public String toString() {
-        return toUri().toString();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == this) {
-            return true;
-        }
-        if (!(o instanceof RingtoneSelection other)) {
-            return false;
-        }
-        return this.mSoundSource == other.mSoundSource
-                && this.mVibrationSource == other.mVibrationSource
-                && Objects.equals(this.mSoundUri, other.mSoundUri)
-                && Objects.equals(this.mVibrationUri, other.mVibrationUri);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mSoundSource, mVibrationSource, mSoundUri, mVibrationUri);
-    }
-
-    /**
-     * Converts a Uri into a RingtoneSelection.
-     *
-     * <p>Null values, Uris that {@link #isRingtoneSelectionUri(Uri)} returns false (except for
-     * old-style symbolic default Uris {@link RingtoneManager#getDefaultUri}) will be treated
-     * according to the behaviour specified by the {@code unrecognizedValueBehavior} parameter.
-     *
-     * <p>Symbolic default Uris (where {@link RingtoneManager#getDefaultType(Uri)} returns -1,
-     * will map sources to {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and
-     * {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
-     *
-     * @param uri The Uri to convert, potentially null.
-     * @param unrecognizedValueBehavior indicates how to treat values for which
-     *   {@link #isRingtoneSelectionUri(Uri)} returns false (including null).
-     * @return the RingtoneSelection represented by the given uri.
-     */
-    @NonNull
-    public static RingtoneSelection fromUri(@Nullable Uri uri,
-            @FromUriBehavior int unrecognizedValueBehavior) {
-        if (isRingtoneSelectionUri(uri)) {
-            return parseRingtoneSelectionUri(uri);
-        }
-        // Old symbolic default URIs map to the sources suggested by the unrecognized behavior.
-        // It doesn't always map to both sources because the app may have its own default behavior
-        // to apply, so non-primary sources are left as unspecified, which will revert to the
-        // system default in the absence of an app default.
-        boolean isDefaultUri = RingtoneManager.getDefaultType(uri) > 0;
-        RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
-        switch (unrecognizedValueBehavior) {
-            case FROM_URI_RINGTONE_SELECTION_ONLY:
-                if (isDefaultUri) {
-                    builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
-                    builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT);
-                }
-                // Always return unspecified (defaults) for unrecognized ringtone selection Uris.
-                return builder.build();
-            case FROM_URI_RINGTONE_SELECTION_OR_SOUND:
-                if (uri == null) {
-                    return builder.setSoundSource(SOUND_SOURCE_OFF).build();
-                } else if (isDefaultUri) {
-                    return builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT).build();
-                } else {
-                    return builder.setSoundSource(uri).build();
-                }
-            case FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
-                if (uri == null) {
-                    return builder.setVibrationSource(VIBRATION_SOURCE_OFF).build();
-                } else if (isDefaultUri) {
-                    return builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT).build();
-                } else {
-                    // Unlike sound, there's no legacy settings alias uri for the default.
-                    return builder.setVibrationSource(uri).build();
-                }
-            default:
-                throw new IllegalArgumentException("Unknown behavior parameter: "
-                        + unrecognizedValueBehavior);
-        }
-    }
-
-    /**
-     * Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}.
-     * As a special case to be more compatible, if the RingtoneSelection has a userId specified in
-     * the authority, then this is pushed down into the sound and vibration Uri, as the selection
-     * itself is agnostic.
-     */
-    @NonNull
-    private static RingtoneSelection parseRingtoneSelectionUri(@NonNull Uri uri) {
-        RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
-        int soundSource = stringToSoundSource(uri.getQueryParameter(URI_PARAM_SOUND_SOURCE));
-        int vibrationSource = stringToVibrationSource(
-                uri.getQueryParameter(URI_PARAM_VIBRATION_SOURCE));
-        Uri soundUri = getParamAsUri(uri, URI_PARAM_SOUND_URI);
-        Uri vibrationUri = getParamAsUri(uri, URI_PARAM_VIBRATION_URI);
-
-        // Add userId if necessary. This won't override an existing one in the sound/vib Uris.
-        int userIdFromAuthority = ContentProvider.getUserIdFromAuthority(
-                uri.getAuthority(), UserHandle.USER_NULL);
-        if (userIdFromAuthority != UserHandle.USER_NULL) {
-            // Won't override existing user id.
-            if (soundUri != null) {
-                soundUri = ContentProvider.maybeAddUserId(soundUri, userIdFromAuthority);
-            }
-            if (vibrationUri != null) {
-                vibrationUri = ContentProvider.maybeAddUserId(vibrationUri, userIdFromAuthority);
-            }
-        }
-
-        // Set sound uri if present, but map system default Uris to the system default source.
-        if (soundUri != null) {
-            if (RingtoneManager.getDefaultType(uri) >= 0) {
-                builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
-            } else {
-                builder.setSoundSource(soundUri);
-            }
-        }
-        if (vibrationUri != null) {
-            builder.setVibrationSource(vibrationUri);
-        }
-
-        // Don't set the source if there's a URI and the source is default, because that will
-        // override the Uri source set above. In effect, we prioritise "explicit" sources over
-        // an implicit Uri source - except for "default", which isn't really explicit.
-        if (soundSource != SOUND_SOURCE_UNSPECIFIED || soundUri == null) {
-            builder.setSoundSource(soundSource);
-        }
-        if (vibrationSource != VIBRATION_SOURCE_UNSPECIFIED || vibrationUri == null) {
-            builder.setVibrationSource(vibrationSource);
-        }
-        return builder.build();
-    }
-
-    @Nullable
-    private static Uri getParamAsUri(@NonNull Uri uri, String param) {
-        // This returns the uri-decoded value, no need to further decode.
-        String value = uri.getQueryParameter(param);
-        if (value == null) {
-            return null;
-        }
-        return Uri.parse(value);
-    }
-
-    /**
-     * Converts the {@link SoundSource} to the uri query param value for it, or null
-     * if the sound source is default, unknown, or implicit (uri).
-     */
-    @Nullable
-    private static String soundSourceToString(@SoundSource int soundSource) {
-        return switch (soundSource) {
-            case SOUND_SOURCE_OFF -> SOURCE_OFF_STRING;
-            case SOUND_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
-            default -> null;
-        };
-    }
-
-    /**
-     * Returns the sound source int corresponding to the query string value. Returns
-     * {@link #SOUND_SOURCE_UNKNOWN} if the value isn't recognised, and
-     * {@link #SOUND_SOURCE_UNSPECIFIED} if the value is {@code null} (not in the Uri).
-     */
-    @SoundSource
-    private static int stringToSoundSource(@Nullable String soundSource) {
-        if (soundSource == null) {
-            return SOUND_SOURCE_UNSPECIFIED;
-        }
-        return switch (soundSource) {
-            case SOURCE_OFF_STRING -> SOUND_SOURCE_OFF;
-            case SOURCE_SYSTEM_DEFAULT_STRING -> SOUND_SOURCE_SYSTEM_DEFAULT;
-            default -> SOUND_SOURCE_UNKNOWN;
-        };
-    }
-
-    /**
-     * Converts the {@code vibrationSource} to the uri query param value for it, or null
-     * if the vibration source is default, unknown, or implicit (uri).
-     */
-    @Nullable
-    private static String vibrationSourceToString(@VibrationSource int vibrationSource) {
-        return switch (vibrationSource) {
-            case VIBRATION_SOURCE_OFF -> SOURCE_OFF_STRING;
-            case VIBRATION_SOURCE_AUDIO_CHANNEL -> VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR -> VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
-            case VIBRATION_SOURCE_APPLICATION_DEFAULT ->
-                    VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING;
-            case VIBRATION_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
-            default -> null;
-        };
-    }
-
-    @VibrationSource
-    private static int stringToVibrationSource(@Nullable String vibrationSource) {
-        if (vibrationSource == null) {
-            return VIBRATION_SOURCE_UNSPECIFIED;
-        }
-        return switch (vibrationSource) {
-            case SOURCE_OFF_STRING -> VIBRATION_SOURCE_OFF;
-            case SOURCE_SYSTEM_DEFAULT_STRING -> VIBRATION_SOURCE_SYSTEM_DEFAULT;
-            case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING -> VIBRATION_SOURCE_AUDIO_CHANNEL;
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING -> VIBRATION_SOURCE_HAPTIC_GENERATOR;
-            case VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING ->
-                    VIBRATION_SOURCE_APPLICATION_DEFAULT;
-            default -> VIBRATION_SOURCE_UNKNOWN;
-        };
-    }
-
-    /**
-     * Builder for {@link RingtoneSelection}. In general, this builder will be used by interfaces
-     * allowing the user to configure their selection. Once a selection is stored as a Uri, then
-     * the RingtoneSelection can be loaded directly using {@link RingtoneSelection#fromUri}.
-     */
-    @FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
-    public static final class Builder {
-        private Uri mSoundUri;
-        private Uri mVibrationUri;
-        @SoundSource private int mSoundSource = SOUND_SOURCE_UNSPECIFIED;
-        @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_UNSPECIFIED;
-
-        /**
-         * Creates a new {@link RingtoneSelection} builder. A default ringtone selection has its
-         * sound and vibration source unspecified, which means they would fall back to app/system
-         * defaults.
-         */
-        public Builder() {}
-
-        /**
-         * Creates a builder initialized with the given ringtone selection.
-         */
-        public Builder(@NonNull RingtoneSelection selection) {
-            requireNonNull(selection);
-            mSoundSource = selection.getSoundSource();
-            mSoundUri = selection.getSoundUri();
-            mVibrationSource = selection.getVibrationSource();
-            mVibrationUri = selection.getVibrationUri();
-        }
-
-        /**
-         * Sets the desired sound source.
-         *
-         * <p>Values other than {@link #SOUND_SOURCE_URI} will clear any previous sound Uri.
-         * For {@link #SOUND_SOURCE_URI}, the {@link #setSoundSource(Uri)} method should be
-         * used instead, as setting it here will have no effect unless the Uri is also set.
-         */
-        @NonNull
-        public Builder setSoundSource(@SoundSource int soundSource) {
-            mSoundSource = soundSource;
-            if (soundSource != SOUND_SOURCE_URI && soundSource != SOUND_SOURCE_UNKNOWN) {
-                // Note that this means the configuration of "silent sound, but use haptic
-                // generator" is currently not supported. Future support could be added by either
-                // using the vibration uri in that case, or by having a special
-                // "setSoundUriForVibrationOnly(Uri)" method that sets sound source to off but
-                // also retains the Uri.
-                mSoundUri = null;
-            }
-            return this;
-        }
-
-        /**
-         * Sets the sound source to {@link #SOUND_SOURCE_URI}, and the sound Uri to the
-         * specified {@link Uri}.
-         */
-        @NonNull
-        public Builder setSoundSource(@NonNull Uri soundUri) {
-            // getCanonicalUri shouldn't return null. If it somehow did, then the
-            // RingtoneSelection constructor will revert this to unspecified.
-            mSoundUri = requireNonNull(soundUri).getCanonicalUri();
-            mSoundSource = SOUND_SOURCE_URI;
-            return this;
-        }
-
-        /**
-         * Sets the vibration source to the specified value.
-         *
-         * <p>Values other than {@link #VIBRATION_SOURCE_URI} will clear any previous vibration Uri.
-         * For {@link #VIBRATION_SOURCE_URI}, the {@link #setVibrationSource(Uri)} method should be
-         * used instead, as setting it here will have no effect unless the Uri is also set.
-         */
-        @NonNull
-        public Builder setVibrationSource(@VibrationSource int vibrationSource) {
-            mVibrationSource = vibrationSource;
-            if (vibrationSource != VIBRATION_SOURCE_URI
-                    && vibrationSource != VIBRATION_SOURCE_UNKNOWN) {
-                mVibrationUri = null;
-            }
-            return this;
-        }
-
-        /**
-         * Sets the vibration source to {@link #VIBRATION_SOURCE_URI}, and the vibration Uri to the
-         * specified {@link Uri}.
-         */
-        @NonNull
-        public Builder setVibrationSource(@NonNull Uri vibrationUri) {
-            // getCanonicalUri shouldn't return null. If it somehow did, then the
-            // RingtoneSelection constructor will revert this to unspecified.
-            mVibrationUri = requireNonNull(vibrationUri).getCanonicalUri();
-            mVibrationSource = VIBRATION_SOURCE_URI;
-            return this;
-        }
-
-        /**
-         * Returns the ringtone Uri that was configured.
-         */
-        @NonNull
-        public RingtoneSelection build() {
-            return new RingtoneSelection(mSoundUri, mSoundSource, mVibrationUri, mVibrationSource);
-        }
-    }
-}
diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp
index c9a8864..fd5f195 100644
--- a/media/tests/projection/Android.bp
+++ b/media/tests/projection/Android.bp
@@ -3,6 +3,7 @@
 //########################################################################
 
 package {
+    default_team: "trendy_team_lse_desktop_os_experience",
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
     // all of the 'license_kinds' from "frameworks_base_license"
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 82b47a9..527701c 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -63,9 +63,9 @@
   <!-- This appears as the description body of the modal bottom sheet which provides all available providers for users to choose. [CHAR LIMIT=200] -->
   <string name="choose_provider_body">Select a password manager to save your info and sign in faster next time</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is passkey. [CHAR LIMIT=200] -->
-  <string name="choose_create_option_passkey_title">Create passkey for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
+  <string name="choose_create_option_passkey_title">Create passkey to sign in to <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is password. [CHAR LIMIT=200] -->
-  <string name="choose_create_option_password_title">Save password for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
+  <string name="choose_create_option_password_title">Save password to sign in to <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is others. [CHAR LIMIT=200] -->
   <string name="choose_create_option_sign_in_title">Save sign-in info for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
   <!-- Types which are inserted as a placeholder as credentialTypes for other strings. [CHAR LIMIT=200] -->
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 1326023..c118f88 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -237,11 +237,8 @@
         if (providerList.isEmpty()) {
             return false
         }
-        var totalEntryCount = 0
-        providerList.forEach { provider ->
-            totalEntryCount += provider.credentialEntryList.size
-        }
         val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerList)
+        var totalEntryCount = providerDisplayInfo.sortedUserNameToCredentialEntryList.size
         val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
         val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
         val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 7277550..ccc4660 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -554,15 +554,11 @@
         if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24)
         else null,
         entryHeadlineText = username,
-        entrySecondLineText = if (
-            credentialEntryInfo.credentialType == CredentialType.PASSWORD) {
-            "••••••••••••"
-        } else {
-            val itemsToDisplay = listOf(
+        entrySecondLineText = listOf(
                 displayName,
                 credentialEntryInfo.credentialTypeDisplayName,
                 credentialEntryInfo.providerDisplayName
-            ).filterNot(TextUtils::isEmpty)
+        ).filterNot(TextUtils::isEmpty).let { itemsToDisplay ->
             if (itemsToDisplay.isEmpty()) null
             else itemsToDisplay.joinToString(
                 separator = stringResource(R.string.get_dialog_sign_in_type_username_separator)
diff --git a/packages/PackageInstaller/res/layout/install_content_view.xml b/packages/PackageInstaller/res/layout/install_content_view.xml
index 2ecd2d5..524a88a 100644
--- a/packages/PackageInstaller/res/layout/install_content_view.xml
+++ b/packages/PackageInstaller/res/layout/install_content_view.xml
@@ -24,114 +24,116 @@
     android:paddingLeft="?android:attr/dialogPreferredPadding"
     android:paddingRight="?android:attr/dialogPreferredPadding">
 
-    <LinearLayout
-        android:id="@+id/staging"
+  <LinearLayout
+      android:id="@+id/staging"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:orientation="vertical"
+      android:visibility="invisible">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@android:style/TextAppearance.Material.Subhead"
+        android:text="@string/message_staging" />
+
+    <ProgressBar
+        android:id="@+id/progress_indeterminate"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:visibility="invisible">
+        android:paddingTop="8dp"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:indeterminate="true" />
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            style="@android:style/TextAppearance.Material.Subhead"
-            android:text="@string/message_staging" />
+  </LinearLayout>
 
-        <ProgressBar
-            android:id="@+id/progress_indeterminate"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingTop="8dp"
-            style="?android:attr/progressBarStyleHorizontal"
-            android:indeterminate="true" />
+  <LinearLayout
+      android:id="@+id/installing"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:orientation="vertical"
+      android:visibility="invisible">
 
-    </LinearLayout>
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@android:style/TextAppearance.Material.Subhead"
+        android:text="@string/installing" />
 
-    <LinearLayout
-        android:id="@+id/installing"
+    <ProgressBar
+        android:id="@+id/progress"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:visibility="invisible">
+        android:paddingTop="8dp"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:indeterminate="true" />
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            style="@android:style/TextAppearance.Material.Subhead"
-            android:text="@string/installing" />
+  </LinearLayout>
 
-        <ProgressBar
-            android:id="@+id/progress"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingTop="8dp"
-            style="?android:attr/progressBarStyleHorizontal"
-            android:indeterminate="true" />
+  <TextView
+      android:id="@+id/install_confirm_question"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      style="@android:style/TextAppearance.Material.Subhead"
+      android:text="@string/install_confirm_question"
+      android:visibility="invisible"
+      android:scrollbars="vertical" />
 
-    </LinearLayout>
+  <TextView
+      android:id="@+id/install_confirm_question_update"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      style="@android:style/TextAppearance.Material.Subhead"
+      android:text="@string/install_confirm_question_update"
+      android:visibility="invisible"
+      android:scrollbars="vertical" />
 
-    <TextView
-        android:id="@+id/install_confirm_question"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
-        android:text="@string/install_confirm_question"
-        android:visibility="invisible" />
+  <TextView
+      android:id="@+id/install_success"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      style="@android:style/TextAppearance.Material.Subhead"
+      android:text="@string/install_done"
+      android:visibility="invisible" />
 
-    <TextView
-        android:id="@+id/install_confirm_question_update"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
-        android:text="@string/install_confirm_question_update"
-        android:visibility="invisible" />
+  <TextView
+      android:id="@+id/install_failed"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      style="@android:style/TextAppearance.Material.Subhead"
+      android:text="@string/install_failed"
+      android:visibility="invisible" />
 
-    <TextView
-        android:id="@+id/install_success"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
-        android:text="@string/install_done"
-        android:visibility="invisible" />
+  <TextView
+      android:id="@+id/install_failed_blocked"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      style="@android:style/TextAppearance.Material.Subhead"
+      android:text="@string/install_failed_blocked"
+      android:visibility="invisible" />
 
-    <TextView
-        android:id="@+id/install_failed"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
-        android:text="@string/install_failed"
-        android:visibility="invisible" />
+  <TextView
+      android:id="@+id/install_failed_conflict"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      style="@android:style/TextAppearance.Material.Subhead"
+      android:text="@string/install_failed_conflict"
+      android:visibility="invisible" />
 
-    <TextView
-        android:id="@+id/install_failed_blocked"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
-        android:text="@string/install_failed_blocked"
-        android:visibility="invisible" />
+  <TextView
+      android:id="@+id/install_failed_incompatible"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      style="@android:style/TextAppearance.Material.Subhead"
+      android:text="@string/install_failed_incompatible"
+      android:visibility="invisible" />
 
-    <TextView
-        android:id="@+id/install_failed_conflict"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
-        android:text="@string/install_failed_conflict"
-        android:visibility="invisible" />
+  <TextView
+      android:id="@+id/install_failed_invalid_apk"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      style="@android:style/TextAppearance.Material.Subhead"
+      android:text="@string/install_failed_invalid_apk"
+      android:visibility="invisible" />
 
-    <TextView
-        android:id="@+id/install_failed_incompatible"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
-        android:text="@string/install_failed_incompatible"
-        android:visibility="invisible" />
-
-    <TextView
-        android:id="@+id/install_failed_invalid_apk"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead"
-        android:text="@string/install_failed_invalid_apk"
-        android:visibility="invisible" />
-
-</FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
index 5666c0e..434e333 100644
--- a/packages/PackageInstaller/res/layout/uninstall_content_view.xml
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -18,31 +18,36 @@
 <!-- Check box that is displayed in the activity resolver UI for the user
      to make their selection the preferred activity. -->
 
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:theme="?android:attr/alertDialogTheme"
-    android:orientation="vertical"
-    android:paddingTop="8dp"
-    android:paddingStart="?android:attr/dialogPreferredPadding"
-    android:paddingEnd="?android:attr/dialogPreferredPadding"
-    android:clipToPadding="false">
+    android:layout_height="wrap_content">
 
-    <TextView
-        android:id="@+id/message"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="@android:style/TextAppearance.Material.Subhead" />
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:theme="?android:attr/alertDialogTheme"
+      android:orientation="vertical"
+      android:paddingTop="8dp"
+      android:paddingStart="?android:attr/dialogPreferredPadding"
+      android:paddingEnd="?android:attr/dialogPreferredPadding"
+      android:clipToPadding="false">
 
-    <CheckBox
-        android:id="@+id/keepData"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="8dp"
-        android:layout_marginStart="-8dp"
-        android:paddingLeft="8sp"
-        android:visibility="gone"
-        style="@android:style/TextAppearance.Material.Subhead" />
+      <TextView
+          android:id="@+id/message"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          style="@android:style/TextAppearance.Material.Subhead" />
 
-</LinearLayout>
\ No newline at end of file
+      <CheckBox
+          android:id="@+id/keepData"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginTop="8dp"
+          android:layout_marginStart="-8dp"
+          android:paddingLeft="8sp"
+          android:visibility="gone"
+          style="@android:style/TextAppearance.Material.Subhead" />
+
+  </LinearLayout>
+</ScrollView>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index cf6aab6..e95a8e6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -51,6 +51,7 @@
 import android.text.Html;
 import android.text.Spanned;
 import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -174,6 +175,7 @@
         }
 
         viewToEnable.setVisibility(View.VISIBLE);
+        viewToEnable.setMovementMethod(new ScrollingMovementMethod());
 
         mEnableOk = true;
         mOk.setEnabled(true);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
index dbe32cc..0a4aa48 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -22,6 +22,7 @@
 import android.content.DialogInterface;
 import android.os.Bundle;
 import android.text.Html;
+import android.text.method.ScrollingMovementMethod;
 import android.view.View;
 import android.widget.TextView;
 import androidx.annotation.NonNull;
@@ -94,6 +95,7 @@
             viewToEnable = dialogView.requireViewById(R.id.install_confirm_question);
         }
         viewToEnable.setVisibility(View.VISIBLE);
+        viewToEnable.setMovementMethod(new ScrollingMovementMethod());
 
         return mDialog;
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
index bb7e857..3acf075 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.foundation.clickable
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import com.android.settingslib.spa.framework.common.EntryMacro
@@ -107,14 +106,9 @@
 ) {
     val onClickWithLog = wrapOnClickWithLog(model.onClick)
     val enabled = model.enabled()
-    val modifier = remember(enabled) {
-        if (onClickWithLog != null) {
-            Modifier.clickable(
-                enabled = enabled,
-                onClick = onClickWithLog
-            )
-        } else Modifier
-    }
+    val modifier = if (onClickWithLog != null) {
+        Modifier.clickable(enabled = enabled, onClick = onClickWithLog)
+    } else Modifier
     EntryHighlight {
         BasePreference(
             title = model.title,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 452dc03..d23cd0c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -35,11 +35,13 @@
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
 import com.android.systemui.keyguard.ui.composable.section.NotificationSection
 import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
@@ -63,6 +65,7 @@
     private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
+    private val mediaCarouselSection: MediaCarouselSection,
     private val clockInteractor: KeyguardClockInteractor,
 ) : ComposableLockscreenSceneBlueprint {
 
@@ -112,10 +115,16 @@
 
                         if (viewModel.isLargeClockVisible) {
                             Spacer(modifier = Modifier.weight(weight = 1f))
-                            with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+                            with(clockSection) {
+                                LargeClock(
+                                    modifier = Modifier.fillMaxWidth(),
+                                )
+                            }
                         }
 
-                        if (viewModel.areNotificationsVisible) {
+                        with(mediaCarouselSection) { MediaCarousel() }
+
+                        if (viewModel.areNotificationsVisible(resources = resources)) {
                             with(notificationSection) {
                                 Notifications(
                                     modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 71c60c7..c422c4b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
 import com.android.systemui.keyguard.ui.composable.section.NotificationSection
 import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
@@ -63,6 +64,7 @@
     private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
+    private val mediaCarouselSection: MediaCarouselSection,
     private val clockInteractor: KeyguardClockInteractor,
 ) : ComposableLockscreenSceneBlueprint {
 
@@ -115,7 +117,9 @@
                             with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
                         }
 
-                        if (viewModel.areNotificationsVisible) {
+                        with(mediaCarouselSection) { MediaCarousel() }
+
+                        if (viewModel.areNotificationsVisible(resources = resources)) {
                             with(notificationSection) {
                                 Notifications(
                                     modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index af836b6..d218425 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
 import com.android.systemui.keyguard.ui.composable.section.NotificationSection
 import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
@@ -70,6 +71,7 @@
     private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
+    private val mediaCarouselSection: MediaCarouselSection,
     private val clockInteractor: KeyguardClockInteractor,
     private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
 ) : ComposableLockscreenSceneBlueprint {
@@ -100,6 +102,14 @@
                                 modifier = Modifier.fillMaxHeight().weight(weight = 1f),
                                 horizontalAlignment = Alignment.CenterHorizontally,
                             ) {
+                                with(clockSection) {
+                                    SmallClock(
+                                        burnInParams = burnIn.parameters,
+                                        onTopChanged = burnIn.onSmallClockTopChanged,
+                                        modifier = Modifier.fillMaxWidth(),
+                                    )
+                                }
+
                                 with(smartSpaceSection) {
                                     SmartSpace(
                                         burnInParams = burnIn.parameters,
@@ -121,9 +131,13 @@
                                     )
                                 }
 
-                                Spacer(modifier = Modifier.weight(weight = 1f))
-                                with(clockSection) { LargeClock() }
-                                Spacer(modifier = Modifier.weight(weight = 1f))
+                                if (viewModel.isLargeClockVisible) {
+                                    Spacer(modifier = Modifier.weight(weight = 1f))
+                                    with(clockSection) { LargeClock() }
+                                    Spacer(modifier = Modifier.weight(weight = 1f))
+                                }
+
+                                with(mediaCarouselSection) { MediaCarousel() }
                             }
                             with(notificationSection) {
                                 val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
index e2e7a95..f86623f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -41,12 +41,14 @@
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
 import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
 import com.android.systemui.keyguard.ui.composable.section.NotificationSection
 import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.composable.section.WeatherClockSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.res.R
 import com.android.systemui.shade.LargeScreenHeaderHelper
 import dagger.Binds
@@ -68,6 +70,7 @@
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
     private val clockInteractor: KeyguardClockInteractor,
+    private val mediaCarouselSection: MediaCarouselSection,
 ) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
@@ -107,7 +110,9 @@
                             )
                         }
 
-                        if (viewModel.areNotificationsVisible) {
+                        with(mediaCarouselSection) { MediaCarousel() }
+
+                        if (viewModel.areNotificationsVisible(resources = resources)) {
                             with(notificationSection) {
                                 Notifications(
                                     modifier = Modifier.fillMaxWidth().weight(weight = 1f)
@@ -228,6 +233,7 @@
     private val clockInteractor: KeyguardClockInteractor,
     private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
     private val weatherClockSection: WeatherClockSection,
+    private val mediaCarouselSection: MediaCarouselSection,
 ) : ComposableLockscreenSceneBlueprint {
     override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
 
@@ -276,6 +282,8 @@
                                                 ),
                                     )
                                 }
+
+                                with(mediaCarouselSection) { MediaCarousel() }
                             }
                             with(notificationSection) {
                                 val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 335c915..152cc67 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
 import javax.inject.Inject
 
 /** Provides small clock and large clock composables for the default clock face. */
@@ -49,7 +48,6 @@
     private val viewModel: KeyguardClockViewModel,
     private val clockInteractor: KeyguardClockInteractor,
     private val aodBurnInViewModel: AodBurnInViewModel,
-    private val lockscreenSmartspaceController: LockscreenSmartspaceController,
 ) {
 
     @Composable
@@ -62,15 +60,11 @@
         val currentClock by viewModel.currentClock.collectAsState()
         viewModel.clock = currentClock
 
-        if (clockSize != KeyguardClockSwitch.SMALL) {
+        if (clockSize != KeyguardClockSwitch.SMALL || currentClock?.smallClock?.view == null) {
             onTopChanged(null)
             return
         }
 
-        if (currentClock?.smallClock?.view == null) {
-            return
-        }
-
         val view = LocalView.current
 
         DisposableEffect(view) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
new file mode 100644
index 0000000..dae120c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.section
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.dimensionResource
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.viewmodel.MediaCarouselViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.res.R
+import com.android.systemui.util.animation.MeasurementInput
+import javax.inject.Inject
+import javax.inject.Named
+
+class MediaCarouselSection
+@Inject
+constructor(
+    private val mediaCarouselController: MediaCarouselController,
+    @param:Named(MediaModule.KEYGUARD) private val mediaHost: MediaHost,
+    private val mediaCarouselViewModel: MediaCarouselViewModel,
+) {
+
+    @Composable
+    fun SceneScope.MediaCarousel(modifier: Modifier = Modifier) {
+        if (!mediaCarouselViewModel.isMediaVisible) {
+            return
+        }
+
+        if (mediaCarouselController.mediaFrame == null) {
+            return
+        }
+
+        val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded)
+        // TODO(b/312714128): MediaPlayer background size is not as expected.
+        MediaCarousel(
+            modifier =
+                modifier.height(mediaHeight).fillMaxWidth().onSizeChanged { size ->
+                    // Notify controller to size the carousel for the
+                    // current space
+                    mediaHost.measurementInput = MeasurementInput(size.width, size.height)
+                    mediaCarouselController.setSceneContainerSize(size.width, size.height)
+                },
+            mediaHost = mediaHost,
+            layoutWidth = 0, // Layout width is not used.
+            layoutHeight = with(LocalDensity.current) { mediaHeight.toPx() }.toInt(),
+            carouselController = mediaCarouselController,
+        )
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 61b2d4e..d3e4553 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -16,9 +16,12 @@
 
 package com.android.systemui.media.controls.ui.composable
 
+import android.view.ViewGroup
+import android.widget.FrameLayout
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.contains
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -45,6 +48,20 @@
 
     AndroidView(
         modifier = modifier.element(MediaCarousel.Elements.Content),
-        factory = { _ -> carouselController.mediaFrame },
+        factory = { context ->
+            FrameLayout(context).apply {
+                val mediaFrame = carouselController.mediaFrame
+                (mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
+                addView(mediaFrame)
+            }
+        },
+        update = {
+            if (it.contains(carouselController.mediaFrame)) {
+                return@AndroidView
+            }
+            val mediaFrame = carouselController.mediaFrame
+            (mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
+            it.addView(mediaFrame)
+        },
     )
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index b611e0a..36919d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -1052,32 +1052,6 @@
             biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
             faceAuthenticateIsCalled()
         }
-    @Test
-    fun authFailedCallAfterAuthLockedOutErrorShouldBeIgnored() =
-        testScope.runTest {
-            initCollectors()
-            allPreconditionsToRunFaceAuthAreTrue()
-            runCurrent()
-            assertThat(canFaceAuthRun()).isTrue()
-
-            underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
-            runCurrent()
-
-            faceAuthenticateIsCalled()
-            authenticationCallback.value.onAuthenticationError(
-                FACE_ERROR_LOCKOUT_PERMANENT,
-                "Too many attempts, face not available"
-            )
-
-            val lockoutError = authStatus() as ErrorFaceAuthenticationStatus
-            assertThat(lockedOut()).isTrue()
-            assertThat(lockoutError.isLockoutError()).isTrue()
-
-            authenticationCallback.value.onAuthenticationFailed()
-            runCurrent()
-
-            assertThat(authStatus()).isEqualTo(lockoutError)
-        }
 
     private suspend fun TestScope.testGatingCheckForFaceAuth(
         gatingCheckModifier: suspend () -> Unit
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 3484025..cd4db2f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -79,34 +79,66 @@
     }
 
     @Test
-    fun dozeAmountTransitionTest() = runTest {
-        val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
+    fun dozeAmountTransitionTest_AodToFromLockscreen() =
+        testScope.runTest {
+            val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
 
-        val steps = mutableListOf<TransitionStep>()
+            val steps = mutableListOf<TransitionStep>()
 
-        steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
-        steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
-        steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
-        steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
-        steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
-        steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
-        steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+            steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+            steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+            steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+            steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
 
-        steps.forEach {
-            repository.sendTransitionStep(it)
-            runCurrent()
+            steps.forEach {
+                repository.sendTransitionStep(it)
+                runCurrent()
+            }
+
+            assertThat(dozeAmountSteps.subList(0, 3))
+                .isEqualTo(
+                    listOf(
+                        steps[0].copy(value = 1f - steps[0].value),
+                        steps[1].copy(value = 1f - steps[1].value),
+                        steps[2].copy(value = 1f - steps[2].value),
+                    )
+                )
+            assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
         }
 
-        assertThat(dozeAmountSteps.subList(0, 3))
-            .isEqualTo(
-                listOf(
-                    steps[0].copy(value = 1f - steps[0].value),
-                    steps[1].copy(value = 1f - steps[1].value),
-                    steps[2].copy(value = 1f - steps[2].value),
+    @Test
+    fun dozeAmountTransitionTest_AodToFromGone() =
+        testScope.runTest {
+            val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
+
+            val steps = mutableListOf<TransitionStep>()
+
+            steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
+            steps.add(TransitionStep(AOD, GONE, 0.3f, RUNNING))
+            steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
+            steps.add(TransitionStep(GONE, AOD, 0f, STARTED))
+            steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING))
+            steps.add(TransitionStep(GONE, AOD, 0.3f, RUNNING))
+            steps.add(TransitionStep(GONE, AOD, 1f, FINISHED))
+
+            steps.forEach {
+                repository.sendTransitionStep(it)
+                runCurrent()
+            }
+
+            assertThat(dozeAmountSteps.subList(0, 3))
+                .isEqualTo(
+                    listOf(
+                        steps[0].copy(value = 1f - steps[0].value),
+                        steps[1].copy(value = 1f - steps[1].value),
+                        steps[2].copy(value = 1f - steps[2].value),
+                    )
                 )
-            )
-        assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
-    }
+            assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
+        }
 
     @Test
     fun finishedKeyguardStateTests() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index d4dd2ac..ad1cef1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -46,6 +47,7 @@
     fun setup() {
         with(kosmos) {
             fakeFeatureFlagsClassic.set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, true)
+            overrideResource(R.bool.config_use_split_notification_shade, false)
             underTest = lockscreenContentViewModel
         }
     }
@@ -87,11 +89,21 @@
         }
 
     @Test
+    fun areNotificationsVisible_splitShadeTrue_true() =
+        with(kosmos) {
+            testScope.runTest {
+                overrideResource(R.bool.config_use_split_notification_shade, true)
+                kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+
+                assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+            }
+        }
+    @Test
     fun areNotificationsVisible_withSmallClock_true() =
         with(kosmos) {
             testScope.runTest {
                 kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
-                assertThat(underTest.areNotificationsVisible).isTrue()
+                assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
             }
         }
 
@@ -100,7 +112,7 @@
         with(kosmos) {
             testScope.runTest {
                 kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
-                assertThat(underTest.areNotificationsVisible).isFalse()
+                assertThat(underTest.areNotificationsVisible(context.resources)).isFalse()
             }
         }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index e139466..bef9515 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -36,10 +36,10 @@
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
-import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Test
 import org.junit.runner.RunWith
 
 @ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
index 28f5eba..86b3f33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
@@ -102,6 +102,24 @@
             values.forEach { assertThat(it).isEqualTo(0f) }
         }
 
+
+    @Test
+    fun lockscreenAlphaFadesOutAndFinishesVisible() =
+        testScope.runTest {
+            val alpha by collectValues(underTest.lockscreenAlpha)
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DOZING,
+                testScope,
+            )
+
+            assertThat(alpha[0]).isEqualTo(1f)
+            // Halfway through, it will have faded out
+            assertThat(alpha[1]).isEqualTo(0f)
+            // FINISHED alpha should be visible, to support pulsing
+            assertThat(alpha[2]).isEqualTo(1f)
+        }
+
     @Test
     fun deviceEntryBackgroundViewDisappear() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 7a564ac..43ab93a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -33,10 +33,10 @@
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth
-import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Test
 import org.junit.runner.RunWith
 
 @ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index fff0a31..667f516 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -615,6 +615,7 @@
     private fun TestScope.emulatePendingTransitionProgress(
         expectedVisible: Boolean = true,
     ) {
+        val isVisible by collectLastValue(sceneContainerViewModel.isVisible)
         assertWithMessage("The FakeSceneDataSource has to be paused for this to do anything.")
             .that(fakeSceneDataSource.isPaused)
             .isTrue()
@@ -651,7 +652,7 @@
         runCurrent()
 
         assertWithMessage("Visibility mismatch after scene transition from $from to $to!")
-            .that(sceneContainerViewModel.isVisible.value)
+            .that(isVisible)
             .isEqualTo(expectedVisible)
         assertThat(sceneContainerViewModel.currentScene.value).isEqualTo(to)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index dd3eb68..db94c39 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.scene.domain.interactor
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,6 +33,7 @@
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
@@ -275,4 +278,18 @@
             underTest.setVisible(true, "reason")
             assertThat(isVisible).isTrue()
         }
+
+    @Test
+    fun isVisible_duringRemoteUserInteraction_forcedVisible() =
+        testScope.runTest {
+            underTest.setVisible(false, "reason")
+            val isVisible by collectLastValue(underTest.isVisible)
+            assertThat(isVisible).isFalse()
+            underTest.onRemoteUserInteractionStarted("reason")
+            assertThat(isVisible).isTrue()
+
+            underTest.onUserInteractionFinished()
+
+            assertThat(isVisible).isFalse()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index ffbdafe..27ae8b6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.scene.ui.viewmodel
 
+import android.view.MotionEvent
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -35,9 +34,9 @@
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -50,7 +49,7 @@
 
     private val kosmos = testKosmos()
     private val testScope by lazy { kosmos.testScope }
-    private val interactor by lazy { kosmos.sceneInteractor }
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val fakeSceneDataSource = kosmos.fakeSceneDataSource
     private val sceneContainerConfig = kosmos.sceneContainerConfig
     private val falsingManager = kosmos.fakeFalsingManager
@@ -62,7 +61,7 @@
         kosmos.fakeSceneContainerFlags.enabled = true
         underTest =
             SceneContainerViewModel(
-                sceneInteractor = interactor,
+                sceneInteractor = sceneInteractor,
                 falsingInteractor = kosmos.falsingInteractor,
                 powerInteractor = kosmos.powerInteractor,
             )
@@ -74,10 +73,10 @@
             val isVisible by collectLastValue(underTest.isVisible)
             assertThat(isVisible).isTrue()
 
-            interactor.setVisible(false, "reason")
+            sceneInteractor.setVisible(false, "reason")
             assertThat(isVisible).isFalse()
 
-            interactor.setVisible(true, "reason")
+            sceneInteractor.setVisible(true, "reason")
             assertThat(isVisible).isTrue()
         }
 
@@ -199,4 +198,20 @@
             underTest.onMotionEvent(mock())
             assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
         }
+
+    @Test
+    fun remoteUserInteraction_keepsContainerVisible() =
+        testScope.runTest {
+            sceneInteractor.setVisible(false, "reason")
+            val isVisible by collectLastValue(underTest.isVisible)
+            assertThat(isVisible).isFalse()
+            sceneInteractor.onRemoteUserInteractionStarted("reason")
+            assertThat(isVisible).isTrue()
+
+            underTest.onMotionEvent(
+                mock { whenever(actionMasked).thenReturn(MotionEvent.ACTION_UP) }
+            )
+
+            assertThat(isVisible).isFalse()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 6390e82..db4d42f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.FakeGlobalSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -142,6 +143,12 @@
         }
     }
 
+    @Override
+    public void SysuiSetup() throws Exception {
+        super.SysuiSetup();
+        mSetFlagsRule.disableFlags(NotificationThrottleHun.FLAG_NAME);
+    }
+
     @Test
     public void testShowNotification_addsEntry() {
         final BaseHeadsUpManager alm = createHeadsUpManager();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
index ec23f76..c032d7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -136,6 +137,8 @@
 
     @Before
     public void setUp() {
+        mSetFlagsRule.disableFlags(NotificationThrottleHun.FLAG_NAME);
+
         when(mShadeInteractor.isAnyExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
         final AccessibilityManagerWrapper accessibilityMgr =
                 mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 05e07a7..169a4e0 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -413,7 +413,6 @@
                     listenForDozing(this)
                     if (migrateClocksToBlueprint()) {
                         listenForDozeAmountTransition(this)
-                        listenForAnyStateToAodTransition(this)
                     } else {
                         listenForDozeAmount(this)
                     }
@@ -522,19 +521,6 @@
         }
     }
 
-    /**
-     * When keyguard is displayed again after being gone, the clock must be reset to full dozing.
-     */
-    @VisibleForTesting
-    internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
-        return scope.launch {
-            keyguardTransitionInteractor.transitionStepsToState(AOD)
-                .filter { it.transitionState == TransitionState.STARTED }
-                .filter { it.from != LOCKSCREEN }
-                .collect { handleDoze(1f) }
-        }
-    }
-
     @VisibleForTesting
     internal fun listenForDozing(scope: CoroutineScope): Job {
         return scope.launch {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 363dd01..f528ec8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -18,11 +18,16 @@
 
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.SystemClock;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.util.Log;
+import android.util.Pair;
 import android.view.View;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -40,6 +45,16 @@
 public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
         extends ViewController<T> {
     /**
+     * Pair representing:
+     *   first - BiometricSource the currently displayed message is associated with.
+     *   second - Timestamp the biometric message came in uptimeMillis.
+     * This Pair can be null if the message is not associated with a biometric.
+     */
+    @Nullable
+    private Pair<BiometricSourceType, Long> mMessageBiometricSource = null;
+    private static final Long SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS = 3500L;
+
+    /**
      * Delay before speaking an accessibility announcement. Used to prevent
      * lift-to-type from interrupting itself.
      */
@@ -149,12 +164,42 @@
      * Sets a message to the underlying text view.
      */
     public void setMessage(CharSequence s, boolean animate) {
+        setMessage(s, animate, null);
+    }
+
+    /**
+     * Sets a message to the underlying text view.
+     */
+    public void setMessage(CharSequence s, BiometricSourceType biometricSourceType) {
+        setMessage(s, true, biometricSourceType);
+    }
+
+    private void setMessage(
+            CharSequence s,
+            boolean animate,
+            BiometricSourceType biometricSourceType) {
+        final long uptimeMillis = SystemClock.uptimeMillis();
+        if (skipShowingFaceMessage(biometricSourceType, uptimeMillis)) {
+            Log.d("KeyguardMessageAreaController", "Skip showing face message \"" + s + "\"");
+            return;
+        }
+        mMessageBiometricSource =  new Pair<>(biometricSourceType, uptimeMillis);
         if (mView.isDisabled()) {
             return;
         }
         mView.setMessage(s, animate);
     }
 
+    private boolean skipShowingFaceMessage(
+            BiometricSourceType biometricSourceType, Long currentUptimeMillis
+    ) {
+        return mMessageBiometricSource != null
+                && biometricSourceType == BiometricSourceType.FACE
+                && mMessageBiometricSource.first == BiometricSourceType.FINGERPRINT
+                && (currentUptimeMillis - mMessageBiometricSource.second)
+                    < SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS;
+    }
+
     public void setMessage(int resId) {
         String message = resId != 0 ? mView.getResources().getString(resId) : null;
         setMessage(message);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 38c2829e..b7667a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -34,6 +34,7 @@
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
 import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
@@ -437,7 +438,8 @@
         }
     };
 
-    private final OnSubscriptionsChangedListener mSubscriptionListener =
+    @VisibleForTesting
+    final OnSubscriptionsChangedListener mSubscriptionListener =
             new OnSubscriptionsChangedListener() {
                 @Override
                 public void onSubscriptionsChanged() {
@@ -586,16 +588,14 @@
     private void handleSimSubscriptionInfoChanged() {
         Assert.isMainThread();
         mLogger.v("onSubscriptionInfoChanged()");
-        List<SubscriptionInfo> sil = mSubscriptionManager
-                .getCompleteActiveSubscriptionInfoList();
-        if (sil != null) {
-            for (SubscriptionInfo subInfo : sil) {
+        List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
+        if (!subscriptionInfos.isEmpty()) {
+            for (SubscriptionInfo subInfo : subscriptionInfos) {
                 mLogger.logSubInfo(subInfo);
             }
         } else {
             mLogger.v("onSubscriptionInfoChanged: list is null");
         }
-        List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
 
         // Hack level over 9000: Because the subscription id is not yet valid when we see the
         // first update in handleSimStateChange, we need to force refresh all SIM states
@@ -658,18 +658,18 @@
 
     /**
      * @return List of SubscriptionInfo records, maybe empty but never null.
+     *
+     * Note that this method will filter out any subscription which is PROFILE_CLASS_PROVISIONING
      */
     public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
         List<SubscriptionInfo> sil = mSubscriptionInfo;
         if (sil == null || forceReload) {
-            sil = mSubscriptionManager.getCompleteActiveSubscriptionInfoList();
+            mSubscriptionInfo = mSubscriptionManager.getCompleteActiveSubscriptionInfoList()
+                    .stream()
+                    .filter(subInfo -> subInfo.getProfileClass() != PROFILE_CLASS_PROVISIONING)
+                    .toList();
         }
-        if (sil == null) {
-            // getCompleteActiveSubscriptionInfoList was null callers expect an empty list.
-            mSubscriptionInfo = new ArrayList<>();
-        } else {
-            mSubscriptionInfo = sil;
-        }
+
         return new ArrayList<>(mSubscriptionInfo);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index d2ad096..ce4032a 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.keyguard.logging
 
+import android.hardware.biometrics.BiometricSourceType
 import com.android.systemui.biometrics.AuthRippleController
 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
 import com.android.systemui.log.LogBuffer
@@ -117,6 +118,26 @@
         )
     }
 
+    fun logDropNonFingerprintMessage(
+        message: CharSequence,
+        followUpMessage: CharSequence?,
+        biometricSourceType: BiometricSourceType?,
+    ) {
+        buffer.log(
+            KeyguardIndicationController.TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = message.toString()
+                str2 = followUpMessage?.toString()
+                str3 = biometricSourceType?.name
+            },
+            {
+                "droppingNonFingerprintMessage message=$str1 " +
+                    "followUpMessage:$str2 biometricSourceType:$str3"
+            }
+        )
+    }
+
     fun logUpdateBatteryIndication(
         powerIndication: String,
         pluggedIn: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 0bd44f0..f4cd5b9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -17,7 +17,6 @@
 package com.android.systemui.biometrics;
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.Flags.customBiometricPrompt;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
@@ -33,6 +32,7 @@
 import android.hardware.biometrics.BiometricAuthenticator.Modality;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -402,7 +402,12 @@
             @Nullable FaceSensorPropertiesInternal faceProps,
             @NonNull VibratorHelper vibratorHelper
     ) {
-        if (Utils.isBiometricAllowed(config.mPromptInfo) || customBiometricPrompt()) {
+        // Set this value before showing either of the prompt.
+        mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
+                config.mPromptInfo);
+
+        if (Utils.isBiometricAllowed(config.mPromptInfo)
+                || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
             addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
         } else if (constraintBp() && Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
             addCredentialView(true, false);
@@ -411,7 +416,6 @@
         }
     }
 
-
     private void addBiometricView(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
             @NonNull PromptViewModel viewModel,
             @Nullable FingerprintSensorPropertiesInternal fpProps,
@@ -534,7 +538,8 @@
                 () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
         if (constraintBp()) {
             // Do nothing on attachment with constraintLayout
-        } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo) || customBiometricPrompt()) {
+        } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo)
+                || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
             mBiometricScrollView.addView(mBiometricView.asView());
         } else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
             addCredentialView(true /* animatePanel */, false /* animateContents */);
@@ -819,6 +824,9 @@
 
         final Runnable endActionRunnable = () -> {
             setVisibility(View.INVISIBLE);
+            if (Flags.customBiometricPrompt()) {
+                mPromptSelectorInteractorProvider.get().resetPrompt();
+            }
             removeWindowIfAttached();
         };
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
index ad7bb0e..b87fadf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -16,8 +16,11 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import android.hardware.biometrics.Flags
 import android.hardware.biometrics.PromptInfo
 import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.Utils.isDeviceCredentialAllowed
 import com.android.systemui.biometrics.shared.model.PromptKind
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -65,6 +68,18 @@
      */
     val isConfirmationRequired: Flow<Boolean>
 
+    /**
+     * If biometric prompt without icon needs to show for displaying content prior to credential
+     * view.
+     */
+    val showBpWithoutIconForCredential: StateFlow<Boolean>
+
+    /**
+     * Update whether biometric prompt without icon needs to show for displaying content prior to
+     * credential view, which should be set before [setPrompt].
+     */
+    fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
+
     /** Update the prompt configuration, which should be set before [isShowing]. */
     fun setPrompt(
         promptInfo: PromptInfo,
@@ -129,6 +144,19 @@
             }
             .distinctUntilChanged()
 
+    private val _showBpWithoutIconForCredential: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
+
+    override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+        val hasCredentialViewShown = kind.value !is PromptKind.Biometric
+        val showBpForCredential =
+            Flags.customBiometricPrompt() &&
+                !Utils.isBiometricAllowed(promptInfo) &&
+                isDeviceCredentialAllowed(promptInfo) &&
+                promptInfo.contentView != null
+        _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
+    }
+
     override fun setPrompt(
         promptInfo: PromptInfo,
         userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index e3facff..94cea57 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -30,6 +30,7 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -59,6 +60,13 @@
     /** If the prompt is currently showing. */
     val isShowing: Flow<Boolean> = biometricPromptRepository.isShowing
 
+    /**
+     * If biometric prompt without icon needs to show for displaying content prior to credential
+     * view.
+     */
+    val showBpWithoutIconForCredential: StateFlow<Boolean> =
+        biometricPromptRepository.showBpWithoutIconForCredential
+
     /** Metadata about the current credential prompt, including app-supplied preferences. */
     val prompt: Flow<BiometricPromptRequest.Credential?> =
         combine(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index b3f9574..45816c1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
@@ -70,6 +71,18 @@
     /** Fingerprint sensor type */
     val sensorType: Flow<FingerprintSensorType>
 
+    /**
+     * If biometric prompt without icon needs to show for displaying content prior to credential
+     * view.
+     */
+    val showBpWithoutIconForCredential: StateFlow<Boolean>
+
+    /**
+     * Update whether biometric prompt without icon needs to show for displaying content prior to
+     * credential view, which should be set before [PromptRepository.setPrompt].
+     */
+    fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
+
     /** Use biometrics for authentication. */
     fun useBiometricsForAuthentication(
         promptInfo: PromptInfo,
@@ -154,6 +167,12 @@
 
     override val sensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType
 
+    override val showBpWithoutIconForCredential = promptRepository.showBpWithoutIconForCredential
+
+    override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+        promptRepository.setShouldShowBpWithoutIconForCredential(promptInfo)
+    }
+
     override fun useBiometricsForAuthentication(
         promptInfo: PromptInfo,
         userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 6133a51c..6f079e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -17,6 +17,7 @@
     val title: String,
     val subtitle: String,
     val description: String,
+    val contentView: PromptContentView?,
     val userInfo: BiometricUserInfo,
     val operationInfo: BiometricOperationInfo,
     val showEmergencyCallButton: Boolean,
@@ -33,11 +34,11 @@
             title = info.title?.toString() ?: "",
             subtitle = info.subtitle?.toString() ?: "",
             description = info.description?.toString() ?: "",
+            contentView = info.contentView,
             userInfo = userInfo,
             operationInfo = operationInfo,
             showEmergencyCallButton = info.isShowEmergencyCallButton
         ) {
-        val contentView: PromptContentView? = info.contentView
         val logoRes: Int = info.logoRes
         val logoBitmap: Bitmap? = info.logoBitmap
         val logoDescription: String? = info.logoDescription
@@ -54,6 +55,7 @@
             title = (info.deviceCredentialTitle ?: info.title)?.toString() ?: "",
             subtitle = (info.deviceCredentialSubtitle ?: info.subtitle)?.toString() ?: "",
             description = (info.deviceCredentialDescription ?: info.description)?.toString() ?: "",
+            contentView = info.contentView,
             userInfo = userInfo,
             operationInfo = operationInfo,
             showEmergencyCallButton = info.isShowEmergencyCallButton
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index cd5b124..ea39247 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -22,7 +22,6 @@
 import android.hardware.biometrics.BiometricAuthenticator
 import android.hardware.biometrics.BiometricConstants
 import android.hardware.biometrics.BiometricPrompt
-import android.hardware.biometrics.Flags.customBiometricPrompt
 import android.hardware.face.FaceManager
 import android.text.method.ScrollingMovementMethod
 import android.util.Log
@@ -151,17 +150,6 @@
             // these do not change and need to be set before any size transitions
             val modalities = viewModel.modalities.first()
 
-            // If there is no biometrics available, biometric prompt is showing just for displaying
-            // content, no authentication needed.
-            if (!(customBiometricPrompt() && modalities.isEmpty)) {
-                PromptIconViewBinder.bind(
-                    iconView,
-                    iconOverlayView,
-                    iconSizeOverride,
-                    viewModel,
-                )
-            }
-
             if (modalities.hasFingerprint) {
                 /**
                  * Load the given [rawResources] immediately so they are cached for use in the
@@ -233,6 +221,19 @@
                 )
             }
 
+            lifecycleScope.launch {
+                viewModel.showBpWithoutIconForCredential.collect {
+                    if (!it) {
+                        PromptIconViewBinder.bind(
+                            iconView,
+                            iconOverlayView,
+                            iconSizeOverride,
+                            viewModel,
+                        )
+                    }
+                }
+            }
+
             // TODO(b/251476085): migrate legacy icon controllers and remove
             // The fingerprint sensor is started by the legacy
             // AuthContainerView#onDialogAnimatedIn in all cases but the implicit coex flow
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index a37d916..8336d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -21,7 +21,6 @@
 import android.animation.ValueAnimator
 import android.graphics.Outline
 import android.graphics.Rect
-import android.hardware.biometrics.Flags
 import android.transition.AutoTransition
 import android.transition.TransitionManager
 import android.view.Surface
@@ -55,6 +54,7 @@
 import com.android.systemui.biometrics.ui.viewmodel.isRight
 import com.android.systemui.biometrics.ui.viewmodel.isSmall
 import com.android.systemui.biometrics.ui.viewmodel.isTop
+import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import kotlin.math.abs
@@ -111,14 +111,9 @@
 
             val smallConstraintSet = ConstraintSet()
             smallConstraintSet.clone(mediumConstraintSet)
-            viewsToHideWhenSmall.forEach { smallConstraintSet.setVisibility(it.id, View.GONE) }
 
             val largeConstraintSet = ConstraintSet()
             largeConstraintSet.clone(mediumConstraintSet)
-            viewsToHideWhenSmall.forEach { largeConstraintSet.setVisibility(it.id, View.GONE) }
-            largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
-            largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
-            largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
             largeConstraintSet.setGuidelineBegin(leftGuideline.id, 0)
             largeConstraintSet.setGuidelineEnd(rightGuideline.id, 0)
             largeConstraintSet.setGuidelineEnd(bottomGuideline.id, 0)
@@ -219,13 +214,18 @@
                     }
                 }
 
-                view.repeatWhenAttached {
-                    var currentSize: PromptSize? = null
-                    val modalities = viewModel.modalities.first()
-                    // TODO(b/288175072): Move all visibility settings together.
-                    //  If there is no biometrics available, biometric prompt is showing just for
-                    // displaying content, no authentication needed.
-                    if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+                fun setConstraintSetVisibility() {
+                    viewsToHideWhenSmall.forEach {
+                        mediumConstraintSet.setVisibility(it.id, it.showContentOrHide())
+                        largeConstraintSet.setVisibility(it.id, View.GONE)
+                        smallConstraintSet.setVisibility(it.id, View.GONE)
+                    }
+
+                    largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+                    largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+                    largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+
+                    if (viewModel.showBpWithoutIconForCredential.value) {
                         smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
                         smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
                         smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
@@ -233,12 +233,17 @@
                         mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
                         mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
                     }
+                }
+
+                view.repeatWhenAttached {
+                    var currentSize: PromptSize? = null
+
                     lifecycleScope.launch {
                         combine(viewModel.position, viewModel.size, ::Pair).collect {
                             (position, size) ->
                             view.doOnAttach {
                                 measureBounds(position)
-
+                                setConstraintSetVisibility()
                                 when {
                                     size.isSmall -> {
                                         val ratio =
@@ -313,7 +318,6 @@
                 // TODO(b/251476085): migrate the legacy panel controller and simplify this
                 view.repeatWhenAttached {
                     var currentSize: PromptSize? = null
-                    val modalities = viewModel.modalities.first()
                     lifecycleScope.launch {
                         /**
                          * View is only set visible in BiometricViewSizeBinder once PromptSize is
@@ -331,11 +335,13 @@
 
                             // prepare for animated size transitions
                             for (v in viewsToHideWhenSmall) {
-                                v.showContentOrHide(forceHide = size.isSmall)
+                                v.visibility = v.showContentOrHide(forceHide = size.isSmall)
                             }
-                            if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+
+                            if (viewModel.showBpWithoutIconForCredential.value) {
                                 iconHolderView.visibility = View.GONE
                             }
+
                             if (currentSize == null && size.isSmall) {
                                 iconHolderView.alpha = 0f
                             }
@@ -344,9 +350,9 @@
                             }
 
                             // TODO(b/302735104): Fix wrong height due to the delay of
-                            // PromptContentView. addOnLayoutChangeListener() will cause crash when
-                            // showing credential view, since |PromptIconViewModel| won't release
-                            // the flow.
+                            // PromptContentView. addOnLayoutChangeListener() will cause crash
+                            // when showing credential view, since |PromptIconViewModel| won't
+                            // release the flow.
                             // propagate size changes to legacy panel controller and animate
                             // transitions
                             view.doOnLayout {
@@ -460,15 +466,14 @@
     return r == Surface.ROTATION_90 || r == Surface.ROTATION_270
 }
 
-private fun View.showContentOrHide(forceHide: Boolean = false) {
+private fun View.showContentOrHide(forceHide: Boolean = false): Int {
     val isTextViewWithBlankText = this is TextView && this.text.isBlank()
     val isImageViewWithoutImage = this is ImageView && this.drawable == null
-    visibility =
-        if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
-            View.GONE
-        } else {
-            View.VISIBLE
-        }
+    return if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
+        View.GONE
+    } else {
+        View.VISIBLE
+    }
 }
 
 private fun View.centerX(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 03c5c53..46be8c7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -4,13 +4,13 @@
 import android.graphics.drawable.Drawable
 import android.text.InputType
 import com.android.internal.widget.LockPatternView
-import com.android.systemui.res.R
 import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.domain.interactor.CredentialStatus
 import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
 import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
 import com.android.systemui.biometrics.shared.model.BiometricUserInfo
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
 import javax.inject.Inject
 import kotlin.reflect.KClass
 import kotlinx.coroutines.flow.Flow
@@ -32,14 +32,16 @@
 
     /** Top level information about the prompt. */
     val header: Flow<CredentialHeaderViewModel> =
-        credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>().map {
-            request ->
+        combine(
+            credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>(),
+            credentialInteractor.showBpWithoutIconForCredential
+        ) { request, showBpWithoutIconForCredential ->
             BiometricPromptHeaderViewModelImpl(
                 request,
                 user = request.userInfo,
                 title = request.title,
-                subtitle = request.subtitle,
-                description = request.description,
+                subtitle = if (showBpWithoutIconForCredential) "" else request.subtitle,
+                description = if (showBpWithoutIconForCredential) "" else request.description,
                 icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId),
                 showEmergencyCallButton = request.showEmergencyCallButton
             )
@@ -145,7 +147,7 @@
                 .createLaunchEmergencyDialerIntent(null)
                 .setFlags(
                     android.content.Intent.FLAG_ACTIVITY_NEW_TASK or
-                            android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
+                        android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
                 )
         context.startActivity(intent)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index c933e0e..2c749ba 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -47,6 +47,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
@@ -123,6 +124,9 @@
     /** The kind of credential the user has. */
     val credentialKind: Flow<PromptKind> = promptSelectorInteractor.credentialKind
 
+    val showBpWithoutIconForCredential: StateFlow<Boolean> =
+        promptSelectorInteractor.showBpWithoutIconForCredential
+
     /** The label to use for the cancel button. */
     val negativeButtonText: Flow<String> =
         promptSelectorInteractor.prompt.map { it?.negativeButtonText ?: "" }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1157d97..19af371 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -51,6 +51,7 @@
 import com.android.systemui.controls.dagger.ControlsModule;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.demomode.dagger.DemoModeModule;
 import com.android.systemui.deviceentry.DeviceEntryModule;
 import com.android.systemui.display.DisplayModule;
@@ -365,7 +366,8 @@
             SysUiState sysUiState,
             FeatureFlags featureFlags,
             NotifPipelineFlags notifPipelineFlags,
-            @Main Executor sysuiMainExecutor) {
+            @Main Executor sysuiMainExecutor,
+            @UiBackground Executor sysuiUiBgExecutor) {
         return Optional.ofNullable(BubblesManager.create(context,
                 bubblesOptional,
                 notificationShadeWindowController,
@@ -384,7 +386,8 @@
                 sysUiState,
                 featureFlags,
                 notifPipelineFlags,
-                sysuiMainExecutor));
+                sysuiMainExecutor,
+                sysuiUiBgExecutor));
     }
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index cf7d601..baae986 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -62,6 +62,10 @@
 import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.UserRepository
 import com.google.errorprone.annotations.CompileTimeConstant
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -83,10 +87,6 @@
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
-import java.io.PrintWriter
-import java.util.Arrays
-import java.util.stream.Collectors
-import javax.inject.Inject
 
 /**
  * API to run face authentication and detection for device entry / on keyguard (as opposed to the
@@ -431,11 +431,11 @@
             override fun onAuthenticationFailed() {
                 _isAuthenticated.value = false
                 faceAuthLogger.authenticationFailed()
+                _authenticationStatus.value = FailedFaceAuthenticationStatus()
                 if (!_isLockedOut.value) {
                     // onAuthenticationError gets invoked before onAuthenticationFailed when the
                     // last auth attempt locks out face authentication.
-                    // Skip updating the authentication status in such a scenario.
-                    _authenticationStatus.value = FailedFaceAuthenticationStatus()
+                    // Skip onFaceAuthRequestCompleted in such a scenario.
                     onFaceAuthRequestCompleted()
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 9fc3692..6aed944 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -31,8 +31,8 @@
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.flow.sample
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -62,7 +62,6 @@
         listenForTransitionToCamera(scope, keyguardInteractor)
     }
 
-    @FlowPreview
     private fun listenForAlternateBouncerToLockscreenHubAodOrDozing() {
         scope.launch {
             keyguardInteractor.alternateBouncerShowing
@@ -71,7 +70,7 @@
                 // happening prematurely.
                 // This should eventually be removed in favor of
                 // [KeyguardTransitionInteractor#startDismissKeyguardTransition]
-                .sample(150L)
+                .onEach { delay(150L) }
                 .sampleCombine(
                     keyguardInteractor.primaryBouncerShowing,
                     startedKeyguardTransitionStep,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 7263ae9..cb1571e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -44,6 +44,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
@@ -287,7 +288,7 @@
         if (KeyguardWmStateRefactor.isEnabled) {
             // When the refactor is enabled, we no longer use isKeyguardGoingAway.
             scope.launch {
-                swipeToDismissInteractor.dismissFling.collect { _ ->
+                swipeToDismissInteractor.dismissFling.filterNotNull().collect { _ ->
                     startTransitionTo(KeyguardState.GONE)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index d1fd719..719edd7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -45,7 +45,6 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.shareIn
 
 /** Encapsulates business-logic related to the keyguard transitions. */
@@ -177,10 +176,16 @@
      * Lockscreen (0f).
      */
     val dozeAmountTransition: Flow<TransitionStep> =
-        merge(
-            aodToLockscreenTransition.map { step -> step.copy(value = 1f - step.value) },
-            lockscreenToAodTransition,
-        )
+        repository.transitions
+            .filter { step -> step.from == AOD || step.to == AOD }
+            .map { step ->
+                if (step.from == AOD) {
+                    step.copy(value = 1 - step.value)
+                } else {
+                    step
+                }
+            }
+            .shareIn(scope, SharingStarted.Eagerly, replay = 1)
 
     /** The last [TransitionStep] with a [TransitionState] of STARTED */
     val startedKeyguardTransitionStep: Flow<TransitionStep> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index 8a3b57b..5741b94 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -29,7 +29,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combineTransform
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.onStart
 
 /** Models UI state for the alpha of the AOD (always-on display). */
@@ -46,24 +45,23 @@
     /** The alpha level for the entire lockscreen while in AOD. */
     val alpha: Flow<Float> =
         combineTransform(
-                keyguardTransitionInteractor.transitions,
-                goneToAodTransitionViewModel.enterFromTopAnimationAlpha.onStart { emit(0f) },
-                goneToDozingTransitionViewModel.lockscreenAlpha.onStart { emit(0f) },
-                keyguardInteractor.keyguardAlpha.onStart { emit(1f) },
-            ) { step, goneToAodAlpha, goneToDozingAlpha, keyguardAlpha ->
-                if (step.to == GONE) {
-                    // When transitioning to GONE, only emit a value when complete as other
-                    // transitions may be controlling the alpha fade
-                    if (step.value == 1f) {
-                        emit(0f)
-                    }
-                } else if (step.from == GONE && step.to == AOD) {
-                    emit(goneToAodAlpha)
-                } else if (step.from == GONE && step.to == DOZING) {
-                    emit(goneToDozingAlpha)
-                } else if (!migrateClocksToBlueprint()) {
-                    emit(keyguardAlpha)
+            keyguardTransitionInteractor.transitions,
+            goneToAodTransitionViewModel.enterFromTopAnimationAlpha.onStart { emit(0f) },
+            goneToDozingTransitionViewModel.lockscreenAlpha.onStart { emit(0f) },
+            keyguardInteractor.keyguardAlpha.onStart { emit(1f) },
+        ) { step, goneToAodAlpha, goneToDozingAlpha, keyguardAlpha ->
+            if (step.to == GONE) {
+                // When transitioning to GONE, only emit a value when complete as other
+                // transitions may be controlling the alpha fade
+                if (step.value == 1f) {
+                    emit(0f)
                 }
+            } else if (step.from == GONE && step.to == AOD) {
+                emit(goneToAodAlpha)
+            } else if (step.from == GONE && step.to == DOZING) {
+                emit(goneToDozingAlpha)
+            } else if (!migrateClocksToBlueprint()) {
+                emit(keyguardAlpha)
             }
-            .distinctUntilChanged()
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 8665aab..7be390a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -28,10 +28,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
-import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
-import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
-import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
@@ -47,7 +43,6 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onStart
 
 /**
@@ -127,17 +122,9 @@
         params: BurnInParameters,
     ): Flow<BurnInModel> {
         return combine(
-            merge(
-                    keyguardTransitionInteractor.transition(GONE, AOD).map { it.value },
-                    keyguardTransitionInteractor.transition(AOD, PRIMARY_BOUNCER).map {
-                        1f - it.value
-                    },
-                    keyguardTransitionInteractor.transition(ALTERNATE_BOUNCER, AOD).map {
-                        it.value
-                    },
-                    keyguardTransitionInteractor.dozeAmountTransition.map { it.value },
-                )
-                .map { dozeAmount -> Interpolators.FAST_OUT_SLOW_IN.getInterpolation(dozeAmount) },
+            keyguardTransitionInteractor.dozeAmountTransition.map {
+                Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
+            },
             burnInInteractor.keyguardBurnIn,
         ) { interpolated, burnIn ->
             val useScaleOnly =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 921eb66..bdcaf09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -74,6 +74,10 @@
     private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
     private val glanceableHubToLockscreenTransitionViewModel:
         GlanceableHubToLockscreenTransitionViewModel,
+    private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+    private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
+    private val lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+    private val lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
     private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
     private val lockscreenToGlanceableHubTransitionViewModel:
         LockscreenToGlanceableHubTransitionViewModel,
@@ -133,17 +137,24 @@
     fun alpha(viewState: ViewStateAccessor): Flow<Float> {
         return combine(
                 communalInteractor.isIdleOnCommunal,
-                keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
+                keyguardTransitionInteractor
+                    .transitionValue(GONE)
+                    .map { it == 1f }
+                    .onStart { emit(false) }
+                    .distinctUntilChanged(),
                 // The transitions are mutually exclusive, so they are safe to merge to get the last
                 // value emitted by any of them. Do not add flows that cannot make this guarantee.
                 merge(
-                        aodAlphaViewModel.alpha,
                         alphaOnShadeExpansion,
                         keyguardInteractor.dismissAlpha.filterNotNull(),
                         alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
                         aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
                         dozingToLockscreenTransitionViewModel.lockscreenAlpha,
                         glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
+                        goneToAodTransitionViewModel.enterFromTopAnimationAlpha,
+                        goneToDozingTransitionViewModel.lockscreenAlpha,
+                        lockscreenToAodTransitionViewModel.lockscreenAlpha(viewState),
+                        lockscreenToDozingTransitionViewModel.lockscreenAlpha,
                         lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
                         lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
                         lockscreenToGoneTransitionViewModel.lockscreenAlpha(viewState),
@@ -156,8 +167,8 @@
                         primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
                     )
                     .onStart { emit(1f) }
-            ) { isIdleOnCommunal, goneValue, alpha ->
-                if (isIdleOnCommunal || goneValue == 1f) {
+            ) { isIdleOnCommunal, gone, alpha ->
+                if (isIdleOnCommunal || gone) {
                     // Keyguard should not show while the communal hub is fully visible. This check
                     // is added since at the moment, closing the notification shade will cause the
                     // keyguard alpha to be set back to 1. Also ensure keyguard is never visible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index d792889..23320be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.SplitShadeStateController
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -39,6 +40,7 @@
     private val interactor: KeyguardBlueprintInteractor,
     private val authController: AuthController,
     val longPress: KeyguardLongPressViewModel,
+    val splitShadeStateController: SplitShadeStateController,
 ) {
     private val clockSize = clockInteractor.clockSize
 
@@ -46,8 +48,10 @@
         get() = authController.isUdfpsSupported
     val isLargeClockVisible: Boolean
         get() = clockSize.value == KeyguardClockSwitch.LARGE
-    val areNotificationsVisible: Boolean
-        get() = !isLargeClockVisible
+    fun areNotificationsVisible(resources: Resources): Boolean {
+        return !isLargeClockVisible ||
+            splitShadeStateController.shouldUseSplitNotificationShade(resources)
+    }
 
     fun getSmartSpacePaddingTop(resources: Resources): Int {
         return if (isLargeClockVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 7bf51a7..1f9f304 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
@@ -67,6 +68,15 @@
             onCancel = { 1f },
         )
 
+    fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+        var startAlpha = 1f
+        return transitionAnimation.sharedFlow(
+            duration = 500.milliseconds,
+            onStart = { startAlpha = viewState.alpha() },
+            onStep = { MathUtils.lerp(startAlpha, 1f, it) },
+        )
+    }
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
             isUdfpsEnrolledAndEnabled ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index b60c52b..c836f01 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -44,6 +44,14 @@
             to = KeyguardState.DOZING,
         )
 
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.sharedFlow(
+            duration = 250.milliseconds,
+            onStep = { 1 - it },
+            onFinish = { 1f },
+            onCancel = { 1f },
+        )
+
     val shortcutsAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
             duration = 250.milliseconds,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt
new file mode 100644
index 0000000..027a739
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import javax.inject.Inject
+
+class MediaCarouselViewModel @Inject constructor(mediaDataManager: MediaDataManager) {
+    val isMediaVisible: Boolean = mediaDataManager.hasActiveMediaOrRecommendation()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 000f3c09d..5f8b5dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -221,7 +221,7 @@
                         // If scene framework is enabled, set the scene container window to
                         // visible and let the touch "slip" into that window.
                         if (mSceneContainerFlags.isEnabled()) {
-                            mSceneInteractor.get().setVisible(true, "swipe down on launcher");
+                            mSceneInteractor.get().onRemoteUserInteractionStarted("launcher swipe");
                         } else {
                             mShadeViewControllerLazy.get().startInputFocusTransfer();
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index a302194..e60dff1 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -49,6 +49,13 @@
     private val _isVisible = MutableStateFlow(true)
     val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow()
 
+    /**
+     * Whether there's an ongoing remotely-initiated user interaction.
+     *
+     * For more information see the logic in `SceneInteractor` that mutates this.
+     */
+    val isRemoteUserInteractionOngoing = MutableStateFlow(false)
+
     private val defaultTransitionState = ObservableTransitionState.Idle(config.initialSceneKey)
     private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
     val transitionState: StateFlow<ObservableTransitionState> =
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 0add444..6b7c672 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -31,6 +31,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
@@ -121,7 +122,21 @@
             )
 
     /** Whether the scene container is visible. */
-    val isVisible: StateFlow<Boolean> = repository.isVisible
+    val isVisible: StateFlow<Boolean> =
+        combine(
+                repository.isVisible,
+                repository.isRemoteUserInteractionOngoing,
+            ) { isVisible, isRemoteUserInteractionOngoing ->
+                isVisibleInternal(
+                    raw = isVisible,
+                    isRemoteUserInteractionOngoing = isRemoteUserInteractionOngoing,
+                )
+            }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = isVisibleInternal()
+            )
 
     /**
      * Returns the keys of all scenes in the container.
@@ -164,7 +179,14 @@
         repository.changeScene(toScene, transitionKey)
     }
 
-    /** Sets the visibility of the container. */
+    /**
+     * Sets the visibility of the container.
+     *
+     * Please do not call this from outside of the scene framework. If you are trying to force the
+     * visibility to visible or invisible, prefer making changes to the existing caller of this
+     * method or to upstream state used to calculate [isVisible]; for an example of the latter,
+     * please see [onRemoteUserInteractionStarted] and [onUserInteractionFinished].
+     */
     fun setVisible(isVisible: Boolean, loggingReason: String) {
         val wasVisible = repository.isVisible.value
         if (wasVisible == isVisible) {
@@ -180,6 +202,31 @@
     }
 
     /**
+     * Notifies that a remote user interaction has begun.
+     *
+     * This is a user interaction that originates outside of the UI of the scene container and
+     * possibly outside of the System UI process itself.
+     *
+     * As an example, consider the dragging that can happen in the launcher that expands the shade.
+     * This is a user interaction that begins remotely (as it starts in the launcher process) and is
+     * then rerouted by window manager to System UI. While the user interaction definitely continues
+     * within the System UI process and code, it also originates remotely.
+     */
+    fun onRemoteUserInteractionStarted(loggingReason: String) {
+        logger.logRemoteUserInteractionStarted(loggingReason)
+        repository.isRemoteUserInteractionOngoing.value = true
+    }
+
+    /**
+     * Notifies that the current user interaction (internally or remotely started, see
+     * [onRemoteUserInteractionStarted]) has finished.
+     */
+    fun onUserInteractionFinished() {
+        logger.logUserInteractionFinished()
+        repository.isRemoteUserInteractionOngoing.value = false
+    }
+
+    /**
      * Binds the given flow so the system remembers it.
      *
      * Note that you must call is with `null` when the UI is done or risk a memory leak.
@@ -187,4 +234,11 @@
     fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
         repository.setTransitionState(transitionState)
     }
+
+    private fun isVisibleInternal(
+        raw: Boolean = repository.isVisible.value,
+        isRemoteUserInteractionOngoing: Boolean = repository.isRemoteUserInteractionOngoing.value,
+    ): Boolean {
+        return raw || isRemoteUserInteractionOngoing
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index d59fcff..cbf7b3e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -95,6 +95,26 @@
         )
     }
 
+    fun logRemoteUserInteractionStarted(
+        reason: String,
+    ) {
+        logBuffer.log(
+            tag = TAG,
+            level = LogLevel.INFO,
+            messageInitializer = { str1 = reason },
+            messagePrinter = { "remote user interaction started, reason: $str3" },
+        )
+    }
+
+    fun logUserInteractionFinished() {
+        logBuffer.log(
+            tag = TAG,
+            level = LogLevel.INFO,
+            messageInitializer = {},
+            messagePrinter = { "user interaction finished" },
+        )
+    }
+
     companion object {
         private const val TAG = "SceneFramework"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 4cd3baa..91861aa 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -68,6 +68,12 @@
     fun onMotionEvent(event: MotionEvent) {
         powerInteractor.onUserTouch()
         falsingInteractor.onTouchEvent(event)
+        if (
+            event.actionMasked == MotionEvent.ACTION_UP ||
+                event.actionMasked == MotionEvent.ACTION_CANCEL
+        ) {
+            sceneInteractor.onUserInteractionFinished()
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7b330b0..fd3c480 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -3074,7 +3074,9 @@
     }
 
     private void onClosingFinished() {
-        mOpenCloseListener.onClosingFinished();
+        if (mOpenCloseListener != null) {
+            mOpenCloseListener.onClosingFinished();
+        }
         setClosingWithAlphaFadeout(false);
         mMediaHierarchyManager.closeGuts();
     }
@@ -4705,7 +4707,9 @@
             if (mSplitShadeEnabled && !isKeyguardShowing()) {
                 mQsController.setExpandImmediate(true);
             }
-            mOpenCloseListener.onOpenStarted();
+            if (mOpenCloseListener != null) {
+                mOpenCloseListener.onOpenStarted();
+            }
         }
         if (state == STATE_CLOSED) {
             mQsController.setExpandImmediate(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 19fe60a..1ec86ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -71,6 +71,7 @@
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
 import android.text.format.Formatter;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
@@ -187,6 +188,7 @@
     private CharSequence mTransientIndication;
     private CharSequence mBiometricMessage;
     private CharSequence mBiometricMessageFollowUp;
+    private BiometricSourceType mBiometricMessageSource;
     protected ColorStateList mInitialTextColorState;
     private boolean mVisible;
     private boolean mOrganizationOwnedDevice;
@@ -206,7 +208,7 @@
     private int mBatteryLevel;
     private boolean mBatteryPresent = true;
     private long mChargingTimeRemaining;
-    private String mBiometricErrorMessageToShowOnScreenOn;
+    private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn;
     private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
     private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
     private boolean mInited;
@@ -225,15 +227,18 @@
                 mIsActiveDreamLockscreenHosted = isLockscreenHosted;
                 updateDeviceEntryIndication(false);
             };
-    private final ScreenLifecycle.Observer mScreenObserver =
-            new ScreenLifecycle.Observer() {
+    private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
         @Override
         public void onScreenTurnedOn() {
             mHandler.removeMessages(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON);
             if (mBiometricErrorMessageToShowOnScreenOn != null) {
                 String followUpMessage = mFaceLockedOutThisAuthSession
                         ? faceLockedOutFollowupMessage() : null;
-                showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn, followUpMessage);
+                showBiometricMessage(
+                        mBiometricErrorMessageToShowOnScreenOn.first,
+                        followUpMessage,
+                        mBiometricErrorMessageToShowOnScreenOn.second
+                );
                 // We want to keep this message around in case the screen was off
                 hideBiometricMessageDelayed(DEFAULT_HIDE_DELAY_MS);
                 mBiometricErrorMessageToShowOnScreenOn = null;
@@ -879,8 +884,35 @@
         updateTransient();
     }
 
-    private void showBiometricMessage(CharSequence biometricMessage) {
-        showBiometricMessage(biometricMessage, null);
+    private void showSuccessBiometricMessage(
+            CharSequence biometricMessage,
+            @Nullable CharSequence biometricMessageFollowUp,
+            BiometricSourceType biometricSourceType
+    ) {
+        showBiometricMessage(biometricMessage, biometricMessageFollowUp, biometricSourceType, true);
+    }
+
+    private void showSuccessBiometricMessage(CharSequence biometricMessage,
+            BiometricSourceType biometricSourceType) {
+        showSuccessBiometricMessage(biometricMessage, null, biometricSourceType);
+    }
+
+    private void showBiometricMessage(CharSequence biometricMessage,
+            BiometricSourceType biometricSourceType) {
+        showBiometricMessage(biometricMessage, null, biometricSourceType, false);
+    }
+
+    private void showBiometricMessage(
+            CharSequence biometricMessage,
+            @Nullable CharSequence biometricMessageFollowUp,
+            BiometricSourceType biometricSourceType
+    ) {
+        showBiometricMessage(
+                biometricMessage,
+                biometricMessageFollowUp,
+                biometricSourceType,
+                false
+        );
     }
 
     /**
@@ -889,15 +921,33 @@
      * by {@link KeyguardIndicationRotateTextViewController}, see class for rotating message
      * logic.
      */
-    private void showBiometricMessage(CharSequence biometricMessage,
-            @Nullable CharSequence biometricMessageFollowUp) {
+    private void showBiometricMessage(
+            CharSequence biometricMessage,
+            @Nullable CharSequence biometricMessageFollowUp,
+            BiometricSourceType biometricSourceType,
+            boolean isSuccessMessage
+    ) {
         if (TextUtils.equals(biometricMessage, mBiometricMessage)
+                && biometricSourceType == mBiometricMessageSource
                 && TextUtils.equals(biometricMessageFollowUp, mBiometricMessageFollowUp)) {
             return;
         }
 
+        if (!isSuccessMessage
+                && mBiometricMessageSource == FINGERPRINT
+                && biometricSourceType != FINGERPRINT) {
+            // drop all non-fingerprint biometric messages if there's a fingerprint message showing
+            mKeyguardLogger.logDropNonFingerprintMessage(
+                    biometricMessage,
+                    biometricMessageFollowUp,
+                    biometricSourceType
+            );
+            return;
+        }
+
         mBiometricMessage = biometricMessage;
         mBiometricMessageFollowUp = biometricMessageFollowUp;
+        mBiometricMessageSource = biometricSourceType;
 
         mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
         hideBiometricMessageDelayed(
@@ -914,6 +964,7 @@
         if (mBiometricMessage != null || mBiometricMessageFollowUp != null) {
             mBiometricMessage = null;
             mBiometricMessageFollowUp = null;
+            mBiometricMessageSource = null;
             mHideBiometricMessageHandler.cancel();
             updateBiometricMessage();
         }
@@ -1085,7 +1136,8 @@
                 } else {
                     message = mContext.getString(R.string.keyguard_retry);
                 }
-                mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
+                mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState,
+                        null);
             }
         } else {
             final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
@@ -1097,34 +1149,40 @@
                         || mAccessibilityManager.isTouchExplorationEnabled();
                 if (udfpsSupported && faceAuthenticated) { // co-ex
                     if (a11yEnabled) {
-                        showBiometricMessage(
+                        showSuccessBiometricMessage(
                                 mContext.getString(R.string.keyguard_face_successful_unlock),
-                                mContext.getString(R.string.keyguard_unlock)
+                                mContext.getString(R.string.keyguard_unlock),
+                                FACE
                         );
                     } else {
-                        showBiometricMessage(
+                        showSuccessBiometricMessage(
                                 mContext.getString(R.string.keyguard_face_successful_unlock),
-                                mContext.getString(R.string.keyguard_unlock_press)
+                                mContext.getString(R.string.keyguard_unlock_press),
+                                FACE
                         );
                     }
                 } else if (faceAuthenticated) { // face-only
-                    showBiometricMessage(
+                    showSuccessBiometricMessage(
                             mContext.getString(R.string.keyguard_face_successful_unlock),
-                            mContext.getString(R.string.keyguard_unlock)
+                            mContext.getString(R.string.keyguard_unlock),
+                            FACE
                     );
                 } else if (udfpsSupported) { // udfps-only
                     if (a11yEnabled) {
-                        showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+                        showSuccessBiometricMessage(
+                                mContext.getString(R.string.keyguard_unlock),
+                                null
+                        );
                     } else {
-                        showBiometricMessage(mContext.getString(
-                                R.string.keyguard_unlock_press));
+                        showSuccessBiometricMessage(mContext.getString(
+                                R.string.keyguard_unlock_press), null);
                     }
                 } else { // no security or unlocked by a trust agent
-                    showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+                    showSuccessBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
                 }
             } else {
                 // suggest swiping up for the primary authentication bouncer
-                showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+                showBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
             }
         }
     }
@@ -1228,6 +1286,13 @@
                     && msgId != BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
             final boolean faceAuthFailed = biometricSourceType == FACE
                     && msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed
+            if (faceAuthFailed && mFaceLockedOutThisAuthSession) {
+                mKeyguardLogger.logBiometricMessage(
+                        "skipped showing faceAuthFailed message due to lockout",
+                        msgId,
+                        helpString);
+                return;
+            }
             final boolean fpAuthFailed = biometricSourceType == FINGERPRINT
                     && msgId == BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; // ran matcher & failed
             final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint();
@@ -1245,49 +1310,55 @@
                     mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString);
                 }
                 mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
-                        mInitialTextColorState);
+                        mInitialTextColorState, biometricSourceType);
             } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
                 if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
                     showBiometricMessage(
                             helpString,
-                            mContext.getString(R.string.keyguard_suggest_fingerprint)
+                            mContext.getString(R.string.keyguard_suggest_fingerprint),
+                            biometricSourceType
                     );
                 } else if (faceAuthFailed && isUnlockWithFingerprintPossible) {
                     showBiometricMessage(
                             mContext.getString(R.string.keyguard_face_failed),
-                            mContext.getString(R.string.keyguard_suggest_fingerprint)
+                            mContext.getString(R.string.keyguard_suggest_fingerprint),
+                            biometricSourceType
                     );
                 } else if (fpAuthFailed
                         && mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()) {
                     // face had already previously unlocked the device, so instead of showing a
                     // fingerprint error, tell them they have already unlocked with face auth
                     // and how to enter their device
-                    showBiometricMessage(
+                    showSuccessBiometricMessage(
                             mContext.getString(R.string.keyguard_face_successful_unlock),
-                            mContext.getString(R.string.keyguard_unlock)
+                            mContext.getString(R.string.keyguard_unlock),
+                            null
                     );
                 } else if (fpAuthFailed
                         && mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())) {
-                    showBiometricMessage(
+                    showSuccessBiometricMessage(
                             getTrustGrantedIndication(),
-                            mContext.getString(R.string.keyguard_unlock)
+                            mContext.getString(R.string.keyguard_unlock),
+                            null
                     );
                 } else if (faceAuthUnavailable) {
                     showBiometricMessage(
                             helpString,
                             isUnlockWithFingerprintPossible
                                     ? mContext.getString(R.string.keyguard_suggest_fingerprint)
-                                    : mContext.getString(R.string.keyguard_unlock)
+                                    : mContext.getString(R.string.keyguard_unlock),
+                            biometricSourceType
                     );
                 } else {
-                    showBiometricMessage(helpString);
+                    showBiometricMessage(helpString, biometricSourceType);
                 }
             } else if (faceAuthFailed) {
                 // show action to unlock
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
                         TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
             } else {
-                mBiometricErrorMessageToShowOnScreenOn = helpString;
+                mBiometricErrorMessageToShowOnScreenOn =
+                        new Pair<>(helpString, biometricSourceType);
                 mHandler.sendMessageDelayed(
                         mHandler.obtainMessage(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON),
                         1000);
@@ -1333,7 +1404,7 @@
             } else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) {
                 handleFaceLockoutError(errString);
             } else {
-                showErrorMessageNowOrLater(errString, null);
+                showErrorMessageNowOrLater(errString, null, FACE);
             }
         }
 
@@ -1343,7 +1414,7 @@
                         msgId,
                         errString);
             } else {
-                showErrorMessageNowOrLater(errString, null);
+                showErrorMessageNowOrLater(errString, null, FINGERPRINT);
             }
         }
 
@@ -1371,7 +1442,7 @@
 
         @Override
         public void onTrustAgentErrorMessage(CharSequence message) {
-            showBiometricMessage(message);
+            showBiometricMessage(message, null);
         }
 
         @Override
@@ -1459,12 +1530,13 @@
         // had too many unsuccessful attempts.
         if (!mFaceLockedOutThisAuthSession) {
             mFaceLockedOutThisAuthSession = true;
-            showErrorMessageNowOrLater(errString, followupMessage);
+            showErrorMessageNowOrLater(errString, followupMessage, FACE);
         } else if (!mAuthController.isUdfpsFingerDown()) {
             // On subsequent lockouts, we show a more generic locked out message.
             showErrorMessageNowOrLater(
                     mContext.getString(R.string.keyguard_face_unlock_unavailable),
-                    followupMessage);
+                    followupMessage,
+                    FACE);
         }
     }
 
@@ -1484,7 +1556,8 @@
                     && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 showBiometricMessage(
                         deferredFaceMessage,
-                        mContext.getString(R.string.keyguard_suggest_fingerprint)
+                        mContext.getString(R.string.keyguard_suggest_fingerprint),
+                        FACE
                 );
             } else {
                 // otherwise, don't show any message
@@ -1496,7 +1569,8 @@
             // user to manually retry.
             showBiometricMessage(
                     deferredFaceMessage,
-                    mContext.getString(R.string.keyguard_unlock)
+                    mContext.getString(R.string.keyguard_unlock),
+                    FACE
             );
         } else {
             // Face-only
@@ -1510,13 +1584,15 @@
                 getCurrentUser()) && mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed();
     }
 
-    private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
+    private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg,
+            BiometricSourceType biometricSourceType) {
         if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
-            mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
+            mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState,
+                    biometricSourceType);
         } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
-            showBiometricMessage(errString, followUpMsg);
+            showBiometricMessage(errString, followUpMsg, biometricSourceType);
         } else {
-            mBiometricErrorMessageToShowOnScreenOn = errString;
+            mBiometricErrorMessageToShowOnScreenOn = new Pair<>(errString, biometricSourceType);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index d10ca3d..6b47ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -239,10 +239,22 @@
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
         mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         layoutParams.height = mStatusBarHeight - waterfallTopInset;
+        updateSystemIconsContainerHeight();
         updatePaddings();
         setLayoutParams(layoutParams);
     }
 
+    private void updateSystemIconsContainerHeight() {
+        View systemIconsContainer = findViewById(R.id.system_icons);
+        ViewGroup.LayoutParams layoutParams = systemIconsContainer.getLayoutParams();
+        int newSystemIconsHeight =
+                getResources().getDimensionPixelSize(R.dimen.status_bar_system_icons_height);
+        if (layoutParams.height != newSystemIconsHeight) {
+            layoutParams.height = newSystemIconsHeight;
+            systemIconsContainer.setLayoutParams(layoutParams);
+        }
+    }
+
     private void updatePaddings() {
         int statusBarPaddingStart = getResources().getDimensionPixelSize(
                 R.dimen.status_bar_padding_start);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 29fd225..d1055c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -23,7 +23,6 @@
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
 
 import android.content.Context;
@@ -98,6 +97,7 @@
 import com.android.systemui.unfold.FoldAodAnimationController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
 
 import dagger.Lazy;
 
@@ -348,6 +348,8 @@
     private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor;
     private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor;
 
+    private final JavaAdapter mJavaAdapter;
+
     @Inject
     public StatusBarKeyguardViewManager(
             Context context,
@@ -378,7 +380,8 @@
             Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor,
             Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
             SelectedUserInteractor selectedUserInteractor,
-            Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor
+            Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor,
+            JavaAdapter javaAdapter
     ) {
         mContext = context;
         mViewMediatorCallback = callback;
@@ -411,6 +414,7 @@
         mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy;
         mSelectedUserInteractor = selectedUserInteractor;
         mSurfaceBehindInteractor = surfaceBehindInteractor;
+        mJavaAdapter = javaAdapter;
     }
 
     KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -481,8 +485,7 @@
 
         if (KeyguardWmStateRefactor.isEnabled()) {
             // Show the keyguard views whenever we've told WM that the lockscreen is visible.
-            collectFlow(
-                    getViewRootImpl().getView(),
+            mJavaAdapter.alwaysCollectFlow(
                     combineFlows(
                             mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
                             mSurfaceBehindInteractor.get().isAnimatingSurface(),
@@ -781,7 +784,7 @@
                     }
 
                     updateAlternateBouncerShowing(mAlternateBouncerInteractor.show());
-                    setKeyguardMessage(message, null);
+                    setKeyguardMessage(message, null, null);
                     return;
                 }
 
@@ -1444,11 +1447,12 @@
     }
 
     /** Display security message to relevant KeyguardMessageArea. */
-    public void setKeyguardMessage(String message, ColorStateList colorState) {
+    public void setKeyguardMessage(String message, ColorStateList colorState,
+            BiometricSourceType biometricSourceType) {
         if (mAlternateBouncerInteractor.isVisibleState()) {
             if (mKeyguardMessageAreaController != null) {
                 DeviceEntryUdfpsRefactor.assertInLegacyMode();
-                mKeyguardMessageAreaController.setMessage(message);
+                mKeyguardMessageAreaController.setMessage(message, biometricSourceType);
             }
         } else {
             mPrimaryBouncerInteractor.showMessage(message, colorState);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 65dede8..cb61534 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -28,6 +28,7 @@
 import static com.android.server.notification.Flags.screenshareNotificationHiding;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
 
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -50,6 +51,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.flags.FeatureFlags;
@@ -97,6 +99,7 @@
     private final Context mContext;
     private final Bubbles mBubbles;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
+    private final KeyguardStateController mKeyguardStateController;
     private final ShadeController mShadeController;
     private final IStatusBarService mBarService;
     private final INotificationManager mNotificationManager;
@@ -109,6 +112,7 @@
     private final NotifPipeline mNotifPipeline;
     private final NotifPipelineFlags mNotifPipelineFlags;
     private final Executor mSysuiMainExecutor;
+    private final Executor mSysuiUiBgExecutor;
 
     private final Bubbles.SysuiProxy mSysuiProxy;
     // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
@@ -116,6 +120,8 @@
     private final StatusBarWindowCallback mStatusBarWindowCallback;
     private final Runnable mSensitiveStateChangedListener;
     private boolean mPanelExpanded;
+    private boolean mKeyguardShowing;
+    private boolean mDreamingOrInPreview;
 
     /**
      * Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present
@@ -140,7 +146,8 @@
             SysUiState sysUiState,
             FeatureFlags featureFlags,
             NotifPipelineFlags notifPipelineFlags,
-            Executor sysuiMainExecutor) {
+            Executor sysuiMainExecutor,
+            Executor sysuiUiBgExecutor) {
         if (bubblesOptional.isPresent()) {
             return new BubblesManager(context,
                     bubblesOptional.get(),
@@ -160,7 +167,8 @@
                     sysUiState,
                     featureFlags,
                     notifPipelineFlags,
-                    sysuiMainExecutor);
+                    sysuiMainExecutor,
+                    sysuiUiBgExecutor);
         } else {
             return null;
         }
@@ -185,10 +193,12 @@
             SysUiState sysUiState,
             FeatureFlags featureFlags,
             NotifPipelineFlags notifPipelineFlags,
-            Executor sysuiMainExecutor) {
+            Executor sysuiMainExecutor,
+            Executor sysuiUiBgExecutor) {
         mContext = context;
         mBubbles = bubbles;
         mNotificationShadeWindowController = notificationShadeWindowController;
+        mKeyguardStateController = keyguardStateController;
         mShadeController = shadeController;
         mNotificationManager = notificationManager;
         mDreamManager = dreamManager;
@@ -200,6 +210,7 @@
         mNotifPipeline = notifPipeline;
         mNotifPipelineFlags = notifPipelineFlags;
         mSysuiMainExecutor = sysuiMainExecutor;
+        mSysuiUiBgExecutor = sysuiUiBgExecutor;
 
         mBarService = statusBarService == null
                 ? IStatusBarService.Stub.asInterface(
@@ -208,12 +219,11 @@
 
         setupNotifPipeline();
 
-        keyguardStateController.addCallback(new KeyguardStateController.Callback() {
+        // TODO(b/327410864): use KeyguardTransitionInteractor to listen for keyguard changes
+        mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
             public void onKeyguardShowingChanged() {
-                boolean isUnlockedShade = !keyguardStateController.isShowing()
-                        && !isDreamingOrInPreview();
-                bubbles.onStatusBarStateChanged(isUnlockedShade);
+                updateKeyguardAndDreamingState();
             }
         });
 
@@ -256,6 +266,14 @@
                         mPanelExpanded = panelExpanded;
                         mBubbles.onNotificationPanelExpandedChanged(panelExpanded);
                     }
+                    if (!mKeyguardShowing && mDreamingOrInPreview && !isDreaming) {
+                        // We check for dreaming state changes when keyguard status changes.
+                        // This causes us to miss events if dreaming state changes after keyguard.
+                        // Add a check here for the case where keyguard is dismissed before
+                        // dreaming state changes. Otherwise bubbles remain invisible.
+                        // TODO(b/327410864): use KeyguardTransitionInteractor for dreaming changes
+                        updateKeyguardAndDreamingState();
+                    }
                 };
         notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
 
@@ -395,6 +413,19 @@
         mBubbles.setSysuiProxy(mSysuiProxy);
     }
 
+    private void updateKeyguardAndDreamingState() {
+        mSysuiUiBgExecutor.execute(() -> {
+            mKeyguardShowing = mKeyguardStateController.isShowing();
+            mDreamingOrInPreview = isDreamingOrInPreview();
+            boolean isUnlockedShade = !mKeyguardShowing && !mDreamingOrInPreview;
+            ProtoLog.d(WM_SHELL_BUBBLES,
+                    "handleKeyguardOrDreamChange isUnlockedShade=%b keyguardShowing=%b "
+                            + "dreamingOrInPreview=%b",
+                    isUnlockedShade, mKeyguardShowing, mDreamingOrInPreview);
+            mBubbles.onStatusBarStateChanged(isUnlockedShade);
+        });
+    }
+
     private boolean isDreamingOrInPreview() {
         try {
             return mDreamManager.isDreamingOrInPreview();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 3026966..0f8a813 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -336,48 +336,6 @@
         }
 
     @Test
-    fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
-        runBlocking(IMMEDIATE) {
-            val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
-                .thenReturn(transitionStep)
-
-            val job = underTest.listenForAnyStateToAodTransition(this)
-            transitionStep.value =
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    transitionState = TransitionState.STARTED,
-                )
-            yield()
-
-            verify(animations, times(2)).doze(1f)
-
-            job.cancel()
-        }
-
-    @Test
-    fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
-        runBlocking(IMMEDIATE) {
-            val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
-                .thenReturn(transitionStep)
-
-            val job = underTest.listenForAnyStateToAodTransition(this)
-            transitionStep.value =
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    transitionState = TransitionState.STARTED,
-                )
-            yield()
-
-            verify(animations, never()).doze(1f)
-
-            job.cancel()
-        }
-
-    @Test
     fun unregisterListeners_validate() =
         runBlocking(IMMEDIATE) {
             underTest.unregisterListeners()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index d4522d0..93e7602 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -19,11 +19,14 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.hardware.biometrics.BiometricSourceType;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -119,4 +122,51 @@
         when(mKeyguardMessageArea.getText()).thenReturn(msg);
         assertThat(mMessageAreaController.getMessage()).isEqualTo(msg);
     }
+
+    @Test
+    public void testFingerprintMessageUpdate() {
+        String msg = "fpMessage";
+        mMessageAreaController.setMessage(
+                msg, BiometricSourceType.FINGERPRINT
+        );
+        verify(mKeyguardMessageArea).setMessage(msg, /* animate= */ true);
+
+        String msg2 = "fpMessage2";
+        mMessageAreaController.setMessage(
+                msg2, BiometricSourceType.FINGERPRINT
+        );
+        verify(mKeyguardMessageArea).setMessage(msg2, /* animate= */ true);
+    }
+
+    @Test
+    public void testFaceMessageDroppedWhileFingerprintMessageShowing() {
+        String fpMsg = "fpMessage";
+        mMessageAreaController.setMessage(
+                fpMsg, BiometricSourceType.FINGERPRINT
+        );
+        verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());
+
+        String faceMessage = "faceMessage";
+        mMessageAreaController.setMessage(
+                faceMessage, BiometricSourceType.FACE
+        );
+        verify(mKeyguardMessageArea, never())
+                .setMessage(eq(faceMessage), /* animate= */ anyBoolean());
+    }
+
+    @Test
+    public void testGenericMessageShowsAfterFingerprintMessageShowing() {
+        String fpMsg = "fpMessage";
+        mMessageAreaController.setMessage(
+                fpMsg, BiometricSourceType.FINGERPRINT
+        );
+        verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());
+
+        String genericMessage = "genericMessage";
+        mMessageAreaController.setMessage(
+                genericMessage, null
+        );
+        verify(mKeyguardMessageArea)
+                .setMessage(eq(genericMessage), /* animate= */ anyBoolean());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 538daee..336a97e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -26,6 +26,8 @@
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_DEFAULT;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -187,6 +189,11 @@
             TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
             TEST_CARRIER_ID, 0);
+    private static final SubscriptionInfo TEST_SUBSCRIPTION_PROVISIONING = new SubscriptionInfo(
+            1, "", 0,
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
+            TEST_CARRIER_ID, PROFILE_CLASS_PROVISIONING);
     private static final int FINGERPRINT_SENSOR_ID = 1;
 
     @Mock
@@ -1204,7 +1211,8 @@
         assertThat(mKeyguardUpdateMonitor.mSimDatas.get(TEST_SUBSCRIPTION.getSubscriptionId()))
                 .isNotNull();
 
-        when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(null);
+        when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList())
+                .thenReturn(new ArrayList<>());
         mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged(
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         mTestableLooper.processAllMessages();
@@ -1216,6 +1224,37 @@
     }
 
     @Test
+    public void testActiveSubscriptionList_filtersProvisioningNetworks() {
+        List<SubscriptionInfo> list = new ArrayList<>();
+        list.add(TEST_SUBSCRIPTION_PROVISIONING);
+        when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list);
+        mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+        assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).isEmpty();
+
+        SubscriptionInfo.Builder b = new SubscriptionInfo.Builder(TEST_SUBSCRIPTION_PROVISIONING);
+        b.setProfileClass(PROFILE_CLASS_DEFAULT);
+        SubscriptionInfo validInfo = b.build();
+
+        list.clear();
+        list.add(validInfo);
+        mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+        assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).hasSize(1);
+    }
+
+    @Test
+    public void testActiveSubscriptionList_filtersProvisioningNetworks_untilValid() {
+        List<SubscriptionInfo> list = new ArrayList<>();
+        list.add(TEST_SUBSCRIPTION_PROVISIONING);
+        when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list);
+        mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+        assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).isEmpty();
+
+    }
+
+    @Test
     public void testIsUserUnlocked() {
         // mUserManager will report the user as unlocked on @Before
         assertThat(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 2c1a87d..10b86ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -21,8 +21,8 @@
 import android.hardware.biometrics.BiometricConstants
 import android.hardware.biometrics.BiometricManager
 import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
-import android.hardware.biometrics.Flags.customBiometricPrompt
 import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
 import android.os.Handler
@@ -40,7 +40,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
@@ -148,8 +147,6 @@
 
     @Before
     fun setup() {
-        mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-        mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
         displayRepository = FakeDisplayRepository()
 
         displayStateInteractor =
@@ -388,9 +385,10 @@
     }
 
     @Test
-    fun testShowCredentialUI() {
+    fun testShowCredentialUI_withDescription() {
+        mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         val container = initializeFingerprintContainer(
-            authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+                authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
         )
         waitForIdleSync()
 
@@ -399,16 +397,12 @@
     }
 
     @Test
-    fun testShowBiometricUIWhenCustomBpEnabledAndNoSensors() {
-        mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+    fun testShowCredentialUI_withCustomBp() {
         val container = initializeFingerprintContainer(
-                authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+                authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+                isUsingContentView = true
         )
-        waitForIdleSync()
-
-        assertThat(customBiometricPrompt()).isTrue()
-        assertThat(container.hasBiometricPrompt()).isTrue()
-        assertThat(container.hasCredentialView()).isFalse()
+        checkBpShowsForCredentialAndGoToCredential(container)
     }
 
     @Test
@@ -512,11 +506,13 @@
 
     private fun initializeFingerprintContainer(
         authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
-        addToView: Boolean = true
+        addToView: Boolean = true,
+        isUsingContentView: Boolean = false,
     ) = initializeContainer(
         TestAuthContainerView(
             authenticators = authenticators,
-            fingerprintProps = fingerprintSensorPropertiesInternal()
+            fingerprintProps = fingerprintSensorPropertiesInternal(),
+                isUsingContentView = isUsingContentView,
         ),
         addToView
     )
@@ -549,7 +545,8 @@
     private inner class TestAuthContainerView(
         authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
         fingerprintProps: List<FingerprintSensorPropertiesInternal> = listOf(),
-        faceProps: List<FaceSensorPropertiesInternal> = listOf()
+        faceProps: List<FaceSensorPropertiesInternal> = listOf(),
+        isUsingContentView: Boolean = false,
     ) : AuthContainerView(
         Config().apply {
             mContext = this@AuthContainerViewTest.context
@@ -559,6 +556,9 @@
             mSkipAnimation = true
             mPromptInfo = PromptInfo().apply {
                 this.authenticators = authenticators
+                if (isUsingContentView) {
+                    this.contentView = PromptVerticalListContentView.Builder().build()
+                }
             }
             mOpPackageName = OP_PACKAGE_NAME
         },
@@ -614,6 +614,17 @@
         val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
         assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.systemBars()) == 0).isTrue()
     }
+
+    private fun checkBpShowsForCredentialAndGoToCredential(container: TestAuthContainerView) {
+        waitForIdleSync()
+        assertThat(container.hasBiometricPrompt()).isTrue()
+        assertThat(container.hasCredentialView()).isFalse()
+
+        container.animateToCredentialUI(false)
+        waitForIdleSync()
+        assertThat(container.hasBiometricPrompt()).isFalse()
+        assertThat(container.hasCredentialView()).isTrue()
+    }
 }
 
 private fun AuthContainerView.hasBiometricPrompt() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index b39e09d..7b972d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import android.hardware.biometrics.BiometricManager
 import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
@@ -131,6 +133,52 @@
         }
 
     @Test
+    fun showBpWithoutIconForCredential_withCustomBp() =
+        testScope.runTest {
+            for (case in
+                listOf(
+                    PromptKind.Biometric(),
+                    PromptKind.Pin,
+                    PromptKind.Password,
+                    PromptKind.Pattern
+                )) {
+                val hasCredentialViewShown = case !is PromptKind.Biometric
+                val promptInfo =
+                    PromptInfo().apply {
+                        authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+                        contentView = PromptVerticalListContentView.Builder().build()
+                    }
+                repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
+                repository.setShouldShowBpWithoutIconForCredential(promptInfo)
+
+                assertThat(repository.showBpWithoutIconForCredential.value)
+                    .isEqualTo(!hasCredentialViewShown)
+            }
+        }
+
+    @Test
+    fun showBpWithoutIconForCredential_withDescription() =
+        testScope.runTest {
+            for (case in
+                listOf(
+                    PromptKind.Biometric(),
+                    PromptKind.Pin,
+                    PromptKind.Password,
+                    PromptKind.Pattern
+                )) {
+                val promptInfo =
+                    PromptInfo().apply {
+                        authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+                        description = "description"
+                    }
+                repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
+                repository.setShouldShowBpWithoutIconForCredential(promptInfo)
+
+                assertThat(repository.showBpWithoutIconForCredential.value).isFalse()
+            }
+        }
+
+    @Test
     fun setsAndUnsetsPrompt() =
         testScope.runTest {
             val kind = PromptKind.Pin
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 52b4275..2817780 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -147,7 +147,7 @@
         testScope.runTest { useCredentialAndReset(Utils.CREDENTIAL_PIN) }
 
     @Test
-    fun usePattermCredentialAndReset() =
+    fun usePatternCredentialAndReset() =
         testScope.runTest { useCredentialAndReset(Utils.CREDENTIAL_PATTERN) }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 6d8e7aa..6aebe36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -126,7 +126,7 @@
             keyguardRepository.setKeyguardDismissible(true)
             runCurrent()
             shadeRepository.setCurrentFling(
-                FlingInfo(expand = true) // Not a dismiss fling (expand = true).
+                FlingInfo(expand = false) // Is a dismiss fling upward (expand = false).
             )
             runCurrent()
 
@@ -153,4 +153,22 @@
 
             assertThatRepository(transitionRepository).noTransitionsStarted()
         }
+
+    @Test
+    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun testDoesNotTransitionToGone_whenDismissFling_emitsNull() =
+        testScope.runTest {
+            underTest.start()
+            verify(transitionRepository, never()).startTransition(any())
+
+            keyguardRepository.setKeyguardDismissible(true)
+            runCurrent()
+
+            // The fling is null when it a) initializes b) ends and in either case we should not
+            // swipe to unlock.
+            shadeRepository.setCurrentFling(null)
+            runCurrent()
+
+            assertThatRepository(transitionRepository).noTransitionsStarted()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index aa53558..1504d4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -92,6 +92,21 @@
 @TestableLooper.RunWithLooper
 public class KeyguardIndicationControllerTest extends KeyguardIndicationControllerBaseTest {
     @Test
+    public void afterFaceLockout_skipShowingFaceNotRecognized() {
+        createController();
+        onFaceLockoutError("lockout");
+        verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "lockout");
+        clearInvocations(mRotateTextViewController);
+
+        // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED (face fail)
+        mKeyguardUpdateMonitorCallback.onBiometricHelp(
+                BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
+                "Face not recognized",
+                BiometricSourceType.FACE);
+        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); // no updated message
+    }
+
+    @Test
     public void createController_setIndicationAreaAgain_destroysPreviousRotateTextViewController() {
         // GIVEN a controller with a mocked rotate text view controlller
         final KeyguardIndicationRotateTextViewController mockedRotateTextViewController =
@@ -593,7 +608,7 @@
         mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
                 "A message", BiometricSourceType.FACE);
 
-        verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any());
+        verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any(), any());
     }
 
     @Test
@@ -608,7 +623,8 @@
         mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
                 "A message", BiometricSourceType.FACE);
 
-        verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(eq(message), any());
+        verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(
+                eq(message), any(), any());
     }
 
     @Test
@@ -1242,7 +1258,7 @@
     public void onBiometricFailed_resetFaceHelpMessageDeferral() {
         createController();
 
-        // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+        // WHEN face sends an onBiometricAuthFailed
         mKeyguardUpdateMonitorCallback.onBiometricAuthFailed(BiometricSourceType.FACE);
 
         // THEN face help message deferral is reset
@@ -1331,7 +1347,9 @@
         verify(mStatusBarKeyguardViewManager)
                 .setKeyguardMessage(
                         eq(mContext.getString(R.string.keyguard_face_unlock_unavailable)),
-                        any());
+                        any(),
+                        any()
+                );
     }
 
     @Test
@@ -1471,6 +1489,71 @@
                 mContext.getString(R.string.keyguard_suggest_fingerprint));
     }
 
+    @Test
+    public void faceErrorMessageDroppedBecauseFingerprintMessageShowing() {
+        createController();
+        mController.setVisible(true);
+        mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                "fp not recognized", BiometricSourceType.FINGERPRINT);
+        clearInvocations(mRotateTextViewController);
+
+        onFaceLockoutError("lockout");
+        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+    }
+
+    @Test
+    public void faceUnlockedMessageShowsEvenWhenFingerprintMessageShowing() {
+        createController();
+        mController.setVisible(true);
+        mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                "fp not recognized", BiometricSourceType.FINGERPRINT);
+        clearInvocations(mRotateTextViewController);
+
+        when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+        when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
+                .thenReturn(true);
+        mController.getKeyguardCallback().onBiometricAuthenticated(0,
+                BiometricSourceType.FACE, false);
+        verifyIndicationMessage(
+                INDICATION_TYPE_BIOMETRIC_MESSAGE,
+                mContext.getString(R.string.keyguard_face_successful_unlock));
+    }
+
+    @Test
+    public void onTrustAgentErrorMessageDroppedBecauseFingerprintMessageShowing() {
+        createController();
+        mController.setVisible(true);
+        mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                "fp not recognized", BiometricSourceType.FINGERPRINT);
+        clearInvocations(mRotateTextViewController);
+
+        mKeyguardUpdateMonitorCallback.onTrustAgentErrorMessage("testMessage");
+        verifyNoMessage(INDICATION_TYPE_TRUST);
+        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+    }
+
+    @Test
+    public void trustGrantedMessageShowsEvenWhenFingerprintMessageShowing() {
+        createController();
+        mController.setVisible(true);
+        mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                "fp not recognized", BiometricSourceType.FINGERPRINT);
+        clearInvocations(mRotateTextViewController);
+
+        // GIVEN trust is granted
+        when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+        // WHEN the showTrustGranted method is called
+        final String trustGrantedMsg = "testing trust granted message after fp message";
+        mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
+                false, false, new TrustGrantFlags(0), trustGrantedMsg);
+
+        // THEN verify the trust granted message shows
+        verifyIndicationMessage(
+                INDICATION_TYPE_TRUST,
+                trustGrantedMsg);
+    }
+
     private void screenIsTurningOn() {
         when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_TURNING_ON);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 5e8b62e..fd2dead 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -26,6 +26,7 @@
 import android.view.MotionEvent
 import android.view.PrivacyIndicatorBounds
 import android.view.RoundedCorners
+import android.view.View
 import android.view.WindowInsets
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
@@ -50,6 +51,8 @@
 class PhoneStatusBarViewTest : SysuiTestCase() {
 
     private lateinit var view: PhoneStatusBarView
+    private val systemIconsContainer: View
+        get() = view.requireViewById(R.id.system_icons)
 
     private val contentInsetsProvider = mock<StatusBarContentInsetsProvider>()
     private val windowController = mock<StatusBarWindowController>()
@@ -62,6 +65,7 @@
         )
         mDependency.injectTestDependency(DarkIconDispatcher::class.java, mock<DarkIconDispatcher>())
         mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController)
+        context.ensureTestableResources()
         view = spy(createStatusBarView())
         whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets())
         whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
@@ -217,7 +221,7 @@
 
         val newInsets = Insets.NONE
         whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-                .thenReturn(newInsets)
+            .thenReturn(newInsets)
         view.onConfigurationChanged(Configuration())
 
         assertThat(view.paddingLeft).isEqualTo(previousInsets.left)
@@ -239,7 +243,7 @@
 
         val newInsets = Insets.NONE
         whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-                .thenReturn(newInsets)
+            .thenReturn(newInsets)
         configuration.densityDpi = 456
         view.onConfigurationChanged(configuration)
 
@@ -262,7 +266,7 @@
 
         val newInsets = Insets.NONE
         whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-                .thenReturn(newInsets)
+            .thenReturn(newInsets)
         configuration.fontScale = 2f
         view.onConfigurationChanged(configuration)
 
@@ -273,6 +277,19 @@
     }
 
     @Test
+    fun onConfigurationChanged_systemIconsHeightChanged_containerHeightIsUpdated() {
+        val newHeight = 123456
+        context.orCreateTestableResources.addOverride(
+            R.dimen.status_bar_system_icons_height,
+            newHeight
+        )
+
+        view.onConfigurationChanged(Configuration())
+
+        assertThat(systemIconsContainer.layoutParams.height).isEqualTo(newHeight)
+    }
+
+    @Test
     fun onApplyWindowInsets_updatesLeftTopRightPaddingsBasedOnInsets() {
         val insets = Insets.of(/* left = */ 90, /* top = */ 10, /* right = */ 45, /* bottom = */ 50)
         whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bd7406a..3666248 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -97,6 +97,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
 
 import com.google.common.truth.Truth;
 
@@ -222,7 +223,8 @@
                         () -> mock(WindowManagerLockscreenVisibilityInteractor.class),
                         () -> mock(KeyguardDismissActionInteractor.class),
                         mSelectedUserInteractor,
-                        () -> mock(KeyguardSurfaceBehindInteractor.class)) {
+                        () -> mock(KeyguardSurfaceBehindInteractor.class),
+                        mock(JavaAdapter.class)) {
                     @Override
                     public ViewRootImpl getViewRootImpl() {
                         return mViewRootImpl;
@@ -730,7 +732,8 @@
                         () -> mock(WindowManagerLockscreenVisibilityInteractor.class),
                         () -> mock(KeyguardDismissActionInteractor.class),
                         mSelectedUserInteractor,
-                        () -> mock(KeyguardSurfaceBehindInteractor.class)) {
+                        () -> mock(KeyguardSurfaceBehindInteractor.class),
+                        mock(JavaAdapter.class)) {
                     @Override
                     public ViewRootImpl getViewRootImpl() {
                         return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index a930860..fbefb0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -609,6 +609,7 @@
                 mSysUiState,
                 mFeatureFlags,
                 mNotifPipelineFlags,
+                syncExecutor,
                 syncExecutor);
         mBubblesManager.addNotifCallback(mNotifCallback);
 
@@ -651,7 +652,7 @@
     }
 
     @Test
-    public void dreamingHidesBubbles() throws RemoteException {
+    public void bubblesHiddenWhileDreaming() throws RemoteException {
         mBubbleController.updateBubble(mBubbleEntry);
         assertTrue(mBubbleController.hasBubbles());
         assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
@@ -662,7 +663,17 @@
                 mKeyguardStateControllerCallbackCaptor.getValue();
         callback.onKeyguardShowingChanged();
 
+        // Dreaming should hide bubbles
         assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.INVISIBLE);
+
+        // Finish dreaming should show bubbles
+        mNotificationShadeWindowController.setDreaming(false);
+        when(mIDreamManager.isDreamingOrInPreview()).thenReturn(false); // dreaming finished
+
+        // Dreaming updates come through mNotificationShadeWindowController
+        mNotificationShadeWindowController.notifyStateChangedCallbacks();
+
+        assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
index f192de2..c3af437 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
@@ -1,6 +1,8 @@
 package com.android.systemui.biometrics.data.repository
 
+import android.hardware.biometrics.Flags
 import android.hardware.biometrics.PromptInfo
+import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.shared.model.PromptKind
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -26,6 +28,9 @@
     private val _isConfirmationRequired = MutableStateFlow(false)
     override val isConfirmationRequired = _isConfirmationRequired.asStateFlow()
 
+    private val _showBpWithoutIconForCredential = MutableStateFlow(false)
+    override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
+
     private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null)
     override val opPackageName = _opPackageName.asStateFlow()
 
@@ -69,6 +74,16 @@
         _isConfirmationRequired.value = false
     }
 
+    override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+        val hasCredentialViewShown = kind.value !is PromptKind.Biometric
+        val showBpForCredential =
+            Flags.customBiometricPrompt() &&
+                !Utils.isBiometricAllowed(promptInfo) &&
+                Utils.isDeviceCredentialAllowed(promptInfo) &&
+                promptInfo.contentView != null
+        _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
+    }
+
     fun setIsShowing(showing: Boolean) {
         _isShowing.value = showing
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index ecf66a2..8ca53e6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -42,6 +42,10 @@
         aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
         dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
         glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
+        goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+        goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
+        lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
+        lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel,
         lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel,
         lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel,
         lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 96de4ba..8da5dd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.splitShadeStateController
 
 val Kosmos.lockscreenContentViewModel by
     Kosmos.Fixture {
@@ -28,5 +29,6 @@
             interactor = keyguardBlueprintInteractor,
             authController = authController,
             longPress = keyguardLongPressViewModel,
+            splitShadeStateController = splitShadeStateController,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
new file mode 100644
index 0000000..df6fc41
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings() }
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index cffcd09..71d291a 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -61,6 +61,12 @@
     NativeCrash native_crash = 6;
     SystemServerStarted system_server_started = 7;
     InstallPackages install_packages = 8;
+    ExcessiveBinderCalls excessive_binder_calls = 9;
+  }
+
+  message ExcessiveBinderCalls {
+    // The uid sending many calls.
+    optional int32 uid = 1;
   }
 
   message InstallPackages {}
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index d0eb59d..1dab40e 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -25,12 +25,12 @@
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 
 import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.MetricUtils.logCreateAssociation;
-import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
-import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
-import static com.android.server.companion.RolesUtils.isRoleHolder;
-import static com.android.server.companion.Utils.prepareForIpc;
+import static com.android.server.companion.utils.MetricUtils.logCreateAssociation;
+import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
+import static com.android.server.companion.utils.PermissionsUtils.enforcePermissionForCreatingAssociation;
+import static com.android.server.companion.utils.RolesUtils.addRoleHolderForAssociation;
+import static com.android.server.companion.utils.RolesUtils.isRoleHolder;
+import static com.android.server.companion.utils.Utils.prepareForIpc;
 
 import static java.util.Objects.requireNonNull;
 
@@ -59,6 +59,7 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.server.companion.utils.PackageUtils;
 
 import java.util.List;
 
@@ -167,7 +168,7 @@
         }
 
         // 1. Enforce permissions and other requirements.
-        enforcePermissionsForAssociation(mContext, request, packageUid);
+        enforcePermissionForCreatingAssociation(mContext, request, packageUid);
         enforceUsesCompanionDeviceFeature(mContext, userId, packageName);
 
         // 2a. Check if association can be created without launching UI (i.e. CDM needs NEITHER
@@ -257,7 +258,7 @@
         // 1. Need to check permissions again in case something changed, since we first received
         // this request.
         try {
-            enforcePermissionsForAssociation(mContext, request, packageUid);
+            enforcePermissionForCreatingAssociation(mContext, request, packageUid);
         } catch (SecurityException e) {
             // Since, at this point the caller is our own UI, we need to catch the exception on
             // forward it back to the application via the callback.
@@ -316,6 +317,9 @@
         // If it is null, then the operation will succeed without granting any role.
         addRoleHolderForAssociation(mService.getContext(), association, success -> {
             if (success) {
+                Slog.i(TAG, "Added " + association.getDeviceProfile() + " role to userId="
+                        + association.getUserId() + ", packageName="
+                        + association.getPackageName());
                 addAssociationToStore(association);
                 sendCallbackAndFinish(association, callback, resultReceiver);
             } else {
diff --git a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java b/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java
index de6382e..10963ea 100644
--- a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java
@@ -20,8 +20,8 @@
 import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
 
 import static com.android.internal.util.CollectionUtils.any;
-import static com.android.server.companion.MetricUtils.logRemoveAssociation;
-import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;
+import static com.android.server.companion.utils.MetricUtils.logRemoveAssociation;
+import static com.android.server.companion.utils.RolesUtils.removeRoleHolderForAssociation;
 import static com.android.server.companion.CompanionDeviceManagerService.PerUserAssociationSet;
 
 import android.annotation.NonNull;
@@ -203,7 +203,8 @@
             return false;
         }
 
-        removeRoleHolderForAssociation(mContext, association);
+        removeRoleHolderForAssociation(mContext, association.getUserId(),
+                association.getPackageName(), association.getDeviceProfile());
         return true;
     }
 
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index af0777c..559ebbc 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -38,6 +38,9 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.infra.PerUser;
 import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
+import com.android.server.companion.presence.ObservableUuidStore;
+import com.android.server.companion.utils.PackageUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -61,7 +64,7 @@
  * <ul>
  * <li> {@link #bindCompanionApplication(int, String, boolean)}
  * <li> {@link #unbindCompanionApplication(int, String)}
- * <li> {@link #notifyCompanionApplicationDevicePresenceEvent(AssociationInfo, int)}
+ * <li> {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)}
  * <li> {@link #isCompanionApplicationBound(int, String)}
  * <li> {@link #isRebindingCompanionApplicationScheduled(int, String)}
  * </ul>
@@ -251,7 +254,13 @@
         serviceConnector.connect();
     }
 
-    void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
+    /**
+     * Notify the app that the device appeared.
+     *
+     * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead
+     */
+    @Deprecated
+    public void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
         final int userId = association.getUserId();
         final String packageName = association.getPackageName();
 
@@ -273,7 +282,13 @@
         primaryServiceConnector.postOnDeviceAppeared(association);
     }
 
-    void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) {
+    /**
+     * Notify the app that the device disappeared.
+     *
+     * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead
+     */
+    @Deprecated
+    public void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) {
         final int userId = association.getUserId();
         final String packageName = association.getPackageName();
 
@@ -295,7 +310,10 @@
         primaryServiceConnector.postOnDeviceDisappeared(association);
     }
 
-    void notifyCompanionApplicationDevicePresenceEvent(AssociationInfo association, int event) {
+    /**
+     * Notify the app that the device appeared.
+     */
+    public void notifyCompanionDevicePresenceEvent(AssociationInfo association, int event) {
         final int userId = association.getUserId();
         final String packageName = association.getPackageName();
         final CompanionDeviceServiceConnector primaryServiceConnector =
@@ -318,7 +336,10 @@
         primaryServiceConnector.postOnDevicePresenceEvent(devicePresenceEvent);
     }
 
-    void notifyApplicationDevicePresenceEvent(ObservableUuid uuid, int event) {
+    /**
+     * Notify the app that the device disappeared.
+     */
+    public void notifyUuidDevicePresenceEvent(ObservableUuid uuid, int event) {
         final int userId = uuid.getUserId();
         final ParcelUuid parcelUuid = uuid.getUuid();
         final String packageName = uuid.getPackageName();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 09c7793..a478a3d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -38,15 +38,15 @@
 import static com.android.internal.util.Preconditions.checkState;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
-import static com.android.server.companion.PackageUtils.isRestrictedSettingsAllowed;
-import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.PackageUtils.getPackageInfo;
-import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
-import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
-import static com.android.server.companion.PermissionsUtils.enforceCallerCanObservingDevicePresenceByUuid;
-import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
-import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
-import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
+import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
+import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
+import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
+import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageCompanionDevice;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanObservingDevicePresenceByUuid;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
+import static com.android.server.companion.utils.PermissionsUtils.sanitizeWithCallerChecks;
 
 import static java.util.Objects.requireNonNull;
 import static java.util.concurrent.TimeUnit.DAYS;
@@ -123,6 +123,8 @@
 import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
 import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback;
 import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
+import com.android.server.companion.presence.ObservableUuidStore;
 import com.android.server.companion.transport.CompanionTransportManager;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -435,7 +437,7 @@
 
                 bindApplicationIfNeeded(association);
 
-                mCompanionAppController.notifyCompanionApplicationDevicePresenceEvent(
+                mCompanionAppController.notifyCompanionDevicePresenceEvent(
                         association, event);
                 break;
             case EVENT_BLE_DISAPPEARED:
@@ -446,7 +448,7 @@
                     return;
                 }
                 if (association.shouldBindWhenPresent()) {
-                    mCompanionAppController.notifyCompanionApplicationDevicePresenceEvent(
+                    mCompanionAppController.notifyCompanionDevicePresenceEvent(
                             association, event);
                 }
                 // Check if there are other devices associated to the app that are present.
@@ -475,7 +477,7 @@
                     Log.i(TAG, "u" + userId + "\\" + packageName + " is already bound");
                 }
 
-                mCompanionAppController.notifyApplicationDevicePresenceEvent(uuid, event);
+                mCompanionAppController.notifyUuidDevicePresenceEvent(uuid, event);
 
                 break;
             case EVENT_BT_DISCONNECTED:
@@ -484,7 +486,7 @@
                     return;
                 }
 
-                mCompanionAppController.notifyApplicationDevicePresenceEvent(uuid, event);
+                mCompanionAppController.notifyUuidDevicePresenceEvent(uuid, event);
                 // Check if there are other devices associated to the app or the UUID to be
                 // observed are present.
                 if (shouldBindPackage(userId, packageName)) return;
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index de4f2b6..74b4cab 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -18,7 +18,7 @@
 
 import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
 
-import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
+import static com.android.server.companion.utils.PermissionsUtils.sanitizeWithCallerChecks;
 
 import android.companion.AssociationInfo;
 import android.companion.ContextSyncMessage;
@@ -36,6 +36,7 @@
 import com.android.server.companion.datatransfer.contextsync.BitmapUtils;
 import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
 import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
 import com.android.server.companion.transport.CompanionTransportManager;
 
 import java.io.PrintWriter;
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 1ebe65c..7527efb 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -27,11 +27,11 @@
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
 import static com.android.server.companion.CompanionDeviceManagerService.getFirstAssociationIdForUser;
 import static com.android.server.companion.CompanionDeviceManagerService.getLastAssociationIdForUser;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.fileToByteArray;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.fileToByteArray;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -51,6 +51,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.companion.utils.DataStoreUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 260b21f..74236a4 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -23,7 +23,7 @@
 import static android.content.ComponentName.createRelative;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 
-import static com.android.server.companion.Utils.prepareForIpc;
+import static com.android.server.companion.utils.Utils.prepareForIpc;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -54,9 +54,9 @@
 import com.android.internal.R;
 import com.android.server.companion.AssociationStore;
 import com.android.server.companion.CompanionDeviceManagerService;
-import com.android.server.companion.PackageUtils;
-import com.android.server.companion.PermissionsUtils;
 import com.android.server.companion.transport.CompanionTransportManager;
+import com.android.server.companion.utils.PackageUtils;
+import com.android.server.companion.utils.PermissionsUtils;
 
 import java.util.List;
 import java.util.concurrent.ExecutorService;
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index c4c80f9..ee816a0 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -22,11 +22,11 @@
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.fileToByteArray;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.fileToByteArray;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index 7e30790..6792434 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -34,7 +34,7 @@
 import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
 
 import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
-import static com.android.server.companion.presence.Utils.btDeviceToString;
+import static com.android.server.companion.utils.Utils.btDeviceToString;
 
 import static java.util.Objects.requireNonNull;
 
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index c514f3e..0287f62 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -20,7 +20,7 @@
 import static android.companion.DevicePresenceEvent.EVENT_BT_DISCONNECTED;
 
 import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
-import static com.android.server.companion.presence.Utils.btDeviceToString;
+import static com.android.server.companion.utils.Utils.btDeviceToString;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
@@ -40,8 +40,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.companion.AssociationStore;
-import com.android.server.companion.ObservableUuid;
-import com.android.server.companion.ObservableUuidStore;
 
 import java.util.Arrays;
 import java.util.Collections;
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 54a4692..caca48d 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -43,8 +43,6 @@
 import android.util.SparseArray;
 
 import com.android.server.companion.AssociationStore;
-import com.android.server.companion.ObservableUuid;
-import com.android.server.companion.ObservableUuidStore;
 
 import java.io.PrintWriter;
 import java.util.HashSet;
diff --git a/services/companion/java/com/android/server/companion/ObservableUuid.java b/services/companion/java/com/android/server/companion/presence/ObservableUuid.java
similarity index 96%
rename from services/companion/java/com/android/server/companion/ObservableUuid.java
rename to services/companion/java/com/android/server/companion/presence/ObservableUuid.java
index 6ab3188..9cfa270 100644
--- a/services/companion/java/com/android/server/companion/ObservableUuid.java
+++ b/services/companion/java/com/android/server/companion/presence/ObservableUuid.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.companion;
+package com.android.server.companion.presence;
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
diff --git a/services/companion/java/com/android/server/companion/ObservableUuidStore.java b/services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java
similarity index 93%
rename from services/companion/java/com/android/server/companion/ObservableUuidStore.java
rename to services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java
index 94be22a..ee8b106 100644
--- a/services/companion/java/com/android/server/companion/ObservableUuidStore.java
+++ b/services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.companion;
+package com.android.server.companion.presence;
 
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -22,10 +22,10 @@
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -57,6 +57,9 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+/**
+ * This store manages the cache and disk data for observable uuids.
+ */
 public class ObservableUuidStore {
     private static final String TAG = "CDM_ObservableUuidStore";
     private static final String FILE_NAME = "observing_uuids_presence.xml";
@@ -84,9 +87,9 @@
     }
 
     /**
-     * Remove the observable uuid from the disk.
+     * Remove the observable uuid.
      */
-    void removeObservableUuid(@UserIdInt int userId, ParcelUuid uuid, String packageName) {
+    public void removeObservableUuid(@UserIdInt int userId, ParcelUuid uuid, String packageName) {
         List<ObservableUuid> cachedObservableUuids;
 
         synchronized (mLock) {
@@ -101,7 +104,10 @@
         mExecutor.execute(() -> writeObservableUuidToStore(userId, cachedObservableUuids));
     }
 
-    void writeObservableUuid(@UserIdInt int userId, ObservableUuid uuid) {
+    /**
+     * Write the observable uuid.
+     */
+    public void writeObservableUuid(@UserIdInt int userId, ObservableUuid uuid) {
         Slog.i(TAG, "Writing uuid=" + uuid.getUuid() + " to store.");
 
         List<ObservableUuid> cachedObservableUuids;
diff --git a/services/companion/java/com/android/server/companion/presence/Utils.java b/services/companion/java/com/android/server/companion/presence/Utils.java
deleted file mode 100644
index 583b443..0000000
--- a/services/companion/java/com/android/server/companion/presence/Utils.java
+++ /dev/null
@@ -1,49 +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.companion.presence;
-
-import android.annotation.NonNull;
-import android.bluetooth.BluetoothDevice;
-
-/** Utilities for working with Bluetooth and BLE devices. */
-class Utils {
-
-    /**
-     * @return short String representation of {@link BluetoothDevice}.
-     */
-    static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
-        final StringBuilder sb = new StringBuilder(btDevice.getAddress());
-
-        sb.append(" [name=");
-        final String name = btDevice.getName();
-        if (name != null) {
-            sb.append('\'').append(name).append('\'');
-        } else {
-            sb.append("null");
-        }
-
-        final String alias = btDevice.getAlias();
-        if (alias != null) {
-            sb.append(", alias='").append(alias).append("'");
-        }
-
-        return sb.append(']').toString();
-    }
-
-    private Utils() {
-    }
-}
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/utils/DataStoreUtils.java
similarity index 97%
rename from services/companion/java/com/android/server/companion/DataStoreUtils.java
rename to services/companion/java/com/android/server/companion/utils/DataStoreUtils.java
index 04ce1f6..c75b1a5 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/DataStoreUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.companion;
+package com.android.server.companion.utils;
 
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -80,7 +80,7 @@
 
     /**
      * Writing to file could fail, for example, if the user has been recently removed and so was
-     * their DE (/data/system_de/<user-id>/) directory.
+     * their DE (/data/system_de/[user-id]/) directory.
      */
     public static void writeToFileSafely(
             @NonNull AtomicFile file, @NonNull ThrowingConsumer<FileOutputStream> consumer) {
diff --git a/services/companion/java/com/android/server/companion/MetricUtils.java b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
similarity index 92%
rename from services/companion/java/com/android/server/companion/MetricUtils.java
rename to services/companion/java/com/android/server/companion/utils/MetricUtils.java
index cf867b6..8ea5c89 100644
--- a/services/companion/java/com/android/server/companion/MetricUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.companion;
+package com.android.server.companion.utils;
 
 import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
@@ -41,7 +41,7 @@
 
 import java.util.Map;
 
-final class MetricUtils {
+public final class MetricUtils {
 
     private static final Map<String, Integer> METRIC_DEVICE_PROFILE;
     static {
@@ -75,13 +75,19 @@
         METRIC_DEVICE_PROFILE = unmodifiableMap(map);
     }
 
-    static void logCreateAssociation(String profile) {
+    /**
+     * Log association creation
+     */
+    public static void logCreateAssociation(String profile) {
         write(CDM_ASSOCIATION_ACTION,
                 CDM_ASSOCIATION_ACTION__ACTION__CREATED,
                 METRIC_DEVICE_PROFILE.get(profile));
     }
 
-    static void logRemoveAssociation(String profile) {
+    /**
+     * Log association removal
+     */
+    public static void logRemoveAssociation(String profile) {
         write(CDM_ASSOCIATION_ACTION,
                 CDM_ASSOCIATION_ACTION__ACTION__REMOVED,
                 METRIC_DEVICE_PROFILE.get(profile));
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/utils/PackageUtils.java
similarity index 86%
rename from services/companion/java/com/android/server/companion/PackageUtils.java
rename to services/companion/java/com/android/server/companion/utils/PackageUtils.java
index 3aae1ec..d38590e 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PackageUtils.java
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.server.companion;
+package com.android.server.companion.utils;
 
 import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
 import static android.content.pm.PackageManager.GET_CONFIGURATIONS;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.os.Binder.getCallingUid;
 
-import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.TAG;
-
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,13 +41,11 @@
 import android.content.pm.Signature;
 import android.os.Binder;
 import android.os.Process;
-import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -58,16 +53,22 @@
 import java.util.Set;
 
 /**
- * Utility methods for working with {@link PackageInfo}-s.
+ * Utility methods for working with {@link PackageInfo}.
  */
 public final class PackageUtils {
+
+    private static final String TAG = "CDM_PackageUtils";
+
     private static final Intent COMPANION_SERVICE_INTENT =
             new Intent(CompanionDeviceService.SERVICE_INTERFACE);
     private static final String PROPERTY_PRIMARY_TAG =
             "android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE";
 
+    /**
+     * Get package info
+     */
     @Nullable
-    static PackageInfo getPackageInfo(@NonNull Context context,
+    public static PackageInfo getPackageInfo(@NonNull Context context,
             @UserIdInt int userId, @NonNull String packageName) {
         final PackageManager pm = context.getPackageManager();
         final PackageInfoFlags flags = PackageInfoFlags.of(GET_PERMISSIONS | GET_CONFIGURATIONS);
@@ -81,26 +82,32 @@
         });
     }
 
-    static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
+    /**
+     * Require the app to declare the companion device feature.
+     */
+    public static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
             @UserIdInt int userId, @NonNull String packageName) {
         // Allow system server to create CDM associations without FEATURE_COMPANION_DEVICE_SETUP
         if (getCallingUid() == Process.SYSTEM_UID) {
             return;
         }
 
-        String requiredFeature = FEATURE_COMPANION_DEVICE_SETUP;
+        PackageInfo packageInfo = getPackageInfo(context, userId, packageName);
+        if (packageInfo == null) {
+            throw new IllegalArgumentException("Package " + packageName + " doesn't exist.");
+        }
 
-        FeatureInfo[] requestedFeatures = getPackageInfo(context, userId, packageName).reqFeatures;
+        FeatureInfo[] requestedFeatures = packageInfo.reqFeatures;
         if (requestedFeatures != null) {
-            for (int i = 0; i < requestedFeatures.length; i++) {
-                if (requiredFeature.equals(requestedFeatures[i].name)) {
+            for (FeatureInfo requestedFeature : requestedFeatures) {
+                if (FEATURE_COMPANION_DEVICE_SETUP.equals(requestedFeature.name)) {
                     return;
                 }
             }
         }
 
         throw new IllegalStateException("Must declare uses-feature "
-                + requiredFeature
+                + FEATURE_COMPANION_DEVICE_SETUP
                 + " in manifest to use this API");
     }
 
@@ -109,7 +116,7 @@
      *         Services marked as "primary" would always appear at the head of the lists, *before*
      *         all non-primary services.
      */
-    static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser(
+    public static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser(
             @NonNull Context context, @UserIdInt int userId) {
         final PackageManager pm = context.getPackageManager();
         final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser(
@@ -179,9 +186,7 @@
         final String[] allowlistedPackages = context.getResources()
                 .getStringArray(com.android.internal.R.array.config_companionDevicePackages);
         if (!ArrayUtils.contains(allowlistedPackages, packageName)) {
-            if (DEBUG) {
-                Log.d(TAG, packageName + " is not allowlisted.");
-            }
+            Slog.d(TAG, packageName + " is not allowlisted.");
             return false;
         }
 
@@ -212,13 +217,6 @@
 
         if (!requestingPackageSignatureAllowlisted) {
             Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName);
-            if (DEBUG) {
-                Log.d(TAG, "  > allowlisted signatures for " + packageName + ": ["
-                        + String.join(", ", allowlistedSignatureDigestsForRequestingPackage)
-                        + "]");
-                Log.d(TAG, "  > actual signatures for " + packageName + ": "
-                        + Arrays.toString(requestingPackageSignatureDigests));
-            }
         }
 
         return requestingPackageSignatureAllowlisted;
diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
similarity index 80%
rename from services/companion/java/com/android/server/companion/PermissionsUtils.java
rename to services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index 15bebba..2cf1f46 100644
--- a/services/companion/java/com/android/server/companion/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.companion;
+package com.android.server.companion.utils;
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
@@ -75,16 +75,22 @@
         DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
     }
 
-    static void enforcePermissionsForAssociation(@NonNull Context context,
+    /**
+     * Require the app to declare necessary permission for creating association.
+     */
+    public static void enforcePermissionForCreatingAssociation(@NonNull Context context,
             @NonNull AssociationRequest request, int packageUid) {
-        enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile(), packageUid);
+        enforcePermissionForRequestingProfile(context, request.getDeviceProfile(), packageUid);
 
         if (request.isSelfManaged()) {
-            enforceRequestSelfManagedPermission(context, packageUid);
+            enforcePermissionForRequestingSelfManaged(context, packageUid);
         }
     }
 
-    static void enforceRequestDeviceProfilePermissions(
+    /**
+     * Require the app to declare necessary permission for creating association with profile.
+     */
+    public static void enforcePermissionForRequestingProfile(
             @NonNull Context context, @Nullable String deviceProfile, int packageUid) {
         // Device profile can be null.
         if (deviceProfile == null) return;
@@ -101,7 +107,11 @@
         }
     }
 
-    static void enforceRequestSelfManagedPermission(@NonNull Context context, int packageUid) {
+    /**
+     * Require the app to declare necessary permission for creating self-managed association.
+     */
+    public static void enforcePermissionForRequestingSelfManaged(@NonNull Context context,
+            int packageUid) {
         if (context.checkPermission(REQUEST_COMPANION_SELF_MANAGED, getCallingPid(), packageUid)
                 != PERMISSION_GRANTED) {
             throw new SecurityException("Application does not hold "
@@ -109,25 +119,39 @@
         }
     }
 
-    static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) {
+    /**
+     * Check if the caller can interact with the user.
+     */
+    public static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) {
         if (getCallingUserId() == userId) return true;
 
         return context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED;
     }
 
-    static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) {
+    /**
+     * Require the caller to be able to interact with the user.
+     */
+    public static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) {
         if (getCallingUserId() == userId) return;
 
         context.enforceCallingPermission(INTERACT_ACROSS_USERS, null);
     }
 
-    static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context, int userId) {
+    /**
+     * Require the caller to be system UID or to be able to interact with the user.
+     */
+    public static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context,
+            int userId) {
         if (getCallingUid() == SYSTEM_UID) return;
 
         enforceCallerCanInteractWithUserId(context, userId);
     }
 
-    static boolean checkCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) {
+    /**
+     * Check if the caller is system UID or the provided user.
+     */
+    public static boolean checkCallerIsSystemOr(@UserIdInt int userId,
+            @NonNull String packageName) {
         final int callingUid = getCallingUid();
         if (callingUid == SYSTEM_UID) return true;
 
@@ -158,13 +182,19 @@
         }
     }
 
-    static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
+    /**
+     * Check if the caller holds the necessary permission to manage companion devices.
+     */
+    public static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
         if (getCallingUid() == SYSTEM_UID) return true;
 
         return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
     }
 
-    static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
+    /**
+     * Require the caller to be able to manage the associations for the package.
+     */
+    public static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
             @UserIdInt int userId, @NonNull String packageName,
             @Nullable String actionDescription) {
         if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return;
@@ -175,7 +205,10 @@
                 + " for u" + userId + "/" + packageName);
     }
 
-    static void enforceCallerCanObservingDevicePresenceByUuid(@NonNull Context context) {
+    /**
+     * Require the caller to hold necessary permission to observe device presence by UUID.
+     */
+    public static void enforceCallerCanObservingDevicePresenceByUuid(@NonNull Context context) {
         if (context.checkCallingPermission(REQUEST_OBSERVE_DEVICE_UUID_PRESENCE)
                 != PERMISSION_GRANTED) {
             throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
@@ -193,7 +226,7 @@
      * </ul>
      * @return whether the caller is one of the above.
      */
-    static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
+    public static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
             @UserIdInt int userId, @NonNull String packageName) {
         if (checkCallerIsSystemOr(userId, packageName)) return true;
 
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/utils/RolesUtils.java
similarity index 70%
rename from services/companion/java/com/android/server/companion/RolesUtils.java
rename to services/companion/java/com/android/server/companion/utils/RolesUtils.java
index af9d2d7..f798e21 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/RolesUtils.java
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server.companion;
+package com.android.server.companion.utils;
 
 import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP;
 
-import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.TAG;
-
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
@@ -29,7 +26,6 @@
 import android.content.Context;
 import android.os.Binder;
 import android.os.UserHandle;
-import android.util.Log;
 import android.util.Slog;
 
 import java.util.List;
@@ -37,9 +33,14 @@
 
 /** Utility methods for accessing {@link RoleManager} APIs. */
 @SuppressLint("LongLogTag")
-final class RolesUtils {
+public final class RolesUtils {
 
-    static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
+    private static final String TAG = "CDM_RolesUtils";
+
+    /**
+     * Check if the package holds the role.
+     */
+    public static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
             @NonNull String packageName, @NonNull String role) {
         final RoleManager roleManager = context.getSystemService(RoleManager.class);
         final List<String> roleHolders = roleManager.getRoleHoldersAsUser(
@@ -58,13 +59,9 @@
      *                        false if failed. If the association does not have any device profile
      *                        specified, then the operation will always be successful as a no-op.
      */
-    static void addRoleHolderForAssociation(
+    public static void addRoleHolderForAssociation(
             @NonNull Context context, @NonNull AssociationInfo associationInfo,
             @NonNull Consumer<Boolean> roleGrantResult) {
-        if (DEBUG) {
-            Log.d(TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
-        }
-
         final String deviceProfile = associationInfo.getDeviceProfile();
         if (deviceProfile == null) {
             // If no device profile is specified, then no-op and resolve callback with success.
@@ -83,33 +80,30 @@
                 roleGrantResult);
     }
 
-    static void removeRoleHolderForAssociation(
-            @NonNull Context context, @NonNull AssociationInfo associationInfo) {
-        if (DEBUG) {
-            Log.d(TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo);
-        }
-
-        final String deviceProfile = associationInfo.getDeviceProfile();
+    /**
+     * Remove the role for the package association.
+     */
+    public static void removeRoleHolderForAssociation(
+            @NonNull Context context, int userId, String packageName, String deviceProfile) {
         if (deviceProfile == null) return;
 
         final RoleManager roleManager = context.getSystemService(RoleManager.class);
 
-        final String packageName = associationInfo.getPackageName();
-        final int userId = associationInfo.getUserId();
         final UserHandle userHandle = UserHandle.of(userId);
 
-        Slog.i(TAG, "Removing CDM role holder, role=" + deviceProfile
-                + ", package=u" + userId + "\\" + packageName);
+        Slog.i(TAG, "Removing CDM role=" + deviceProfile
+                + " for userId=" + userId + ", packageName=" + packageName);
         final long identity = Binder.clearCallingIdentity();
         try {
             roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
-                MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
-                success -> {
-                    if (!success) {
-                        Slog.e(TAG, "Failed to remove u" + userId + "\\" + packageName
-                                + " from the list of " + deviceProfile + " holders.");
-                    }
-                });
+                    MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
+                    success -> {
+                        if (!success) {
+                            Slog.e(TAG, "Failed to remove userId=" + userId + ", packageName="
+                                    + packageName + " from the list of " + deviceProfile
+                                    + " holders.");
+                        }
+                    });
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/companion/java/com/android/server/companion/Utils.java b/services/companion/java/com/android/server/companion/utils/Utils.java
similarity index 64%
rename from services/companion/java/com/android/server/companion/Utils.java
rename to services/companion/java/com/android/server/companion/utils/Utils.java
index b9f61ec..8302997 100644
--- a/services/companion/java/com/android/server/companion/Utils.java
+++ b/services/companion/java/com/android/server/companion/utils/Utils.java
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server.companion;
+package com.android.server.companion.utils;
 
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothDevice;
 import android.os.Parcel;
 import android.os.ResultReceiver;
 
@@ -43,5 +45,27 @@
         return ipcFriendly;
     }
 
+    /**
+     * Return a human-readable string for the BluetoothDevice.
+     */
+    public static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
+        final StringBuilder sb = new StringBuilder(btDevice.getAddress());
+
+        sb.append(" [name=");
+        final String name = btDevice.getName();
+        if (name != null) {
+            sb.append('\'').append(name).append('\'');
+        } else {
+            sb.append("null");
+        }
+
+        final String alias = btDevice.getAlias();
+        if (alias != null) {
+            sb.append(", alias='").append(alias).append("'");
+        }
+
+        return sb.append(']').toString();
+    }
+
     private Utils() {}
 }
diff --git a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
index 3312be2..96fee929 100644
--- a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
+++ b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
@@ -32,8 +32,10 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.internal.widget.LockSettingsStateListener;
@@ -57,6 +59,8 @@
     private static final int MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT = 2;
     private static final int AUTH_SUCCESS = 1;
     private static final int AUTH_FAILURE = 0;
+    private static final int TYPE_PRIMARY_AUTH = 0;
+    private static final int TYPE_BIOMETRIC_AUTH = 1;
 
     private final LockPatternUtils mLockPatternUtils;
     private final LockSettingsInternal mLockSettings;
@@ -67,6 +71,7 @@
     private final UserManagerInternal mUserManager;
     @VisibleForTesting
     final SparseIntArray mFailedAttemptsForUser = new SparseIntArray();
+    private final SparseLongArray mLastLockedTimestamp = new SparseLongArray();
 
     public AdaptiveAuthService(Context context) {
         this(context, new LockPatternUtils(context));
@@ -170,7 +175,7 @@
             Slog.d(TAG, "handleReportPrimaryAuthAttempt: success=" + success
                     + ", userId=" + userId);
         }
-        reportAuthAttempt(success, userId);
+        reportAuthAttempt(TYPE_PRIMARY_AUTH, success, userId);
     }
 
     private void handleReportBiometricAuthAttempt(boolean success, int userId) {
@@ -178,13 +183,24 @@
             Slog.d(TAG, "handleReportBiometricAuthAttempt: success=" + success
                     + ", userId=" + userId);
         }
-        reportAuthAttempt(success, userId);
+        reportAuthAttempt(TYPE_BIOMETRIC_AUTH, success, userId);
     }
 
-    private void reportAuthAttempt(boolean success, int userId) {
+    private void reportAuthAttempt(int authType, boolean success, int userId) {
         if (success) {
             // Deleting the entry effectively resets the counter of failed attempts for the user
             mFailedAttemptsForUser.delete(userId);
+
+            // Collect metrics if the device was locked by adaptive auth before
+            if (mLastLockedTimestamp.indexOfKey(userId) >= 0) {
+                final long lastLockedTime = mLastLockedTimestamp.get(userId);
+                collectTimeElapsedSinceLastLocked(
+                        lastLockedTime, SystemClock.elapsedRealtime(), authType);
+
+                // Remove the entry for the last locked time because a successful auth just happened
+                // and metrics have been collected
+                mLastLockedTimestamp.delete(userId);
+            }
             return;
         }
 
@@ -210,6 +226,34 @@
         lockDevice(userId);
     }
 
+    private static void collectTimeElapsedSinceLastLocked(long lastLockedTime, long authTime,
+            int authType) {
+        final int unlockType =  switch (authType) {
+            case TYPE_PRIMARY_AUTH -> FrameworkStatsLog
+                    .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__PRIMARY_AUTH;
+            case TYPE_BIOMETRIC_AUTH -> FrameworkStatsLog
+                    .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__BIOMETRIC_AUTH;
+            default -> FrameworkStatsLog
+                    .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__UNKNOWN;
+        };
+
+        if (DEBUG) {
+            Slog.d(TAG, "collectTimeElapsedSinceLastLockedForUser: "
+                    + "lastLockedTime=" + lastLockedTime
+                    + ", authTime=" + authTime
+                    + ", unlockType=" + unlockType);
+        }
+
+        // This usually shouldn't happen, and just check out of an abundance of caution
+        if (lastLockedTime > authTime) {
+            return;
+        }
+
+        // Log to statsd
+        FrameworkStatsLog.write(FrameworkStatsLog.ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED,
+                lastLockedTime, authTime, unlockType);
+    }
+
     /**
      * Locks the device and requires primary auth or biometric auth for unlocking
      */
@@ -234,5 +278,9 @@
 
         // Lock the device
         mWindowManager.lockNow();
+
+        // Record the time that the device is locked by adaptive auth to collect metrics when the
+        // next successful primary or biometric auth happens
+        mLastLockedTimestamp.put(userId, SystemClock.elapsedRealtime());
     }
 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b8f6b3f..b8e09cc 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4133,7 +4133,7 @@
                         || (callerApp.mState.getCurProcState() <= PROCESS_STATE_TOP
                             && c.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)),
                         b.client);
-                if ((serviceBindingOomAdjPolicy
+                if (!s.mOomAdjBumpedInExec && (serviceBindingOomAdjPolicy
                         & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) == 0) {
                     needOomAdj = true;
                     mAm.enqueueOomAdjTargetLocked(s.app);
@@ -4277,7 +4277,7 @@
                 }
 
                 serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false, false,
-                        !Flags.serviceBindingOomAdjPolicy() || b == null || !b.mSkippedOomAdj
+                        !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
                         ? OOM_ADJ_REASON_EXECUTING_SERVICE : OOM_ADJ_REASON_NONE);
             }
         } finally {
@@ -4398,7 +4398,6 @@
                         + (b != null ? b.apps.size() : 0));
 
                 boolean inDestroying = mDestroyingServices.contains(r);
-                boolean skipOomAdj = false;
                 if (b != null) {
                     if (b.apps.size() > 0 && !inDestroying) {
                         // Applications have already bound since the last
@@ -4423,11 +4422,11 @@
                         // a new client.
                         b.doRebind = true;
                     }
-                    skipOomAdj = Flags.serviceBindingOomAdjPolicy() && b.mSkippedOomAdj;
                 }
 
                 serviceDoneExecutingLocked(r, inDestroying, false, false,
-                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
+                        !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
+                        ? OOM_ADJ_REASON_UNBIND_SERVICE : OOM_ADJ_REASON_NONE);
             }
         } finally {
             mAm.mInjector.restoreCallingIdentity(origId);
@@ -4905,9 +4904,8 @@
      * Bump the given service record into executing state.
      * @param oomAdjReason The caller requests it to perform the oomAdjUpdate not {@link
      *         ActivityManagerInternal#OOM_ADJ_REASON_NONE}.
-     * @return {@code true} if it performed oomAdjUpdate.
      */
-    private boolean bumpServiceExecutingLocked(
+    private void bumpServiceExecutingLocked(
             ServiceRecord r, boolean fg, String why, @OomAdjReason int oomAdjReason,
             boolean skipTimeoutIfPossible) {
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
@@ -4972,19 +4970,17 @@
                 }
             }
         }
-        boolean oomAdjusted = false;
         if (oomAdjReason != OOM_ADJ_REASON_NONE && r.app != null
                 && r.app.mState.getCurProcState() > ActivityManager.PROCESS_STATE_SERVICE) {
             // Force an immediate oomAdjUpdate, so the client app could be in the correct process
             // state before doing any service related transactions
             mAm.enqueueOomAdjTargetLocked(r.app);
             mAm.updateOomAdjPendingTargetsLocked(oomAdjReason);
-            oomAdjusted = true;
+            r.mOomAdjBumpedInExec = true;
         }
         r.executeFg |= fg;
         r.executeNesting++;
         r.executingStart = SystemClock.uptimeMillis();
-        return oomAdjusted;
     }
 
     private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
@@ -5001,7 +4997,7 @@
                 & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND) != 0;
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
-                i.mSkippedOomAdj = !bumpServiceExecutingLocked(r, execInFg, "bind",
+                bumpServiceExecutingLocked(r, execInFg, "bind",
                         skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_BIND_SERVICE,
                         skipOomAdj /* skipTimeoutIfPossible */);
                 if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
@@ -5020,14 +5016,16 @@
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
                 final boolean inDestroying = mDestroyingServices.contains(r);
                 serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
-                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
+                        !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
+                        ? OOM_ADJ_REASON_UNBIND_SERVICE : OOM_ADJ_REASON_NONE);
                 throw e;
             } catch (RemoteException e) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                 // Keep the executeNesting count accurate.
                 final boolean inDestroying = mDestroyingServices.contains(r);
                 serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
-                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
+                        !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
+                        ? OOM_ADJ_REASON_UNBIND_SERVICE : OOM_ADJ_REASON_NONE);
                 return false;
             }
         }
@@ -5823,6 +5821,7 @@
             // process state before doing any service related transactions
             mAm.enqueueOomAdjTargetLocked(app);
             mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE);
+            r.mOomAdjBumpedInExec = true;
         } else {
             // Since we skipped the oom adj update, the Service#onCreate() might be running in
             // the cached state, if the service process drops into the cached state after the call.
@@ -5863,7 +5862,8 @@
                 // Keep the executeNesting count accurate.
                 final boolean inDestroying = mDestroyingServices.contains(r);
                 serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
-                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_STOP_SERVICE);
+                        !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
+                        ? OOM_ADJ_REASON_STOP_SERVICE : OOM_ADJ_REASON_NONE);
 
                 // Cleanup.
                 if (newService) {
@@ -5898,7 +5898,7 @@
                     null, null, 0, null, null, ActivityManager.PROCESS_STATE_UNKNOWN));
         }
 
-        sendServiceArgsLocked(r, execInFg, true);
+        sendServiceArgsLocked(r, execInFg, r.mOomAdjBumpedInExec);
 
         if (r.delayed) {
             if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
@@ -6085,7 +6085,8 @@
             }
         }
 
-        boolean oomAdjusted = false;
+        boolean oomAdjusted = Flags.serviceBindingOomAdjPolicy() && r.mOomAdjBumpedInExec;
+
         // Tell the service that it has been unbound.
         if (r.app != null && r.app.isThreadReady()) {
             for (int i = r.bindings.size() - 1; i >= 0; i--) {
@@ -6094,9 +6095,10 @@
                         + ": hasBound=" + ibr.hasBound);
                 if (ibr.hasBound) {
                     try {
-                        oomAdjusted |= bumpServiceExecutingLocked(r, false, "bring down unbind",
-                                OOM_ADJ_REASON_UNBIND_SERVICE,
-                                false /* skipTimeoutIfPossible */);
+                        bumpServiceExecutingLocked(r, false, "bring down unbind",
+                                oomAdjusted ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE,
+                                oomAdjusted /* skipTimeoutIfPossible */);
+                        oomAdjusted |= r.mOomAdjBumpedInExec;
                         ibr.hasBound = false;
                         ibr.requested = false;
                         r.app.getThread().scheduleUnbindService(r,
@@ -6252,10 +6254,11 @@
                     }
                 } else {
                     try {
-                        oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy",
-                                oomAdjusted ? 0 : OOM_ADJ_REASON_STOP_SERVICE,
-                                false /* skipTimeoutIfPossible */);
+                        bumpServiceExecutingLocked(r, false, "destroy",
+                                oomAdjusted ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE,
+                                oomAdjusted /* skipTimeoutIfPossible */);
                         mDestroyingServices.add(r);
+                        oomAdjusted |= r.mOomAdjBumpedInExec;
                         r.destroying = true;
                         r.app.getThread().scheduleStopService(r);
                     } catch (Exception e) {
@@ -6411,7 +6414,7 @@
                 final boolean skipOomAdj = (serviceBindingOomAdjPolicy
                         & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) != 0;
                 try {
-                    b.intent.mSkippedOomAdj = !bumpServiceExecutingLocked(s, false, "unbind",
+                    bumpServiceExecutingLocked(s, false, "unbind",
                             skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE,
                             skipOomAdj /* skipTimeoutIfPossible */);
                     if (b.client != s.app && c.notHasFlag(Context.BIND_WAIVE_PRIORITY)
@@ -6461,6 +6464,7 @@
         boolean inDestroying = mDestroyingServices.contains(r);
         if (r != null) {
             boolean skipOomAdj = false;
+            boolean needOomAdj = false;
             if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
                 // This is a call from a service start...  take care of
                 // book-keeping.
@@ -6536,15 +6540,13 @@
                     // Fake it to keep from ANR due to orphaned entry.
                     r.executeNesting = 1;
                 }
-            } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_REBIND
-                    || type == ActivityThread.SERVICE_DONE_EXECUTING_UNBIND) {
-                final Intent.FilterComparison filter = new Intent.FilterComparison(intent);
-                final IntentBindRecord b = r.bindings.get(filter);
-                skipOomAdj = Flags.serviceBindingOomAdjPolicy() && b != null && b.mSkippedOomAdj;
+                // The service is done, force an oom adj update.
+                needOomAdj = true;
             }
             final long origId = mAm.mInjector.clearCallingIdentity();
             serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj,
-                    skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_EXECUTING_SERVICE);
+                    !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec || needOomAdj
+                    ? OOM_ADJ_REASON_EXECUTING_SERVICE : OOM_ADJ_REASON_NONE);
             mAm.mInjector.restoreCallingIdentity(origId);
         } else {
             Slog.w(TAG, "Done executing unknown service from pid "
@@ -6600,18 +6602,16 @@
                     mDestroyingServices.remove(r);
                     r.bindings.clear();
                 }
-                boolean oomAdjusted = false;
                 if (oomAdjReason != OOM_ADJ_REASON_NONE) {
                     if (enqueueOomAdj) {
                         mAm.enqueueOomAdjTargetLocked(r.app);
                     } else {
                         mAm.updateOomAdjLocked(r.app, oomAdjReason);
                     }
-                    oomAdjusted = true;
                 } else {
                     // Skip oom adj if it wasn't bumped during the bumpServiceExecutingLocked()
-                    oomAdjusted = false;
                 }
+                r.mOomAdjBumpedInExec = false;
             }
             r.executeFg = false;
             if (r.tracker != null) {
@@ -6995,6 +6995,7 @@
             sr.setProcess(null, null, 0, null);
             sr.isolationHostProc = null;
             sr.executeNesting = 0;
+            sr.mOomAdjBumpedInExec = false;
             synchronized (mAm.mProcessStats.mLock) {
                 sr.forceClearTracker();
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e222878..663ba8a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9028,6 +9028,7 @@
                         Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
                                 + Process.myUid());
                         BinderProxy.dumpProxyDebugInfo();
+                        CriticalEventLog.getInstance().logExcessiveBinderCalls(uid);
                         if (uid == Process.SYSTEM_UID) {
                             Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
                         } else {
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index db47e3f..1a3652e 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -49,14 +49,6 @@
 
     String stringName;      // caching of toString
 
-    /**
-     * Mark if we've skipped oom adj update before calling into the {@link Service#onBind()}
-     * or {@link Service#onUnbind()}.
-     *
-     * <p>If it's true, we'll skip the oom adj update too during the serviceDoneExecuting.
-     */
-    boolean mSkippedOomAdj;
-
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("service="); pw.println(service);
         dumpInService(pw, prefix);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 3c8d7fc..e3aac02 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -267,6 +267,11 @@
     int mAllowStart_byBindings = REASON_DENIED;
 
     /**
+     * Whether or not we've bumped its oom adj scores during its execution.
+     */
+    boolean mOomAdjBumpedInExec;
+
+    /**
      * Whether to use the new "while-in-use permission" logic for FGS start
      */
     private boolean useNewWiuLogic_forStart() {
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 16dbe18..c06bdf9 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -53,3 +53,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    namespace: "backstage_power"
+    name: "defer_outgoing_bcasts"
+    description: "Defer outgoing broadcasts from processes in freezable state"
+    bug: "327496592"
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 007b746..fb826c8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -24,6 +24,7 @@
 import android.app.TaskStackListener;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricFaceConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.ComponentInfoInternal;
@@ -39,6 +40,7 @@
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -64,6 +66,7 @@
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -359,6 +362,17 @@
                     null /* callback */);
         }
 
+        if (Build.isDebuggable()) {
+            BiometricUtils<Face> utils = FaceUtils.getInstance(
+                    mFaceSensors.keyAt(0));
+            for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+                List<Face> enrollments = utils.getBiometricsForUser(mContext, user.id);
+                Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
+                        + enrollments.stream().map(
+                        BiometricAuthenticator.Identifier::getBiometricId).toList());
+            }
+        }
+
         return mDaemon;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index a104cf4..c04c47e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.content.res.TypedArray;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.IInvalidationCallback;
@@ -46,6 +47,7 @@
 import android.hardware.fingerprint.ISidefpsController;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -71,6 +73,7 @@
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -382,6 +385,17 @@
                     null /* callback */);
         }
 
+        if (Build.isDebuggable()) {
+            BiometricUtils<Fingerprint> utils = FingerprintUtils.getInstance(
+                    mFingerprintSensors.keyAt(0));
+            for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+                List<Fingerprint> enrollments = utils.getBiometricsForUser(mContext, user.id);
+                Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
+                        + enrollments.stream().map(
+                                BiometricAuthenticator.Identifier::getBiometricId).toList());
+            }
+        }
+
         return mDaemon;
     }
 
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index 816c349..036284f 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -30,6 +30,7 @@
 import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
 import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
 import com.android.server.criticalevents.nano.CriticalEventProto.InstallPackages;
+import com.android.server.criticalevents.nano.CriticalEventProto.ExcessiveBinderCalls;
 import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
 import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
 import com.android.server.criticalevents.nano.CriticalEventProto.SystemServerStarted;
@@ -143,6 +144,15 @@
         return System.currentTimeMillis();
     }
 
+   /** Logs when a uid sends an excessive number of binder calls. */
+    public void logExcessiveBinderCalls(int uid) {
+        CriticalEventProto event = new CriticalEventProto();
+        ExcessiveBinderCalls calls = new ExcessiveBinderCalls();
+        calls.uid = uid;
+        event.setExcessiveBinderCalls(calls);
+        log(event);
+    }
+
     /** Logs when one or more packages are installed. */
     public void logInstallPackagesStarted() {
         CriticalEventProto event = new CriticalEventProto();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dbe85ea..76f3035 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1474,6 +1474,7 @@
                 && mFlags.isDisplayOffloadEnabled()
                 && mPowerRequest.policy == POLICY_DOZE
                 && mDisplayOffloadSession != null
+                && mAutomaticBrightnessController != null
                 && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
             rawBrightnessState = mAutomaticBrightnessController
                     .getAutomaticScreenBrightnessBasedOnLastObservedLux(mTempBrightnessEvent);
@@ -1724,6 +1725,7 @@
         mTempBrightnessEvent.setBrightness(brightnessState);
         mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
         mTempBrightnessEvent.setDisplayState(state);
+        mTempBrightnessEvent.setDisplayPolicy(mPowerRequest.policy);
         mTempBrightnessEvent.setReason(mBrightnessReason);
         mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax());
         mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode());
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 5423b74..82b401a 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -16,6 +16,9 @@
 
 package com.android.server.display.brightness;
 
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.policyToString;
+
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
 import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
 
@@ -46,6 +49,7 @@
     private int mDisplayId;
     private String mPhysicalDisplayId;
     private int mDisplayState;
+    private int mDisplayPolicy;
     private long mTime;
     private float mLux;
     private float mPreThresholdLux;
@@ -85,6 +89,7 @@
         mDisplayId = that.getDisplayId();
         mPhysicalDisplayId = that.getPhysicalDisplayId();
         mDisplayState = that.mDisplayState;
+        mDisplayPolicy = that.mDisplayPolicy;
         mTime = that.getTime();
         // Lux values
         mLux = that.getLux();
@@ -117,6 +122,7 @@
         mTime = SystemClock.uptimeMillis();
         mPhysicalDisplayId = "";
         mDisplayState = Display.STATE_UNKNOWN;
+        mDisplayPolicy = POLICY_OFF;
         // Lux values
         mLux = 0;
         mPreThresholdLux = 0;
@@ -155,6 +161,7 @@
                 && mDisplayId == that.mDisplayId
                 && mPhysicalDisplayId.equals(that.mPhysicalDisplayId)
                 && mDisplayState == that.mDisplayState
+                && mDisplayPolicy == that.mDisplayPolicy
                 && Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux)
                 && Float.floatToRawIntBits(mPreThresholdLux)
                 == Float.floatToRawIntBits(that.mPreThresholdLux)
@@ -191,6 +198,7 @@
                 + "disp=" + mDisplayId
                 + ", physDisp=" + mPhysicalDisplayId
                 + ", displayState=" + Display.stateToString(mDisplayState)
+                + ", displayPolicy=" + policyToString(mDisplayPolicy)
                 + ", brt=" + mBrightness + ((mFlags & FLAG_USER_SET) != 0 ? "(user_set)" : "")
                 + ", initBrt=" + mInitialBrightness
                 + ", rcmdBrt=" + mRecommendedBrightness
@@ -251,6 +259,10 @@
         mDisplayState = state;
     }
 
+    public void setDisplayPolicy(int policy) {
+        mDisplayPolicy = policy;
+    }
+
     public float getLux() {
         return mLux;
     }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 9a76ebd..29ea071 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -108,7 +108,6 @@
 import android.provider.Settings;
 import android.security.AndroidKeyStoreMaintenance;
 import android.security.Authorization;
-import android.security.KeyStore;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
 import android.security.keystore.recovery.KeyChainProtectionParams;
@@ -169,6 +168,7 @@
 import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
+import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
@@ -292,7 +292,7 @@
     private final IActivityManager mActivityManager;
     private final SyntheticPasswordManager mSpManager;
 
-    private final java.security.KeyStore mJavaKeyStore;
+    private final KeyStore mKeyStore;
     private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
     private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache;
 
@@ -564,10 +564,6 @@
             return DeviceStateCache.getInstance();
         }
 
-        public KeyStore getKeyStore() {
-            return KeyStore.getInstance();
-        }
-
         public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
             return RecoverableKeyStoreManager.getInstance(mContext);
         }
@@ -619,9 +615,9 @@
             return (BiometricManager) mContext.getSystemService(Context.BIOMETRIC_SERVICE);
         }
 
-        public java.security.KeyStore getJavaKeyStore() {
+        public KeyStore getKeyStore() {
             try {
-                java.security.KeyStore ks = java.security.KeyStore.getInstance(
+                KeyStore ks = KeyStore.getInstance(
                         SyntheticPasswordCrypto.androidKeystoreProviderName());
                 ks.load(new AndroidKeyStoreLoadStoreParameter(
                         SyntheticPasswordCrypto.keyNamespace()));
@@ -631,8 +627,7 @@
             }
         }
 
-        public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
-                java.security.KeyStore ks) {
+        public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(KeyStore ks) {
             return new UnifiedProfilePasswordCache(ks);
         }
 
@@ -654,7 +649,7 @@
     protected LockSettingsService(Injector injector) {
         mInjector = injector;
         mContext = injector.getContext();
-        mJavaKeyStore = injector.getJavaKeyStore();
+        mKeyStore = injector.getKeyStore();
         mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
         mHandler = injector.getHandler(injector.getServiceThread());
         mStrongAuth = injector.getStrongAuth();
@@ -676,7 +671,7 @@
         mGatekeeperPasswords = new LongSparseArray<>();
 
         mSpManager = injector.getSyntheticPasswordManager(mStorage);
-        mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore);
+        mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mKeyStore);
         mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
 
         mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
@@ -1482,7 +1477,7 @@
         byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
                 storedData.length);
         byte[] decryptionResult;
-        SecretKey decryptionKey = (SecretKey) mJavaKeyStore.getKey(
+        SecretKey decryptionKey = (SecretKey) mKeyStore.getKey(
                 PROFILE_KEY_NAME_DECRYPT + userId, null);
 
         Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
@@ -1875,9 +1870,10 @@
         }
     }
 
-    private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
-        updatePasswordHistory(newCredential, userHandle);
-        mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
+    private void onPostPasswordChanged(LockscreenCredential newCredential, int userId) {
+        updatePasswordHistory(newCredential, userId);
+        mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userId);
+        sendMainUserCredentialChangedNotificationIfNeeded(userId);
     }
 
     /**
@@ -2076,16 +2072,16 @@
             keyGenerator.init(new SecureRandom());
             SecretKey secretKey = keyGenerator.generateKey();
             try {
-                mJavaKeyStore.setEntry(
+                mKeyStore.setEntry(
                         PROFILE_KEY_NAME_ENCRYPT + profileUserId,
-                        new java.security.KeyStore.SecretKeyEntry(secretKey),
+                        new KeyStore.SecretKeyEntry(secretKey),
                         new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
                                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                                 .build());
-                mJavaKeyStore.setEntry(
+                mKeyStore.setEntry(
                         PROFILE_KEY_NAME_DECRYPT + profileUserId,
-                        new java.security.KeyStore.SecretKeyEntry(secretKey),
+                        new KeyStore.SecretKeyEntry(secretKey),
                         new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
                                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
@@ -2094,7 +2090,7 @@
                                 .setUserAuthenticationValidityDurationSeconds(30)
                                 .build());
                 // Key imported, obtain a reference to it.
-                SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey(
+                SecretKey keyStoreEncryptionKey = (SecretKey) mKeyStore.getKey(
                         PROFILE_KEY_NAME_ENCRYPT + profileUserId, null);
                 Cipher cipher = Cipher.getInstance(
                         KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
@@ -2104,7 +2100,7 @@
                 iv = cipher.getIV();
             } finally {
                 // The original key can now be discarded.
-                mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
+                mKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
             }
         } catch (UnrecoverableKeyException
                 | BadPaddingException | IllegalBlockSizeException | KeyStoreException
@@ -2556,11 +2552,10 @@
         final String encryptAlias = PROFILE_KEY_NAME_ENCRYPT + targetUserId;
         final String decryptAlias = PROFILE_KEY_NAME_DECRYPT + targetUserId;
         try {
-            if (mJavaKeyStore.containsAlias(encryptAlias) ||
-                    mJavaKeyStore.containsAlias(decryptAlias)) {
+            if (mKeyStore.containsAlias(encryptAlias) || mKeyStore.containsAlias(decryptAlias)) {
                 Slogf.i(TAG, "Removing keystore profile key for user %d", targetUserId);
-                mJavaKeyStore.deleteEntry(encryptAlias);
-                mJavaKeyStore.deleteEntry(decryptAlias);
+                mKeyStore.deleteEntry(encryptAlias);
+                mKeyStore.deleteEntry(decryptAlias);
             }
         } catch (KeyStoreException e) {
             // We have tried our best to remove the key.
@@ -3062,7 +3057,6 @@
         setCurrentLskfBasedProtectorId(newProtectorId, userId);
         LockPatternUtils.invalidateCredentialTypeCache();
         synchronizeUnifiedChallengeForProfiles(userId, profilePasswords);
-        sendMainUserCredentialChangedNotificationIfNeeded(userId);
 
         setUserPasswordMetrics(credential, userId);
         mUnifiedProfilePasswordCache.removePassword(userId);
@@ -3457,7 +3451,7 @@
 
     private void dumpKeystoreKeys(IndentingPrintWriter pw) {
         try {
-            final Enumeration<String> aliases = mJavaKeyStore.aliases();
+            final Enumeration<String> aliases = mKeyStore.aliases();
             while (aliases.hasMoreElements()) {
                 pw.println(aliases.nextElement());
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 69f7906..fe8030b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1541,6 +1541,19 @@
             return;
         }
 
+        // Initialize all necessary settings for archival installation.
+        pkgSetting
+                // No package.
+                .setPkg(null)
+                // Mark for later restore.
+                .setPendingRestore(true);
+        for (int userId : userIds) {
+            // Unmark "installed" for all users.
+            pkgSetting
+                    .modifyUserState(userId)
+                    .setInstalled(false);
+        }
+
         String responsibleInstallerPackage = PackageArchiver.getResponsibleInstallerPackage(
                 pkgSetting);
         // TODO(b/278553670) Check if responsibleInstallerPackage supports unarchival.
@@ -1551,16 +1564,11 @@
         for (int userId : userIds) {
             var archiveState = mInstallerService.mPackageArchiver.createArchiveState(
                     archivePackage, userId, responsibleInstallerPackage);
-            if (archiveState == null) {
-                continue;
-            }
-            pkgSetting
-                    .setPkg(null)
-                    // This package was installed as archived. Need to mark it for later restore.
-                    .setPendingRestore(true)
+            if (archiveState != null) {
+                pkgSetting
                     .modifyUserState(userId)
-                    .setInstalled(false)
                     .setArchiveState(archiveState);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ed5df5f..981c4c0 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -506,7 +506,8 @@
                     // keep backwards compatibility we remove the task from recents when finishing
                     // task with root activity.
                     mTaskSupervisor.removeTask(tr, false /*killProcess*/,
-                            finishWithRootActivity, "finish-activity", r.getUid(), r.info.name);
+                            finishWithRootActivity, "finish-activity", r.getUid(), r.getPid(),
+                            r.info.name);
                     res = true;
                     // Explicitly dismissing the activity so reset its relaunch flag.
                     r.mRelaunchReason = RELAUNCH_REASON_NONE;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 90cff39..92fde18 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1476,7 +1476,8 @@
         }
     }
 
-    private void scheduleConfigurationChanged(Configuration config) {
+    private void scheduleConfigurationChanged(@NonNull Configuration config,
+            @NonNull ActivityWindowInfo activityWindowInfo) {
         if (!attachedToProcess()) {
             ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration "
                     + "update - client not running, activityRecord=%s", this);
@@ -1487,7 +1488,7 @@
                     + "config: %s", this, config);
 
             mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                    ActivityConfigurationChangeItem.obtain(token, config));
+                    ActivityConfigurationChangeItem.obtain(token, config, activityWindowInfo));
         } catch (RemoteException e) {
             // If process died, whatever.
         }
@@ -9785,7 +9786,11 @@
         // configurations because there are cases (like moving a task to the root pinned task) where
         // the combine configurations are equal, but would otherwise differ in the override config
         mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
-        if (getConfiguration().equals(mTmpConfig) && !displayChanged) {
+        final ActivityWindowInfo newActivityWindowInfo = getActivityWindowInfo();
+        final boolean isActivityWindowInfoChanged = Flags.activityWindowInfoFlag()
+                && !mLastReportedActivityWindowInfo.equals(newActivityWindowInfo);
+        if (!displayChanged && !isActivityWindowInfoChanged
+                && getConfiguration().equals(mTmpConfig)) {
             ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
                     + "unchanged in %s", this);
             return true;
@@ -9800,7 +9805,6 @@
 
         // Update last reported values.
         final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
-        final ActivityWindowInfo newActivityWindowInfo = getActivityWindowInfo();
 
         setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
         setLastReportedActivityWindowInfo(newActivityWindowInfo);
@@ -9823,7 +9827,7 @@
                 scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig,
                         newActivityWindowInfo);
             } else {
-                scheduleConfigurationChanged(newMergedOverrideConfig);
+                scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
             }
             notifyDisplayCompatPolicyAboutConfigurationChange(
                     mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
@@ -9891,7 +9895,7 @@
             scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig,
                     newActivityWindowInfo);
         } else {
-            scheduleConfigurationChanged(newMergedOverrideConfig);
+            scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
         }
         notifyDisplayCompatPolicyAboutConfigurationChange(
                 mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index 01d077a..4149bd9 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -41,7 +41,7 @@
     static final String DOC_LINK = "go/android-asm";
 
     /** Used to determine which version of the ASM logic was used in logs while we iterate */
-    static final int ASM_VERSION = 9;
+    static final int ASM_VERSION = 10;
 
     private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
     private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c137c54..6ad056f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2089,7 +2089,7 @@
 
         if (!mSupervisor.getBackgroundActivityLaunchController().checkActivityAllowedToStart(
                 mSourceRecord, r, newTask, avoidMoveToFront(), targetTask, mLaunchFlags, mBalCode,
-                mCallingUid, mRealCallingUid)) {
+                mCallingUid, mRealCallingUid, mPreferredTaskDisplayArea)) {
             return START_ABORTED;
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index aefa777..d1d498d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -42,6 +42,7 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+import static android.os.Process.INVALID_PID;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -1652,11 +1653,11 @@
      * @return Returns true if the given task was found and removed.
      */
     boolean removeTaskById(int taskId, boolean killProcess, boolean removeFromRecents,
-            String reason, int callingUid) {
+            String reason, int callingUid, int callingPid) {
         final Task task =
                 mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
         if (task != null) {
-            removeTask(task, killProcess, removeFromRecents, reason, callingUid, null);
+            removeTask(task, killProcess, removeFromRecents, reason, callingUid, callingPid, null);
             return true;
         }
         Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
@@ -1664,11 +1665,11 @@
     }
 
     void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
-        removeTask(task, killProcess, removeFromRecents, reason, SYSTEM_UID, null);
+        removeTask(task, killProcess, removeFromRecents, reason, SYSTEM_UID, INVALID_PID, null);
     }
 
     void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason,
-            int callingUid, String callerActivityClassName) {
+            int callingUid, int callingPid, String callerActivityClassName) {
         if (task.mInRemoveTask) {
             // Prevent recursion.
             return;
@@ -1705,8 +1706,8 @@
             if (task.isPersistable) {
                 mService.notifyTaskPersisterLocked(null, true);
             }
-            mBalController
-                    .checkActivityAllowedToClearTask(task, callingUid, callerActivityClassName);
+            mBalController.checkActivityAllowedToClearTask(
+                            task, callingUid, callingPid, callerActivityClassName);
         } finally {
             task.mInRemoveTask = false;
         }
@@ -1874,7 +1875,7 @@
             // Task was trimmed from the recent tasks list -- remove the active task record as well
             // since the user won't really be able to go back to it
             removeTaskById(task.mTaskId, killProcess, false /* removeFromRecents */,
-                    "recent-task-trimmed", SYSTEM_UID);
+                    "recent-task-trimmed", SYSTEM_UID, INVALID_PID);
         }
         task.removedFromRecents();
     }
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 50de0b0..d699af8 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -77,11 +77,13 @@
 
         synchronized (mService.mGlobalLock) {
             int origCallingUid = Binder.getCallingUid();
+            int origCallingPid = Binder.getCallingPid();
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 // We remove the task from recents to preserve backwards
                 if (!mService.mTaskSupervisor.removeTaskById(mTaskId, false,
-                        REMOVE_FROM_RECENTS, "finish-and-remove-task", origCallingUid)) {
+                        REMOVE_FROM_RECENTS, "finish-and-remove-task", origCallingUid,
+                        origCallingPid)) {
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                 }
             } finally {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4e28384..071f403 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -25,9 +25,12 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+import static android.os.Process.INVALID_PID;
+import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
 
+import static com.android.server.wm.ActivityStarter.ASM_RESTRICTIONS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -67,6 +70,7 @@
 import android.util.Slog;
 import android.widget.Toast;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
@@ -75,6 +79,7 @@
 import com.android.server.am.PendingIntentRecord;
 
 import java.lang.annotation.Retention;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.StringJoiner;
 import java.util.function.Consumer;
@@ -1022,7 +1027,7 @@
     }
 
     /**
-     * Log activity starts which violate one of the following rules of the
+     * Check activity starts which violate one of the following rules of the
      * activity security model (ASM):
      * See go/activity-security for rationale behind the rules.
      * 1. Within a task, only an activity matching a top UID of the task can start activities
@@ -1032,7 +1037,7 @@
     boolean checkActivityAllowedToStart(@Nullable ActivityRecord sourceRecord,
             @NonNull ActivityRecord targetRecord, boolean newTask, boolean avoidMoveTaskToFront,
             @Nullable Task targetTask, int launchFlags, int balCode, int callingUid,
-            int realCallingUid) {
+            int realCallingUid, TaskDisplayArea preferredTaskDisplayArea) {
         // BAL Exception allowed in all cases
         if (balCode == BAL_ALLOW_ALLOWLISTED_UID) {
             return true;
@@ -1055,68 +1060,46 @@
             }
         }
 
-        if (balCode == BAL_ALLOW_GRACE_PERIOD) {
-            // Allow if launching into new task, and caller matches most recently finished activity
-            if (taskToFront && mTopFinishedActivity != null
-                    && mTopFinishedActivity.mUid == callingUid) {
-                return true;
-            }
-
-            // Launching into existing task - allow if matches most recently finished activity
-            // within the task.
-            // We can reach here multiple ways:
-            // 1. activity in fg fires intent (taskToFront = false, sourceRecord is available)
-            // 2. activity in bg fires intent (taskToFront = false, sourceRecord is available)
-            // 3. activity in bg fires intent with NEW_FLAG (taskToFront = true,
-            //         avoidMoveTaskToFront = true, sourceRecord is available)
-            // 4. activity in bg fires PI (taskToFront = true, avoidMoveTaskToFront = true,
-            //         sourceRecord is not available, targetTask may be available)
-            if (!taskToFront || avoidMoveTaskToFront) {
-                if (targetTask != null) {
-                    FinishedActivityEntry finishedEntry =
-                            mTaskIdToFinishedActivity.get(targetTask.mTaskId);
-                    if (finishedEntry != null && finishedEntry.mUid == callingUid) {
-                        return true;
-                    }
-                }
-
-                if (sourceRecord != null) {
-                    FinishedActivityEntry finishedEntry =
-                            mTaskIdToFinishedActivity.get(sourceRecord.getTask().mTaskId);
-                    if (finishedEntry != null && finishedEntry.mUid == callingUid) {
-                        return true;
-                    }
-                }
-            }
-        }
-
-        BlockActivityStart bas = null;
+        BlockActivityStart bas = new BlockActivityStart();
         if (sourceRecord != null) {
-            boolean passesAsmChecks = true;
             Task sourceTask = sourceRecord.getTask();
 
+            Task taskToCheck = taskToFront ? sourceTask : targetTask;
+            bas = checkTopActivityForAsm(taskToCheck, sourceRecord.getUid(),
+                    sourceRecord, bas);
+
             // Allow launching into a new task (or a task matching the launched activity's
             // affinity) only if the current task is foreground or mutating its own task.
             // The latter can happen eg. if caller uses NEW_TASK flag and the activity being
             // launched matches affinity of source task.
-            if (taskToFront) {
-                passesAsmChecks = sourceTask != null
-                        && (sourceTask.isVisible() || sourceTask == targetTask);
-            }
-
-            if (passesAsmChecks) {
-                Task taskToCheck = taskToFront ? sourceTask : targetTask;
-                bas = isTopActivityMatchingUidAbsentForAsm(taskToCheck, sourceRecord.getUid(),
-                        sourceRecord);
+            if (taskToFront && bas.mTopActivityMatchesSource) {
+                bas.mTopActivityMatchesSource = (sourceTask != null
+                        && (sourceTask.isVisible() || sourceTask == targetTask));
             }
         } else if (targetTask != null && (!taskToFront || avoidMoveTaskToFront)) {
             // We don't have a sourceRecord, and we're launching into an existing task.
             // Allow if callingUid is top of stack.
-            bas = isTopActivityMatchingUidAbsentForAsm(targetTask, callingUid,
-                    /*sourceRecord*/null);
+            bas = checkTopActivityForAsm(targetTask, callingUid,
+                    /*sourceRecord*/null, bas);
+        } else {
+            // We're launching from a non-visible activity. Has any visible app opted in?
+            TaskDisplayArea displayArea = targetTask != null && targetTask.getDisplayArea() != null
+                    ? targetTask.getDisplayArea()
+                    : preferredTaskDisplayArea;
+            if (displayArea != null) {
+                ArrayList<Task> visibleTasks = displayArea.getVisibleTasks();
+                for (int i = 0; i < visibleTasks.size(); i++) {
+                    Task task = visibleTasks.get(i);
+                    if (visibleTasks.size() == 1 && task.isActivityTypeHomeOrRecents()) {
+                        bas.optedIn(task.getTopMostActivity());
+                    } else {
+                        bas = checkTopActivityForAsm(task, callingUid, /*sourceRecord*/null, bas);
+                    }
+                }
+            }
         }
 
-        if (bas != null && !bas.mWouldBlockActivityStartIgnoringFlag) {
+        if (bas.mTopActivityMatchesSource) {
             return true;
         }
 
@@ -1140,13 +1123,16 @@
                 ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_SAME_TASK
                 : FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_DIFFERENT_TASK);
 
-        boolean blockActivityStartAndFeatureEnabled = ActivitySecurityModelFeatureFlags
-                .shouldRestrictActivitySwitch(callingUid)
-                && (bas == null || bas.mBlockActivityStartIfFlagEnabled);
+        boolean enforceBlock = bas.mTopActivityOptedIn
+                && ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(callingUid);
+
+        boolean allowedByGracePeriod = allowedByAsmGracePeriod(callingUid, sourceRecord, targetTask,
+                balCode, taskToFront, avoidMoveTaskToFront);
 
         String asmDebugInfo = getDebugInfoForActivitySecurity("Launch", sourceRecord,
                 targetRecord, targetTask, targetTopActivity, realCallingUid, balCode,
-                blockActivityStartAndFeatureEnabled, taskToFront, avoidMoveTaskToFront);
+                enforceBlock, taskToFront, avoidMoveTaskToFront, allowedByGracePeriod,
+                bas.mActivityOptedIn);
 
         FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
                 /* caller_uid */
@@ -1184,7 +1170,7 @@
         String launchedFromPackageName = targetRecord.launchedFromPackage;
         if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
             String toastText = ActivitySecurityModelFeatureFlags.DOC_LINK
-                    + (blockActivityStartAndFeatureEnabled ? " blocked " : " would block ")
+                    + (enforceBlock ? " blocked " : " would block ")
                     + getApplicationLabel(mService.mContext.getPackageManager(),
                     launchedFromPackageName);
             showToast(toastText);
@@ -1192,7 +1178,7 @@
             Slog.i(TAG, asmDebugInfo);
         }
 
-        if (blockActivityStartAndFeatureEnabled) {
+        if (enforceBlock) {
             Slog.e(TAG, "[ASM] Abort Launching r: " + targetRecord
                     + " as source: "
                     + (sourceRecord != null ? sourceRecord : launchedFromPackageName)
@@ -1251,18 +1237,18 @@
 
         // Find the first activity which matches a safe UID and is not finishing. Clear everything
         // above it
+        int[] finishCount = new int[1];
         boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags
                 .shouldRestrictActivitySwitch(callingUid);
-        int[] finishCount = new int[0];
-        if (shouldBlockActivityStart
-                && blockCrossUidActivitySwitchFromBelowForActivity(targetTaskTop)) {
+        BlockActivityStart bas = checkCrossUidActivitySwitchFromBelow(
+                targetTaskTop, callingUid, new BlockActivityStart());
+        if (shouldBlockActivityStart && bas.mTopActivityOptedIn) {
             ActivityRecord activity = targetTask.getActivity(isLaunchingOrLaunched);
             if (activity == null) {
                 // mStartActivity is not in task, so clear everything
                 activity = targetRecord;
             }
 
-            finishCount = new int[1];
             targetTask.performClearTop(activity, launchFlags, finishCount);
             if (finishCount[0] > 0) {
                 Slog.w(TAG, "Cleared top n: " + finishCount[0] + " activities from task t: "
@@ -1279,7 +1265,8 @@
 
             Slog.i(TAG, getDebugInfoForActivitySecurity("Clear Top", sourceRecord, targetRecord,
                     targetTask, targetTaskTop, realCallingUid, balCode, shouldBlockActivityStart,
-                    /* taskToFront */ true, /* avoidMoveTaskToFront */ false));
+                    /* taskToFront */ true, /* avoidMoveTaskToFront */ false,
+                    /* allowedByAsmGracePeriod */ false, bas.mActivityOptedIn));
         }
     }
 
@@ -1287,7 +1274,7 @@
      * Returns home if the passed in callingUid is not top of the stack, rather than returning to
      * previous task.
      */
-    void checkActivityAllowedToClearTask(@NonNull Task task, int callingUid,
+    void checkActivityAllowedToClearTask(@NonNull Task task, int callingUid, int callingPid,
             @NonNull String callerActivityClassName) {
         // We may have already checked that the callingUid has additional clearTask privileges, and
         // cleared the calling identify. If so, we infer we do not need further restrictions here.
@@ -1295,14 +1282,28 @@
             return;
         }
 
+        String packageName =  mService.mContext.getPackageManager().getNameForUid(callingUid);
+        BalState state = new BalState(callingUid, callingPid, packageName, INVALID_UID,
+                INVALID_PID, null, null, null, null, null, ActivityOptions.makeBasic());
+        @BalCode int balCode = checkBackgroundActivityStartAllowedByCaller(state).mCode;
+        if (balCode == BAL_ALLOW_ALLOWLISTED_UID
+                || balCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
+                || balCode == BAL_ALLOW_PERMISSION
+                || balCode == BAL_ALLOW_SAW_PERMISSION
+                || balCode == BAL_ALLOW_VISIBLE_WINDOW
+                || balCode == BAL_ALLOW_NON_APP_VISIBLE_WINDOW) {
+            return;
+        }
+
         TaskDisplayArea displayArea = task.getTaskDisplayArea();
         if (displayArea == null) {
             // If there is no associated display area, we can not return home.
             return;
         }
 
-        BlockActivityStart bas = isTopActivityMatchingUidAbsentForAsm(task, callingUid, null);
-        if (!bas.mWouldBlockActivityStartIgnoringFlag) {
+        BlockActivityStart bas = checkTopActivityForAsm(task, callingUid, null,
+                new BlockActivityStart());
+        if (bas.mTopActivityMatchesSource) {
             return;
         }
 
@@ -1339,8 +1340,7 @@
         );
 
         boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags
-                .shouldRestrictActivitySwitch(callingUid)
-                        && bas.mBlockActivityStartIfFlagEnabled;
+                .shouldRestrictActivitySwitch(callingUid) && bas.mTopActivityOptedIn;
 
         PackageManager pm = mService.mContext.getPackageManager();
         String callingPackage = pm.getNameForUid(callingUid);
@@ -1381,32 +1381,30 @@
      * <p>
      * The 'sourceRecord' can be considered top even if it is 'finishing'
      * <p>
-     * Returns a class where the elements are:
-     * <pre>
-     * shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
-     * consideration feature flag and targetSdk).
-     * wouldBlockActivityStartIgnoringFlags: {@code true} if we should warn about the transition via
-     * toasts. This happens if the transition would be blocked in case both the app was targeting V+
-     * and the feature was enabled.
-     * </pre>
      */
-    private BlockActivityStart isTopActivityMatchingUidAbsentForAsm(@NonNull Task task,
-            int uid, @Nullable ActivityRecord sourceRecord) {
+    private BlockActivityStart checkTopActivityForAsm(@NonNull Task task,
+            int uid, @Nullable ActivityRecord sourceRecord, BlockActivityStart bas) {
         // If the source is visible, consider it 'top'.
         if (sourceRecord != null && sourceRecord.isVisibleRequested()) {
-            return BlockActivityStart.ACTIVITY_START_ALLOWED;
+            return bas.matchesSource();
         }
 
-        // Always allow actual top activity to clear task
+        // Always allow actual top activity
         ActivityRecord topActivity = task.getTopMostActivity();
-        if (topActivity != null && topActivity.isUid(uid)) {
-            return BlockActivityStart.ACTIVITY_START_ALLOWED;
+        if (topActivity == null) {
+            Slog.wtf(TAG, "Activities for task: " + task + " not found.");
+            return bas.optedIn(topActivity);
+        }
+
+        bas = checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
+        if (bas.mTopActivityMatchesSource) {
+            return bas;
         }
 
         // If UID is visible in target task, allow launch
         if (task.forAllActivities((Predicate<ActivityRecord>)
                 ar -> ar.isUid(uid) && ar.isVisibleRequested())) {
-            return BlockActivityStart.ACTIVITY_START_ALLOWED;
+            return bas.matchesSource();
         }
 
         // Consider the source activity, whether or not it is finishing. Do not consider any other
@@ -1417,82 +1415,91 @@
         // Check top of stack (or the first task fragment for embedding).
         topActivity = task.getActivity(topOfStackPredicate);
         if (topActivity == null) {
-            return new BlockActivityStart(true, true);
+            return bas;
         }
 
-        BlockActivityStart pair = blockCrossUidActivitySwitchFromBelow(topActivity, uid);
-        if (!pair.mBlockActivityStartIfFlagEnabled) {
-            return pair;
+        bas = checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
+        if (bas.mTopActivityMatchesSource) {
+            return bas;
         }
 
         // Even if the top activity is not a match, we may be in an embedded activity scenario with
         // an adjacent task fragment. Get the second fragment.
         TaskFragment taskFragment = topActivity.getTaskFragment();
         if (taskFragment == null) {
-            return pair;
+            return bas;
         }
 
         TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
         if (adjacentTaskFragment == null) {
-            return pair;
+            return bas;
         }
 
         // Check the second fragment.
         topActivity = adjacentTaskFragment.getActivity(topOfStackPredicate);
         if (topActivity == null) {
-            return new BlockActivityStart(true, true);
+            return bas;
         }
 
-        return blockCrossUidActivitySwitchFromBelow(topActivity, uid);
+        return checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
     }
 
     /**
      * Determines if a source is allowed to add or remove activities from the task,
      * if the current ActivityRecord is above it in the stack
      * <p>
-     * A transition is blocked ({@code false} returned) if all of the following are met:
+     * A transition is blocked if all of the following are met:
      * <pre>
      * 1. The source activity and the current activity record belong to different apps
      * (i.e, have different UIDs).
-     * 2. Both the source activity and the current activity target U+
-     * 3. The current activity has not set
+     * 2. The current activity target V+
+     * 3. The current app has set
+     * {@link R.styleable#AndroidManifestApplication_allowCrossUidActivitySwitchFromBelow}
+     * to {@code false}
+     * 4. The current activity has not set
      * {@link ActivityRecord#setAllowCrossUidActivitySwitchFromBelow(boolean)} to {@code true}
      * </pre>
      *
-     * Returns a class where the elements are:
-     * <pre>
-     * shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
-     * consideration feature flag and targetSdk).
-     * wouldBlockActivityStartIgnoringFlags: {@code true} if we should warn about the transition via
-     * toasts. This happens if the transition would be blocked in case both the app was targeting V+
-     * and the feature was enabled.
-     * </pre>
      *
      * @param sourceUid The source (s) activity performing the state change
      */
-    private BlockActivityStart blockCrossUidActivitySwitchFromBelow(ActivityRecord ar,
-            int sourceUid) {
+    private BlockActivityStart checkCrossUidActivitySwitchFromBelow(ActivityRecord ar,
+            int sourceUid, BlockActivityStart bas) {
         if (ar.isUid(sourceUid)) {
-            return BlockActivityStart.ACTIVITY_START_ALLOWED;
+            return bas.matchesSource();
         }
 
-        if (!blockCrossUidActivitySwitchFromBelowForActivity(ar)) {
-            return BlockActivityStart.ACTIVITY_START_ALLOWED;
+        // We don't need to check package level if activity has opted out.
+        if (ar.mAllowCrossUidActivitySwitchFromBelow) {
+            bas.mTopActivityOptedIn = false;
+            return bas.matchesSource();
         }
 
-        // At this point, we would block if the feature is launched and both apps were V+
-        // Since we have a feature flag, we need to check that too
-        // TODO(b/258792202) Replace with CompatChanges and replace Pair with boolean once feature
-        // flag is removed
-        boolean restrictActivitySwitch =
-                ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(ar.getUid())
-                        && ActivitySecurityModelFeatureFlags
-                        .shouldRestrictActivitySwitch(sourceUid);
-        if (restrictActivitySwitch) {
-            return BlockActivityStart.BLOCK;
-        } else {
-            return BlockActivityStart.LOG_ONLY;
+        if (!CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, ar.getUid())) {
+            return bas;
         }
+
+        if (ar.isUid(SYSTEM_UID)) {
+            return bas.optedIn(ar);
+        }
+
+        String packageName = ar.packageName;
+        if (packageName == null) {
+            Slog.wtf(TAG, "Package name: " + ar + " not found.");
+            return bas.optedIn(ar);
+        }
+
+        PackageManager pm = mService.mContext.getPackageManager();
+        ApplicationInfo applicationInfo;
+
+        try {
+            applicationInfo = pm.getApplicationInfo(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.wtf(TAG, "Package name: " + packageName + " not found.");
+            return bas.optedIn(ar);
+        }
+
+        return applicationInfo.allowCrossUidActivitySwitchFromBelow ? bas : bas.optedIn(ar);
     }
 
     /**
@@ -1502,8 +1509,9 @@
             @Nullable ActivityRecord sourceRecord, @NonNull ActivityRecord targetRecord,
             @Nullable Task targetTask, @Nullable ActivityRecord targetTopActivity,
             int realCallingUid, @BalCode int balCode,
-            boolean blockActivityStartAndFeatureEnabled, boolean taskToFront,
-            boolean avoidMoveTaskToFront) {
+            boolean enforceBlock, boolean taskToFront,
+            boolean avoidMoveTaskToFront, boolean allowedByGracePeriod,
+            ActivityRecord activityOptedIn) {
         final String prefix = "[ASM] ";
         Function<ActivityRecord, String> recordToString = (ar) -> {
             if (ar == null) {
@@ -1519,9 +1527,16 @@
 
         StringJoiner joiner = new StringJoiner("\n");
         joiner.add(prefix + "------ Activity Security " + action + " Debug Logging Start ------");
-        joiner.add(prefix + "Block Enabled: " + blockActivityStartAndFeatureEnabled);
+        joiner.add(prefix + "Block Enabled: " + enforceBlock);
+        if (!enforceBlock) {
+            joiner.add(prefix + "Feature Flag Enabled: " + android.security
+                    .Flags.asmRestrictionsEnabled());
+            joiner.add(prefix + "Mendel Override: " + ActivitySecurityModelFeatureFlags
+                    .asmRestrictionsEnabledForAll());
+        }
         joiner.add(prefix + "ASM Version: " + ActivitySecurityModelFeatureFlags.ASM_VERSION);
         joiner.add(prefix + "System Time: " + SystemClock.uptimeMillis());
+        joiner.add(prefix + "Activity Opted In: " + recordToString.apply(activityOptedIn));
 
         boolean targetTaskMatchesSourceTask = targetTask != null
                 && sourceRecord != null && sourceRecord.getTask() == targetTask;
@@ -1561,6 +1576,7 @@
         joiner.add(prefix + "TaskToFront: " + taskToFront);
         joiner.add(prefix + "AvoidMoveToFront: " + avoidMoveTaskToFront);
         joiner.add(prefix + "BalCode: " + balCodeToString(balCode));
+        joiner.add(prefix + "Allowed By Grace Period: " + allowedByGracePeriod);
         joiner.add(prefix + "LastResumedActivity: "
                        + recordToString.apply(mService.mLastResumedActivity));
 
@@ -1588,6 +1604,44 @@
         return joiner.toString();
     }
 
+    private boolean allowedByAsmGracePeriod(int callingUid, @Nullable ActivityRecord sourceRecord,
+            @Nullable Task targetTask, @BalCode int balCode, boolean taskToFront,
+            boolean avoidMoveTaskToFront) {
+        if (balCode == BAL_ALLOW_GRACE_PERIOD) {
+            // Allow if launching into new task, and caller matches most recently finished activity
+            if (taskToFront && mTopFinishedActivity != null
+                    && mTopFinishedActivity.mUid == callingUid) {
+                return true;
+            }
+
+            // Launching into existing task - allow if matches most recently finished activity
+            // within the task.
+            // We can reach here multiple ways:
+            // 1. activity in fg fires intent (taskToFront = false, sourceRecord is available)
+            // 2. activity in bg fires intent (taskToFront = false, sourceRecord is available)
+            // 3. activity in bg fires intent with NEW_FLAG (taskToFront = true,
+            //         avoidMoveTaskToFront = true, sourceRecord is available)
+            // 4. activity in bg fires PI (taskToFront = true, avoidMoveTaskToFront = true,
+            //         sourceRecord is not available, targetTask may be available)
+            if (!taskToFront || avoidMoveTaskToFront) {
+                if (targetTask != null) {
+                    FinishedActivityEntry finishedEntry =
+                            mTaskIdToFinishedActivity.get(targetTask.mTaskId);
+                    if (finishedEntry != null && finishedEntry.mUid == callingUid) {
+                        return true;
+                    }
+                }
+
+                if (sourceRecord != null) {
+                    FinishedActivityEntry finishedEntry =
+                            mTaskIdToFinishedActivity.get(sourceRecord.getTask().mTaskId);
+                    return finishedEntry != null && finishedEntry.mUid == callingUid;
+                }
+            }
+        }
+        return false;
+    }
+
     private static boolean isSystemExemptFlagEnabled() {
         return DeviceConfig.getBoolean(
                 NAMESPACE_WINDOW_MANAGER,
@@ -1698,55 +1752,22 @@
         }
     }
 
-    /**
-     * Activity level allowCrossUidActivitySwitchFromBelow defaults to false.
-     * Package level defaults to true.
-     * We block the launch if dev has explicitly set package level to false, and activity level has
-     * not opted out
-     */
-    private boolean blockCrossUidActivitySwitchFromBelowForActivity(@NonNull ActivityRecord ar) {
-        // We don't need to check package level if activity has opted out.
-        if (ar.mAllowCrossUidActivitySwitchFromBelow) {
-            return false;
-        }
-
-        if (ActivitySecurityModelFeatureFlags.asmRestrictionsEnabledForAll()) {
-            return true;
-        }
-
-        String packageName = ar.packageName;
-        if (packageName == null) {
-            return false;
-        }
-
-        PackageManager pm = mService.mContext.getPackageManager();
-        ApplicationInfo applicationInfo;
-
-        try {
-            applicationInfo = pm.getApplicationInfo(packageName, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.wtf(TAG, "Package name: " + packageName + " not found.");
-            return false;
-        }
-
-        return !applicationInfo.allowCrossUidActivitySwitchFromBelow;
-    }
-
     private static class BlockActivityStart {
-        private static final BlockActivityStart ACTIVITY_START_ALLOWED =
-                new BlockActivityStart(false, false);
-        private static final BlockActivityStart LOG_ONLY = new BlockActivityStart(false, true);
-        private static final BlockActivityStart BLOCK = new BlockActivityStart(true, true);
-        // We should block if feature flag is enabled
-        private final boolean mBlockActivityStartIfFlagEnabled;
-        // Used for logging/toasts. Would we block if target sdk was V and feature was
-        // enabled?
-        private final boolean mWouldBlockActivityStartIgnoringFlag;
+        private boolean mTopActivityOptedIn;
+        private boolean mTopActivityMatchesSource;
+        private ActivityRecord mActivityOptedIn;
 
-        private BlockActivityStart(boolean shouldBlockActivityStart,
-                boolean wouldBlockActivityStartIgnoringFlags) {
-            this.mBlockActivityStartIfFlagEnabled = shouldBlockActivityStart;
-            this.mWouldBlockActivityStartIgnoringFlag = wouldBlockActivityStartIgnoringFlags;
+        BlockActivityStart optedIn(ActivityRecord activity) {
+            mTopActivityOptedIn = true;
+            if (mActivityOptedIn == null) {
+                mActivityOptedIn = activity;
+            }
+            return this;
+        }
+
+        BlockActivityStart matchesSource() {
+            mTopActivityMatchesSource = true;
+            return this;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c93cc07..7e061298 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6706,7 +6706,7 @@
 
     private void dumpLogStatus(PrintWriter pw) {
         pw.println("WINDOW MANAGER LOGGING (dumpsys window logging)");
-        if (android.tracing.Flags.perfettoProtolog()) {
+        if (android.tracing.Flags.perfettoProtologTracing()) {
             pw.println("Deprecated legacy command. Use Perfetto commands instead.");
             return;
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a7a28c2..18ac0e7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2041,6 +2041,13 @@
         if (!isVisible()) {
             return;
         }
+        final WallpaperWindowToken wallpaperToken = mToken.asWallpaperToken();
+        if (wallpaperToken != null) {
+            if (wallpaperToken.hasVisibleNotDrawnWallpaper()) {
+                outWaitingForDrawn.add(this);
+            }
+            return;
+        }
         if (mActivityRecord != null) {
             if (!mActivityRecord.isVisibleRequested()) return;
             if (mActivityRecord.allDrawn) {
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 424d504..6d5fc80 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -155,13 +155,13 @@
             logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
             writeTraceToFileLocked();
             logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
-            if (!android.tracing.Flags.perfettoProtolog()) {
+            if (!android.tracing.Flags.perfettoProtologTracing()) {
                 ((LegacyProtoLogImpl) mProtoLog).stopProtoLog(pw, true);
             }
             logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
             mBuffer.resetBuffer();
             mEnabled = mEnabledLockFree = true;
-            if (!android.tracing.Flags.perfettoProtolog()) {
+            if (!android.tracing.Flags.perfettoProtologTracing()) {
                 ((LegacyProtoLogImpl) mProtoLog).startProtoLog(pw);
             }
         }
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index b9d89c2..28889de 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -347,7 +347,8 @@
     }
 
     fun isAppOpGranted(flags: Int): Boolean =
-        isPermissionGranted(flags) && !flags.hasBits(APP_OP_REVOKED)
+        isPermissionGranted(flags) && !flags.hasBits(RESTRICTION_REVOKED) &&
+            !flags.hasBits(APP_OP_REVOKED)
 
     fun toApiFlags(flags: Int): Int {
         var apiFlags = 0
diff --git a/services/tests/VpnTests/Android.bp b/services/tests/VpnTests/Android.bp
new file mode 100644
index 0000000..6ad27fc
--- /dev/null
+++ b/services/tests/VpnTests/Android.bp
@@ -0,0 +1,22 @@
+//########################################################################
+// Build FrameworksVpnTests package
+//########################################################################
+package {
+    default_team: "trendy_team_fwk_core_networking",
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "Android-Apache-2.0"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "FrameworksVpnTests",
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.kt",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/services/tests/VpnTests/AndroidManifest.xml b/services/tests/VpnTests/AndroidManifest.xml
new file mode 100644
index 0000000..d884084
--- /dev/null
+++ b/services/tests/VpnTests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.frameworks.tests.vpn">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.frameworks.tests.vpn"
+        android:label="Frameworks VPN Tests" />
+</manifest>
\ No newline at end of file
diff --git a/services/tests/VpnTests/AndroidTest.xml b/services/tests/VpnTests/AndroidTest.xml
new file mode 100644
index 0000000..ebeeac7
--- /dev/null
+++ b/services/tests/VpnTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+<configuration description="Runs VPN Tests.">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="FrameworksVpnTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="FrameworksVpnTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.tests.vpn" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/services/tests/VpnTests/OWNERS b/services/tests/VpnTests/OWNERS
new file mode 100644
index 0000000..45ea251
--- /dev/null
+++ b/services/tests/VpnTests/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index acddc9d..fcee70f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1694,6 +1694,39 @@
                 /* ignoreAnimationLimits= */ anyBoolean());
     }
 
+    @Test
+    public void testInitialDozeBrightness_AbcIsNull() {
+        when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
+        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true,
+                /* isAutoBrightnessAvailable= */ false);
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+        float brightness = 0.277f;
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.automaticBrightnessController
+                .getAutomaticScreenBrightnessBasedOnLastObservedLux(any(BrightnessEvent.class)))
+                .thenReturn(brightness);
+        when(mHolder.hbmController.getCurrentBrightnessMax())
+                .thenReturn(PowerManager.BRIGHTNESS_MAX);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        // Automatic Brightness Controller is null so no initial doze brightness should be set and
+        // we should not crash
+        verify(mHolder.animator, never()).animateTo(eq(brightness),
+                /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+                /* ignoreAnimationLimits= */ anyBoolean());
+    }
+
     /**
      * Creates a mock and registers it to {@link LocalServices}.
      */
@@ -1778,6 +1811,12 @@
 
     private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
             String uniqueId, boolean isEnabled) {
+        return createDisplayPowerController(displayId, uniqueId, isEnabled,
+                /* isAutoBrightnessAvailable= */ true);
+    }
+
+    private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
+            String uniqueId, boolean isEnabled, boolean isAutoBrightnessAvailable) {
         final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
         final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
         final AutomaticBrightnessController automaticBrightnessController =
@@ -1812,6 +1851,7 @@
         final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class);
 
         setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
+        when(config.isAutoBrightnessAvailable()).thenReturn(isAutoBrightnessAvailable);
 
         final DisplayPowerController dpc = new DisplayPowerController(
                 mContext, injector, mDisplayPowerCallbacksMock, mHandler,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
index 060f99b..397d77c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display.brightness;
 
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
 
 import static org.junit.Assert.assertEquals;
@@ -43,6 +45,7 @@
                 getReason(BrightnessReason.REASON_DOZE, BrightnessReason.MODIFIER_LOW_POWER));
         mBrightnessEvent.setPhysicalDisplayId("test");
         mBrightnessEvent.setDisplayState(Display.STATE_ON);
+        mBrightnessEvent.setDisplayPolicy(POLICY_BRIGHT);
         mBrightnessEvent.setLux(100.0f);
         mBrightnessEvent.setPreThresholdLux(150.0f);
         mBrightnessEvent.setTime(System.currentTimeMillis());
@@ -74,11 +77,12 @@
     public void testToStringWorksAsExpected() {
         String actualString = mBrightnessEvent.toString(false);
         String expectedString =
-                "BrightnessEvent: disp=1, physDisp=test, displayState=ON, brt=0.6, initBrt=25.0,"
-                + " rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150.0, hbmMax=0.62, hbmMode=off,"
-                + " rbcStrength=-1, thrmMax=0.65, powerFactor=0.2, wasShortTermModelActive=true,"
-                + " flags=, reason=doze [ low_pwr ], autoBrightness=true, strategy="
-                        + DISPLAY_BRIGHTNESS_STRATEGY_NAME + ", autoBrightnessMode=idle";
+                "BrightnessEvent: disp=1, physDisp=test, displayState=ON, displayPolicy=BRIGHT,"
+                + " brt=0.6, initBrt=25.0, rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150.0,"
+                + " hbmMax=0.62, hbmMode=off, rbcStrength=-1, thrmMax=0.65, powerFactor=0.2,"
+                + " wasShortTermModelActive=true, flags=, reason=doze [ low_pwr ],"
+                + " autoBrightness=true, strategy=" + DISPLAY_BRIGHTNESS_STRATEGY_NAME
+                + ", autoBrightnessMode=idle";
         assertEquals(expectedString, actualString);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
index 01159b1..bcb4877 100644
--- a/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
@@ -32,7 +32,6 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.server.companion.PackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index e59b5ea..2ba3969 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -48,7 +48,6 @@
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
-import android.security.KeyStore;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -102,7 +101,6 @@
     IActivityManager mActivityManager;
     DevicePolicyManager mDevicePolicyManager;
     DevicePolicyManagerInternal mDevicePolicyManagerInternal;
-    KeyStore mKeyStore;
     MockSyntheticPasswordManager mSpManager;
     IAuthSecret mAuthSecretService;
     WindowManagerInternal mMockWindowManager;
@@ -165,7 +163,6 @@
                 new LockSettingsServiceTestable.MockInjector(
                         mContext,
                         mStorage,
-                        mKeyStore,
                         mActivityManager,
                         setUpStorageManagerMock(),
                         mSpManager,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 296d2cb..f9077c4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -30,7 +30,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.storage.IStorageManager;
-import android.security.KeyStore;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.service.gatekeeper.IGateKeeperService;
 
@@ -41,6 +40,7 @@
 import com.android.server.pm.UserManagerInternal;
 
 import java.io.FileNotFoundException;
+import java.security.KeyStore;
 
 public class LockSettingsServiceTestable extends LockSettingsService {
     private Intent mSavedFrpNotificationIntent = null;
@@ -50,7 +50,6 @@
     public static class MockInjector extends LockSettingsService.Injector {
 
         private LockSettingsStorage mLockSettingsStorage;
-        private KeyStore mKeyStore;
         private IActivityManager mActivityManager;
         private IStorageManager mStorageManager;
         private SyntheticPasswordManager mSpManager;
@@ -62,14 +61,13 @@
         public boolean mIsHeadlessSystemUserMode = false;
         public boolean mIsMainUserPermanentAdmin = false;
 
-        public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
-                IActivityManager activityManager,
-                IStorageManager storageManager, SyntheticPasswordManager spManager,
-                FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+        public MockInjector(Context context, LockSettingsStorage storage,
+                IActivityManager activityManager, IStorageManager storageManager,
+                SyntheticPasswordManager spManager, FakeGsiService gsiService,
+                RecoverableKeyStoreManager recoverableKeyStoreManager,
                 UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
             super(context);
             mLockSettingsStorage = storage;
-            mKeyStore = keyStore;
             mActivityManager = activityManager;
             mStorageManager = storageManager;
             mSpManager = spManager;
@@ -110,11 +108,6 @@
         }
 
         @Override
-        public KeyStore getKeyStore() {
-            return mKeyStore;
-        }
-
-        @Override
         public IStorageManager getStorageManager() {
             return mStorageManager;
         }
@@ -145,8 +138,7 @@
         }
 
         @Override
-        public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
-                java.security.KeyStore ks) {
+        public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(KeyStore ks) {
             return mock(UnifiedProfilePasswordCache.class);
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 67c528c..09e7b91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -527,7 +527,8 @@
 
         // The configuration change is still sent to the activity, even if it doesn't relaunch.
         final ActivityConfigurationChangeItem expected =
-                ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
+                ActivityConfigurationChangeItem.obtain(activity.token, newConfig,
+                        activity.getActivityWindowInfo());
         verify(mClientLifecycleManager).scheduleTransactionItem(
                 eq(activity.app.getThread()), eq(expected));
     }
@@ -599,7 +600,8 @@
         final Configuration currentConfig = activity.getConfiguration();
         assertEquals(expectedOrientation, currentConfig.orientation);
         final ActivityConfigurationChangeItem expected =
-                ActivityConfigurationChangeItem.obtain(activity.token, currentConfig);
+                ActivityConfigurationChangeItem.obtain(activity.token, currentConfig,
+                        activity.getActivityWindowInfo());
         verify(mClientLifecycleManager).scheduleTransactionItem(activity.app.getThread(), expected);
         verify(displayRotation).onSetRequestedOrientation();
     }
@@ -818,7 +820,7 @@
 
             final ActivityConfigurationChangeItem expected =
                     ActivityConfigurationChangeItem.obtain(activity.token,
-                            activity.getConfiguration());
+                            activity.getConfiguration(), activity.getActivityWindowInfo());
             verify(mClientLifecycleManager).scheduleTransactionItem(
                     activity.app.getThread(), expected);
         } finally {
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 530a39e..0f2c62d 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -53,11 +53,14 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.usb.UsbServiceDumpProto;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -147,6 +150,7 @@
     private final UsbSettingsManager mSettingsManager;
     private final UsbPermissionManager mPermissionManager;
 
+    static final int PACKAGE_MONITOR_OPERATION_ID = 1;
     /**
      * The user id of the current user. There might be several profiles (with separate user ids)
      * per user.
@@ -156,6 +160,10 @@
 
     private final Object mLock = new Object();
 
+    // Key: USB port id
+    // Value: A set of UIDs of requesters who request disabling usb data
+    private final ArrayMap<String, ArraySet<Integer>> mUsbDisableRequesters = new ArrayMap<>();
+
     /**
      * @return the {@link UsbUserSettingsManager} for the given userId
      */
@@ -261,6 +269,10 @@
         if (mDeviceManager != null) {
             mDeviceManager.bootCompleted();
         }
+        if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) {
+            new PackageUninstallMonitor()
+                    .register(mContext, UserHandle.ALL, BackgroundThread.getHandler());
+        }
     }
 
     /** Called when a user is unlocked. */
@@ -873,6 +885,11 @@
         Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:"
                 + operationId);
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+        if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) {
+            if (!shouldUpdateUsbSignaling(portId, enable, Binder.getCallingUid())) return false;
+        }
+
         final long ident = Binder.clearCallingIdentity();
         boolean wait;
         try {
@@ -892,6 +909,31 @@
         return wait;
     }
 
+    /**
+     * If enable = true, exclude UID from update list.
+     * If enable = false, include UID in update list.
+     * Return false if enable = true and the list is empty (no updates).
+     * Return true otherwise (let downstream decide on updates).
+     */
+    private boolean shouldUpdateUsbSignaling(String portId, boolean enable, int uid) {
+        synchronized (mUsbDisableRequesters) {
+            if (!mUsbDisableRequesters.containsKey(portId)) {
+                mUsbDisableRequesters.put(portId, new ArraySet<>());
+            }
+
+            ArraySet<Integer> uidsOfDisableRequesters = mUsbDisableRequesters.get(portId);
+
+            if (enable) {
+                uidsOfDisableRequesters.remove(uid);
+                // re-enable USB port (return true) if there are no other disable requesters
+                return uidsOfDisableRequesters.isEmpty();
+            } else {
+                uidsOfDisableRequesters.add(uid);
+            }
+        }
+        return true;
+    }
+
     @Override
     public void enableUsbDataWhileDocked(String portId, int operationId,
             IUsbOperationInternal callback) {
@@ -1344,4 +1386,26 @@
     private static String removeLastChar(String value) {
         return value.substring(0, value.length() - 1);
     }
+
+    /**
+     * Upon app removal, clear associated UIDs from the mUsbDisableRequesters list
+     * and re-enable USB data signaling if no remaining apps require USB disabling.
+     */
+    private class PackageUninstallMonitor extends PackageMonitor {
+        @Override
+        public void onUidRemoved(int uid) {
+            synchronized (mUsbDisableRequesters) {
+                for (String portId : mUsbDisableRequesters.keySet()) {
+                    ArraySet<Integer> disabledUid = mUsbDisableRequesters.get(portId);
+                    if (disabledUid != null) {
+                        disabledUid.remove(uid);
+                        if (disabledUid.isEmpty()) {
+                            enableUsbData(portId, true, PACKAGE_MONITOR_OPERATION_ID,
+                                    new IUsbOperationInternal.Default());
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index aef7158..1e1dd00 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -181,10 +181,8 @@
                 LocalServices.getService(ActivityManagerInternal.class));
         mAtmInternal = Objects.requireNonNull(
                 LocalServices.getService(ActivityTaskManagerInternal.class));
-        mWmInternal = Objects.requireNonNull(
-                LocalServices.getService(WindowManagerInternal.class));
-        mDpmInternal = Objects.requireNonNull(
-                LocalServices.getService(DevicePolicyManagerInternal.class));
+        mWmInternal = LocalServices.getService(WindowManagerInternal.class);
+        mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
         LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
                 LegacyPermissionManagerInternal.class);
         permissionManagerInternal.setVoiceInteractionPackagesProvider(
@@ -2750,11 +2748,17 @@
                 if (isAssistDataAllowed) {
                     visiblePackageNames.add(record.getComponentName().getPackageName());
                 }
-                if (mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
+                if (mDpmInternal != null
+                        && mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
                     isManagedProfileVisible = true;
                 }
             }
-            final ScreenCapture.ScreenshotHardwareBuffer shb = mWmInternal.takeAssistScreenshot();
+            final ScreenCapture.ScreenshotHardwareBuffer shb;
+            if (mWmInternal != null) {
+                shb = mWmInternal.takeAssistScreenshot();
+            } else {
+                shb = null;
+            }
             final Bitmap bm = shb != null ? shb.asBitmap() : null;
             // Now that everything is fetched, putting it in the launchIntent.
             if (bm != null) {
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 82ed340..58488d1 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -573,7 +573,7 @@
      * Returns the number of this subscription.
      *
      * Starting with API level 30, returns the number of this subscription if the calling app meets
-     * one of the following requirements:
+     * at least one of the following requirements:
      * <ul>
      *     <li>If the calling app's target SDK is API level 29 or lower and the app has been granted
      *     the READ_PHONE_STATE permission.
@@ -584,8 +584,8 @@
      *     <li>If the calling app is the default SMS role holder.
      * </ul>
      *
-     * @return the number of this subscription, or an empty string if one of these requirements is
-     * not met
+     * @return the number of this subscription, or an empty string if none of the requirements
+     * are met.
      * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
      *             {@link #getSubscriptionId() subscription ID}.
      */
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 9789082..3f02ae9 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -549,13 +549,19 @@
         public static final int CAPABILITY_TYPE_SMS = 1 << 3;
 
         /**
-         * This MmTelFeature supports Call Composer (section 2.4 of RC.20)
+         * This MmTelFeature supports Call Composer (section 2.4 of RC.20). This is the superset
+         * Call Composer, meaning that all subset types of Call Composers must be enabled when this
+         * capability is enabled
          */
         public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
 
 
         /**
-         * This MmTelFeature supports Business-only Call Composer
+         * This MmTelFeature supports Business-only Call Composer. This is a subset of
+         * {@code CAPABILITY_TYPE_CALL_COMPOSER} that only supports business related
+         * information for calling (e.g. information to signal if the call is a business call) in
+         * the SIP header.  When enabling {@code CAPABILITY_TYPE_CALL_COMPOSER}, the
+         * {@code CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY} capability must also be enabled.
          */
         @FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER)
         public static final int CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY = 1 << 5;
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
index b9f1738..a963890 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
@@ -37,7 +37,7 @@
 public class PerfettoDataSourceTest {
     @Before
     public void before() {
-        assumeTrue(android.tracing.Flags.perfettoProtolog());
+        assumeTrue(android.tracing.Flags.perfettoProtologTracing());
     }
 
     @Test
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index cee29dc..1dec6ab 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -46,6 +46,7 @@
 class TestWithGoldenOutput(unittest.TestCase):
 
     # Test to check the generated jar files to the golden output.
+    @unittest.skip("Disabled until JDK 21 is merged and the golden files updated")
     def test_compare_to_golden(self):
         files = os.listdir(GOLDEN_DIR)
         files.sort()
diff --git a/tools/protologtool/src/com/android/protolog/tool/Constants.kt b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
index aa3e00f..4a93de9 100644
--- a/tools/protologtool/src/com/android/protolog/tool/Constants.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
@@ -18,7 +18,7 @@
 
 object Constants {
         const val NAME = "protologtool"
-        const val VERSION = "1.0.0"
+        const val VERSION = "2.0.0"
         const val IS_ENABLED_METHOD = "isEnabled"
         const val ENUM_VALUES_METHOD = "values"
 }