Merge "[People Service] Prevent system_server crashes due to Content Provider issues in People Service" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 9c766e9..0080a8d 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -81,6 +81,7 @@
         "android.view.inputmethod.flags-aconfig-java",
         "android.webkit.flags-aconfig-java",
         "android.widget.flags-aconfig-java",
+        "android.xr.flags-aconfig-java",
         "art_exported_aconfig_flags_lib",
         "backstage_power_flags_lib",
         "backup_flags_lib",
@@ -899,6 +900,20 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// XR
+aconfig_declarations {
+    name: "android.xr.flags-aconfig",
+    package: "android.xr",
+    container: "system",
+    srcs: ["core/java/android/content/pm/xr.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.xr.flags-aconfig-java",
+    aconfig_declarations: "android.xr.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // android.app
 aconfig_declarations {
     name: "android.app.flags-aconfig",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 03a3a0d..46cc3f0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -498,14 +498,6 @@
 
     private long mEJGracePeriodTopAppMs = QcConstants.DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
 
-    private long mQuotaBumpAdditionalDurationMs =
-            QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS;
-    private int mQuotaBumpAdditionalJobCount = QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT;
-    private int mQuotaBumpAdditionalSessionCount =
-            QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT;
-    private long mQuotaBumpWindowSizeMs = QcConstants.DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS;
-    private int mQuotaBumpLimit = QcConstants.DEFAULT_QUOTA_BUMP_LIMIT;
-
     /**
      * List of system apps with the {@link android.Manifest.permission#INSTALL_PACKAGES} permission
      * granted for each user.
@@ -1095,7 +1087,7 @@
         // essentially run until they reach the maximum limit.
         if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
             return calculateTimeUntilQuotaConsumedLocked(
-                    events, startMaxElapsed, maxExecutionTimeRemainingMs, false);
+                    events, startMaxElapsed, maxExecutionTimeRemainingMs);
         }
 
         // Need to check both max time and period time in case one is less than the other.
@@ -1104,9 +1096,9 @@
         // bucket value.
         return Math.min(
                 calculateTimeUntilQuotaConsumedLocked(
-                        events, startMaxElapsed, maxExecutionTimeRemainingMs, false),
+                        events, startMaxElapsed, maxExecutionTimeRemainingMs),
                 calculateTimeUntilQuotaConsumedLocked(
-                        events, startWindowElapsed, allowedTimeRemainingMs, true));
+                        events, startWindowElapsed, allowedTimeRemainingMs));
     }
 
     /**
@@ -1116,36 +1108,12 @@
      * @param deadSpaceMs        How much time can be allowed to count towards the quota
      */
     private long calculateTimeUntilQuotaConsumedLocked(@NonNull List<TimedEvent> sessions,
-            final long windowStartElapsed, long deadSpaceMs, boolean allowQuotaBumps) {
+            final long windowStartElapsed, long deadSpaceMs) {
         long timeUntilQuotaConsumedMs = 0;
         long start = windowStartElapsed;
-        int numQuotaBumps = 0;
-        final long quotaBumpWindowStartElapsed =
-                sElapsedRealtimeClock.millis() - mQuotaBumpWindowSizeMs;
         final int numSessions = sessions.size();
-        if (allowQuotaBumps) {
-            for (int i = numSessions - 1; i >= 0; --i) {
-                TimedEvent event = sessions.get(i);
-
-                if (event instanceof QuotaBump) {
-                    if (event.getEndTimeElapsed() >= quotaBumpWindowStartElapsed
-                            && numQuotaBumps++ < mQuotaBumpLimit) {
-                        deadSpaceMs += mQuotaBumpAdditionalDurationMs;
-                    } else {
-                        break;
-                    }
-                }
-            }
-        }
         for (int i = 0; i < numSessions; ++i) {
-            TimedEvent event = sessions.get(i);
-
-            if (event instanceof QuotaBump) {
-                continue;
-            }
-
-            TimingSession session = (TimingSession) event;
-
+            TimingSession session = (TimingSession) sessions.get(i);
             if (session.endTimeElapsed < windowStartElapsed) {
                 // Outside of window. Ignore.
                 continue;
@@ -1330,41 +1298,15 @@
         final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
         final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
         int sessionCountInWindow = 0;
-        int numQuotaBumps = 0;
-        final long quotaBumpWindowStartElapsed = nowElapsed - mQuotaBumpWindowSizeMs;
         // The minimum time between the start time and the beginning of the events that were
         // looked at --> how much time the stats will be valid for.
         long emptyTimeMs = Long.MAX_VALUE;
         // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
         // the most recent ones.
         final int loopStart = events.size() - 1;
-        // Process QuotaBumps first to ensure the limits are properly adjusted.
-        for (int i = loopStart; i >= 0; --i) {
-            TimedEvent event = events.get(i);
-
-            if (event.getEndTimeElapsed() < quotaBumpWindowStartElapsed
-                    || numQuotaBumps >= mQuotaBumpLimit) {
-                break;
-            }
-
-            if (event instanceof QuotaBump) {
-                stats.allowedTimePerPeriodMs += mQuotaBumpAdditionalDurationMs;
-                stats.jobCountLimit += mQuotaBumpAdditionalJobCount;
-                stats.sessionCountLimit += mQuotaBumpAdditionalSessionCount;
-                emptyTimeMs = Math.min(emptyTimeMs,
-                        event.getEndTimeElapsed() - quotaBumpWindowStartElapsed);
-                numQuotaBumps++;
-            }
-        }
         TimingSession lastSeenTimingSession = null;
         for (int i = loopStart; i >= 0; --i) {
-            TimedEvent event = events.get(i);
-
-            if (event instanceof QuotaBump) {
-                continue;
-            }
-
-            TimingSession session = (TimingSession) event;
+            TimingSession session = (TimingSession) events.get(i);
 
             // Window management.
             if (startWindowElapsed < session.endTimeElapsed) {
@@ -2058,28 +2000,6 @@
     }
 
     @VisibleForTesting
-    static final class QuotaBump implements TimedEvent {
-        // Event timestamp in elapsed realtime timebase.
-        public final long eventTimeElapsed;
-
-        QuotaBump(long eventElapsed) {
-            this.eventTimeElapsed = eventElapsed;
-        }
-
-        @Override
-        public long getEndTimeElapsed() {
-            return eventTimeElapsed;
-        }
-
-        @Override
-        public void dump(IndentingPrintWriter pw) {
-            pw.print("Quota bump @ ");
-            pw.print(eventTimeElapsed);
-            pw.println();
-        }
-    }
-
-    @VisibleForTesting
     static final class ShrinkableDebits {
         /** The amount of quota remaining. Can be negative if limit changes. */
         private long mDebitTally;
@@ -2528,21 +2448,6 @@
                 updateStandbyBucket(userId, packageName, bucketIndex);
             });
         }
-
-        @Override
-        public void triggerTemporaryQuotaBump(String packageName, @UserIdInt int userId) {
-            synchronized (mLock) {
-                List<TimedEvent> events = mTimingEvents.get(userId, packageName);
-                if (events == null || events.size() == 0) {
-                    // If the app hasn't run any jobs, there's no point giving it a quota bump.
-                    return;
-                }
-                events.add(new QuotaBump(sElapsedRealtimeClock.millis()));
-                invalidateAllExecutionStatsLocked(userId, packageName);
-            }
-            // Update jobs out of band.
-            mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
-        }
     }
 
     @VisibleForTesting
@@ -3019,7 +2924,6 @@
         mQcConstants.mRateLimitingConstantsUpdated = false;
         mQcConstants.mExecutionPeriodConstantsUpdated = false;
         mQcConstants.mEJLimitConstantsUpdated = false;
-        mQcConstants.mQuotaBumpConstantsUpdated = false;
     }
 
     @Override
@@ -3046,7 +2950,6 @@
         private boolean mRateLimitingConstantsUpdated = false;
         private boolean mExecutionPeriodConstantsUpdated = false;
         private boolean mEJLimitConstantsUpdated = false;
-        private boolean mQuotaBumpConstantsUpdated = false;
 
         /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
         private static final String QC_CONSTANT_PREFIX = "qc_";
@@ -3194,21 +3097,6 @@
         @VisibleForTesting
         static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
                 QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS =
-                QC_CONSTANT_PREFIX + "quota_bump_additional_duration_ms";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT =
-                QC_CONSTANT_PREFIX + "quota_bump_additional_job_count";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT =
-                QC_CONSTANT_PREFIX + "quota_bump_additional_session_count";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_WINDOW_SIZE_MS =
-                QC_CONSTANT_PREFIX + "quota_bump_window_size_ms";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_LIMIT =
-                QC_CONSTANT_PREFIX + "quota_bump_limit";
 
         private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
                 10 * 60 * 1000L; // 10 minutes
@@ -3281,11 +3169,6 @@
         private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0;
         private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS;
-        private static final long DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS = 1 * MINUTE_IN_MILLIS;
-        private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = 2;
-        private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = 1;
-        private static final long DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS = 8 * HOUR_IN_MILLIS;
-        private static final int DEFAULT_QUOTA_BUMP_LIMIT = 8;
 
         /**
          * How much time each app in the exempted bucket will have to run jobs within their standby
@@ -3587,33 +3470,6 @@
          */
         public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
 
-        /**
-         * How much additional session duration to give an app for each accepted quota bump.
-         */
-        public long QUOTA_BUMP_ADDITIONAL_DURATION_MS = DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS;
-
-        /**
-         * How many additional regular jobs to give an app for each accepted quota bump.
-         */
-        public int QUOTA_BUMP_ADDITIONAL_JOB_COUNT = DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT;
-
-        /**
-         * How many additional sessions to give an app for each accepted quota bump.
-         */
-        public int QUOTA_BUMP_ADDITIONAL_SESSION_COUNT =
-                DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT;
-
-        /**
-         * The rolling window size within which to accept and apply quota bump events.
-         */
-        public long QUOTA_BUMP_WINDOW_SIZE_MS = DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS;
-
-        /**
-         * The maximum number of quota bumps to accept and apply within the
-         * {@link #QUOTA_BUMP_WINDOW_SIZE_MS window}.
-         */
-        public int QUOTA_BUMP_LIMIT = DEFAULT_QUOTA_BUMP_LIMIT;
-
         public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
                 @NonNull String key) {
             switch (key) {
@@ -3650,14 +3506,6 @@
                     updateEJLimitConstantsLocked();
                     break;
 
-                case KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS:
-                case KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT:
-                case KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT:
-                case KEY_QUOTA_BUMP_WINDOW_SIZE_MS:
-                case KEY_QUOTA_BUMP_LIMIT:
-                    updateQuotaBumpConstantsLocked();
-                    break;
-
                 case KEY_MAX_JOB_COUNT_EXEMPTED:
                     MAX_JOB_COUNT_EXEMPTED = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_EXEMPTED);
                     int newExemptedMaxJobCount =
@@ -4156,65 +4004,6 @@
             }
         }
 
-        private void updateQuotaBumpConstantsLocked() {
-            if (mQuotaBumpConstantsUpdated) {
-                return;
-            }
-            mQuotaBumpConstantsUpdated = true;
-
-            // Query the values as an atomic set.
-            final DeviceConfig.Properties properties = DeviceConfig.getProperties(
-                    DeviceConfig.NAMESPACE_JOB_SCHEDULER,
-                    KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
-                    KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
-                    KEY_QUOTA_BUMP_WINDOW_SIZE_MS, KEY_QUOTA_BUMP_LIMIT);
-            QUOTA_BUMP_ADDITIONAL_DURATION_MS = properties.getLong(
-                    KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
-                    DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS);
-            QUOTA_BUMP_ADDITIONAL_JOB_COUNT = properties.getInt(
-                    KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT);
-            QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = properties.getInt(
-                    KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
-                    DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT);
-            QUOTA_BUMP_WINDOW_SIZE_MS = properties.getLong(
-                    KEY_QUOTA_BUMP_WINDOW_SIZE_MS, DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS);
-            QUOTA_BUMP_LIMIT = properties.getInt(
-                    KEY_QUOTA_BUMP_LIMIT, DEFAULT_QUOTA_BUMP_LIMIT);
-
-            // The window must be in the range [1 hour, 24 hours].
-            long newWindowSizeMs = Math.max(HOUR_IN_MILLIS,
-                    Math.min(MAX_PERIOD_MS, QUOTA_BUMP_WINDOW_SIZE_MS));
-            if (mQuotaBumpWindowSizeMs != newWindowSizeMs) {
-                mQuotaBumpWindowSizeMs = newWindowSizeMs;
-                mShouldReevaluateConstraints = true;
-            }
-            // The limit must be nonnegative.
-            int newLimit = Math.max(0, QUOTA_BUMP_LIMIT);
-            if (mQuotaBumpLimit != newLimit) {
-                mQuotaBumpLimit = newLimit;
-                mShouldReevaluateConstraints = true;
-            }
-            // The job count must be nonnegative.
-            int newJobAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_JOB_COUNT);
-            if (mQuotaBumpAdditionalJobCount != newJobAddition) {
-                mQuotaBumpAdditionalJobCount = newJobAddition;
-                mShouldReevaluateConstraints = true;
-            }
-            // The session count must be nonnegative.
-            int newSessionAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_SESSION_COUNT);
-            if (mQuotaBumpAdditionalSessionCount != newSessionAddition) {
-                mQuotaBumpAdditionalSessionCount = newSessionAddition;
-                mShouldReevaluateConstraints = true;
-            }
-            // The additional duration must be in the range [0, 10 minutes].
-            long newAdditionalDuration = Math.max(0,
-                    Math.min(10 * MINUTE_IN_MILLIS, QUOTA_BUMP_ADDITIONAL_DURATION_MS));
-            if (mQuotaBumpAdditionalDurationMs != newAdditionalDuration) {
-                mQuotaBumpAdditionalDurationMs = newAdditionalDuration;
-                mShouldReevaluateConstraints = true;
-            }
-        }
-
         private void dump(IndentingPrintWriter pw) {
             pw.println();
             pw.println("QuotaController:");
@@ -4277,15 +4066,6 @@
                     EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS).println();
             pw.print(KEY_EJ_GRACE_PERIOD_TOP_APP_MS, EJ_GRACE_PERIOD_TOP_APP_MS).println();
 
-            pw.print(KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
-                    QUOTA_BUMP_ADDITIONAL_DURATION_MS).println();
-            pw.print(KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT,
-                    QUOTA_BUMP_ADDITIONAL_JOB_COUNT).println();
-            pw.print(KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
-                    QUOTA_BUMP_ADDITIONAL_SESSION_COUNT).println();
-            pw.print(KEY_QUOTA_BUMP_WINDOW_SIZE_MS, QUOTA_BUMP_WINDOW_SIZE_MS).println();
-            pw.print(KEY_QUOTA_BUMP_LIMIT, QUOTA_BUMP_LIMIT).println();
-
             pw.decreaseIndent();
         }
 
@@ -4503,31 +4283,6 @@
         return mQcConstants;
     }
 
-    @VisibleForTesting
-    long getQuotaBumpAdditionDurationMs() {
-        return mQuotaBumpAdditionalDurationMs;
-    }
-
-    @VisibleForTesting
-    int getQuotaBumpAdditionJobCount() {
-        return mQuotaBumpAdditionalJobCount;
-    }
-
-    @VisibleForTesting
-    int getQuotaBumpAdditionSessionCount() {
-        return mQuotaBumpAdditionalSessionCount;
-    }
-
-    @VisibleForTesting
-    int getQuotaBumpLimit() {
-        return mQuotaBumpLimit;
-    }
-
-    @VisibleForTesting
-    long getQuotaBumpWindowSizeMs() {
-        return mQuotaBumpWindowSizeMs;
-    }
-
     //////////////////////////// DATA DUMP //////////////////////////////
 
     @NeverCompile // Avoid size overhead of debugging code.
diff --git a/core/api/current.txt b/core/api/current.txt
index 6f60378..f59b575 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -56623,7 +56623,7 @@
     method public java.util.Map<android.view.inputmethod.InputMethodInfo,java.util.List<android.view.inputmethod.InputMethodSubtype>> getShortcutInputMethodsAndSubtypes();
     method @Deprecated public void hideSoftInputFromInputMethod(android.os.IBinder, int);
     method public boolean hideSoftInputFromWindow(android.os.IBinder, int);
-    method public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver);
+    method @Deprecated public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver);
     method @Deprecated public void hideStatusIcon(android.os.IBinder);
     method public void invalidateInput(@NonNull android.view.View);
     method public boolean isAcceptingText();
@@ -56647,7 +56647,7 @@
     method public void showInputMethodAndSubtypeEnabler(@Nullable String);
     method public void showInputMethodPicker();
     method public boolean showSoftInput(android.view.View, int);
-    method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
+    method @Deprecated public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
     method @Deprecated public void showSoftInputFromInputMethod(android.os.IBinder, int);
     method @Deprecated public void showStatusIcon(android.os.IBinder, String, @DrawableRes int);
     method @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public void startConnectionlessStylusHandwriting(@NonNull android.view.View, @Nullable android.view.inputmethod.CursorAnchorInfo, @NonNull java.util.concurrent.Executor, @NonNull android.view.inputmethod.ConnectionlessHandwritingCallback);
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 3c7c0d6..a3cab29 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -82,12 +82,12 @@
 package android.graphics {
 
   @Deprecated public class AvoidXfermode extends android.graphics.Xfermode {
-    ctor public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode);
+    ctor @Deprecated public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode);
   }
 
-  public enum AvoidXfermode.Mode {
-    enum_constant public static final android.graphics.AvoidXfermode.Mode AVOID;
-    enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET;
+  @Deprecated public enum AvoidXfermode.Mode {
+    enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode AVOID;
+    enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode TARGET;
   }
 
   public class Canvas {
@@ -102,9 +102,9 @@
   }
 
   @Deprecated public class LayerRasterizer extends android.graphics.Rasterizer {
-    ctor public LayerRasterizer();
-    method public void addLayer(android.graphics.Paint, float, float);
-    method public void addLayer(android.graphics.Paint);
+    ctor @Deprecated public LayerRasterizer();
+    method @Deprecated public void addLayer(android.graphics.Paint, float, float);
+    method @Deprecated public void addLayer(android.graphics.Paint);
   }
 
   public class Paint {
@@ -118,7 +118,7 @@
   }
 
   @Deprecated public class PixelXorXfermode extends android.graphics.Xfermode {
-    ctor public PixelXorXfermode(int);
+    ctor @Deprecated public PixelXorXfermode(int);
   }
 
   public class Rasterizer {
@@ -170,14 +170,14 @@
 package android.net {
 
   @Deprecated public class NetworkBadging {
-    method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
-    field public static final int BADGING_4K = 30; // 0x1e
-    field public static final int BADGING_HD = 20; // 0x14
-    field public static final int BADGING_NONE = 0; // 0x0
-    field public static final int BADGING_SD = 10; // 0xa
+    method @Deprecated @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
+    field @Deprecated public static final int BADGING_4K = 30; // 0x1e
+    field @Deprecated public static final int BADGING_HD = 20; // 0x14
+    field @Deprecated public static final int BADGING_NONE = 0; // 0x0
+    field @Deprecated public static final int BADGING_SD = 10; // 0xa
   }
 
-  @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging {
+  @Deprecated @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging {
   }
 
   public final class Proxy {
@@ -304,14 +304,14 @@
 
   @Deprecated public static final class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
     field @Deprecated public static final String CONTENT_DIRECTORY = "stream_items";
-    field public static final String _COUNT = "_count";
-    field public static final String _ID = "_id";
+    field @Deprecated public static final String _COUNT = "_count";
+    field @Deprecated public static final String _ID = "_id";
   }
 
   @Deprecated public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
     field @Deprecated public static final String PHOTO = "photo";
-    field public static final String _COUNT = "_count";
-    field public static final String _ID = "_id";
+    field @Deprecated public static final String _COUNT = "_count";
+    field @Deprecated public static final String _ID = "_id";
   }
 
   @Deprecated protected static interface ContactsContract.StreamItemPhotosColumns {
@@ -332,16 +332,16 @@
     field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item";
     field @Deprecated public static final android.net.Uri CONTENT_URI;
     field @Deprecated public static final String MAX_ITEMS = "max_items";
-    field public static final String _COUNT = "_count";
-    field public static final String _ID = "_id";
+    field @Deprecated public static final String _COUNT = "_count";
+    field @Deprecated public static final String _ID = "_id";
   }
 
   @Deprecated public static final class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
     field @Deprecated public static final String CONTENT_DIRECTORY = "photo";
     field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo";
     field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo";
-    field public static final String _COUNT = "_count";
-    field public static final String _ID = "_id";
+    field @Deprecated public static final String _COUNT = "_count";
+    field @Deprecated public static final String _ID = "_id";
   }
 
   @Deprecated protected static interface ContactsContract.StreamItemsColumns {
@@ -447,14 +447,14 @@
 package android.util {
 
   @Deprecated public class FloatMath {
-    method public static float ceil(float);
-    method public static float cos(float);
-    method public static float exp(float);
-    method public static float floor(float);
-    method public static float hypot(float, float);
-    method public static float pow(float, float);
-    method public static float sin(float);
-    method public static float sqrt(float);
+    method @Deprecated public static float ceil(float);
+    method @Deprecated public static float cos(float);
+    method @Deprecated public static float exp(float);
+    method @Deprecated public static float floor(float);
+    method @Deprecated public static float hypot(float, float);
+    method @Deprecated public static float pow(float, float);
+    method @Deprecated public static float sin(float);
+    method @Deprecated public static float sqrt(float);
   }
 
 }
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index bbfa0ec..78b9994 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -7,8 +7,8 @@
   }
 
   @Deprecated public abstract static class AppOpsManager.AppOpsCollector extends android.app.AppOpsManager.OnOpNotedCallback {
-    ctor public AppOpsManager.AppOpsCollector();
-    method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor();
+    ctor @Deprecated public AppOpsManager.AppOpsCollector();
+    method @Deprecated @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor();
   }
 
   public class Notification implements android.os.Parcelable {
@@ -207,7 +207,7 @@
 
   @Deprecated public static interface TranslationService.OnTranslationResultCallback {
     method @Deprecated public void onError();
-    method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
+    method @Deprecated public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
   }
 
 }
@@ -261,64 +261,64 @@
   }
 
   @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable {
-    method public boolean containsKey(@NonNull String);
-    method @NonNull public android.os.PersistableBundle copyBundle();
-    method public int describeContents();
-    method public boolean getBoolean(@NonNull String, boolean);
-    method public int getInt(@NonNull String, int);
-    method @Nullable public String getString(@NonNull String);
-    method public long getVersion();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
-    field public static final String IPTYPE_IPV4 = "IPV4";
-    field public static final String IPTYPE_IPV6 = "IPV6";
-    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
-    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
-    field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
-    field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
-    field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
-    field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
-    field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
-    field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
-    field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
-    field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
-    field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
-    field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
-    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
-    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
-    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
-    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
-    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
-    field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
-    field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
-    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
-    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
-    field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
-    field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
-    field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
-    field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
-    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
-    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
-    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
-    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
-    field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
-    field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
-    field public static final String SIP_TRANSPORT_TCP = "TCP";
-    field public static final String SIP_TRANSPORT_UDP = "UDP";
+    method @Deprecated public boolean containsKey(@NonNull String);
+    method @Deprecated @NonNull public android.os.PersistableBundle copyBundle();
+    method @Deprecated public int describeContents();
+    method @Deprecated public boolean getBoolean(@NonNull String, boolean);
+    method @Deprecated public int getInt(@NonNull String, int);
+    method @Deprecated @Nullable public String getString(@NonNull String);
+    method @Deprecated public long getVersion();
+    method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
+    field @Deprecated public static final String IPTYPE_IPV4 = "IPV4";
+    field @Deprecated public static final String IPTYPE_IPV6 = "IPV6";
+    field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
+    field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP";
+    field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP";
   }
 
-  public static final class SipDelegateImsConfiguration.Builder {
-    ctor public SipDelegateImsConfiguration.Builder(int);
-    ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
-    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
-    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
-    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
-    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
+  @Deprecated public static final class SipDelegateImsConfiguration.Builder {
+    ctor @Deprecated public SipDelegateImsConfiguration.Builder(int);
+    ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
+    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
+    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
+    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
   }
 
 }
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 1265de1..d12e1bf 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -19,6 +19,7 @@
     srcs: [
         "**/*.java",
         "**/*.aidl",
+        ":systemfeatures-gen-srcs",
         ":framework-nfc-non-updatable-sources",
         ":messagequeue-gen",
         ":ranging_stack_mock_initializer",
@@ -214,6 +215,9 @@
 
 aidl_interface {
     name: "android.os.hintmanager_aidl",
+    defaults: [
+        "android.hardware.power-aidl",
+    ],
     srcs: [
         "android/os/IHintManager.aidl",
         "android/os/IHintSession.aidl",
@@ -231,9 +235,6 @@
             enabled: true,
         },
     },
-    imports: [
-        "android.hardware.power-V5",
-    ],
 }
 
 aidl_library {
@@ -665,3 +666,29 @@
 }
 
 // protolog end
+
+// Whether to enable read-only system feature codegen.
+gen_readonly_feature_apis = select(release_flag("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS"), {
+    true: "true",
+    false: "false",
+    default: "false",
+})
+
+// Generates com.android.internal.pm.RoSystemFeatures, optionally compiling in
+// details about fixed system features defined by build flags. When disabled,
+// the APIs are simply passthrough stubs with no meaningful side effects.
+genrule {
+    name: "systemfeatures-gen-srcs",
+    cmd: "$(location systemfeatures-gen-tool) com.android.internal.pm.RoSystemFeatures " +
+        // --readonly=false (default) makes the codegen an effective no-op passthrough API.
+        " --readonly=" + gen_readonly_feature_apis +
+        // For now, only export "android.hardware.type.*" system features APIs.
+        // TODO(b/203143243): Use an intermediate soong var that aggregates all declared
+        // RELEASE_SYSTEM_FEATURE_* declarations into a single arg.
+        " --feature-apis=AUTOMOTIVE,WATCH,TELEVISION,EMBEDDED,PC" +
+        " > $(out)",
+    out: [
+        "RoSystemFeatures.java",
+    ],
+    tools: ["systemfeatures-gen-tool"],
+}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index d363e19..ba71afb 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -97,6 +97,7 @@
 
 # Performance
 per-file PropertyInvalidatedCache.java = file:/PERFORMANCE_OWNERS
+per-file performance.aconfig = file:/PERFORMANCE_OWNERS
 
 # Pinner
 per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index c17da24..55296eb 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.text.TextUtils.formatSimple;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -30,11 +32,10 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FastPrintWriter;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -42,12 +43,14 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Random;
 import java.util.Set;
 import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
 
 /**
@@ -224,12 +227,24 @@
     }
 
     /**
-     * Reserved nonce values.  Use isReservedNonce() to test for a reserved value.  Note
-     * that all values cause the cache to be skipped.
+     * Reserved nonce values.  Use isReservedNonce() to test for a reserved value.  Note that all
+     * reserved values cause the cache to be skipped.
      */
+    // This is the initial value of all cache keys.  It is changed when a cache is invalidated.
     private static final int NONCE_UNSET = 0;
+    // This value is used in two ways.  First, it is used internally to indicate that the cache is
+    // disabled for the current query.  Secondly, it is used to global disable the cache across the
+    // entire system.  Once a cache is disabled, there is no way to enable it again.  The global
+    // behavior is unused and will likely be removed in the future.
     private static final int NONCE_DISABLED = 1;
+    // The cache is corked, which means that clients must act as though the cache is always
+    // invalid.  This is used when the server is processing updates that continuously invalidate
+    // caches.  Rather than issuing individual invalidations (which has a performance penalty),
+    // the server corks the caches at the start of the process and uncorks at the end of the
+    // process.
     private static final int NONCE_CORKED = 2;
+    // The cache is bypassed for the current query.  Unlike UNSET and CORKED, this value is never
+    // written to global store.
     private static final int NONCE_BYPASS = 3;
 
     private static boolean isReservedNonce(long n) {
@@ -237,7 +252,7 @@
     }
 
     /**
-     * The names of the nonces
+     * The names of the reserved nonces.
      */
     private static final String[] sNonceName =
             new String[]{ "unset", "disabled", "corked", "bypass" };
@@ -277,32 +292,17 @@
     private static final Object sCorkLock = new Object();
 
     /**
-     * Record the number of invalidate or cork calls that were nops because the cache was already
-     * corked.  This is static because invalidation is done in a static context.  Entries are
-     * indexed by the cache property.
-     */
-    @GuardedBy("sCorkLock")
-    private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>();
-
-    /**
-     * A map of cache keys that we've "corked". (The values are counts.)  When a cache key is
-     * corked, we skip the cache invalidate when the cache key is in the unset state --- that
-     * is, when a cache key is corked, an invalidation does not enable the cache if somebody
-     * else hasn't disabled it.
-     */
-    @GuardedBy("sCorkLock")
-    private static final HashMap<String, Integer> sCorks = new HashMap<>();
-
-    /**
      * A lock for the global list of caches and cache keys.  This must never be taken inside mLock
      * or sCorkLock.
      */
     private static final Object sGlobalLock = new Object();
 
     /**
-     * A map of cache keys that have been disabled in the local process.  When a key is
-     * disabled locally, existing caches are disabled and the key is saved in this map.
-     * Future cache instances that use the same key will be disabled in their constructor.
+     * A map of cache keys that have been disabled in the local process.  When a key is disabled
+     * locally, existing caches are disabled and the key is saved in this map.  Future cache
+     * instances that use the same key will be disabled in their constructor.  Note that "disabled"
+     * means the cache is not used in this process.  Invalidation still proceeds normally, because
+     * the cache may be used in other processes.
      */
     @GuardedBy("sGlobalLock")
     private static final HashSet<String> sDisabledKeys = new HashSet<>();
@@ -315,14 +315,6 @@
     private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>();
 
     /**
-     * Counts of the number of times a cache key was invalidated.  Invalidation occurs in a static
-     * context with no cache object available, so this is a static map.  Entries are indexed by
-     * the cache property.
-     */
-    @GuardedBy("sGlobalLock")
-    private static final HashMap<String, Long> sInvalidates = new HashMap<>();
-
-    /**
      * If sEnabled is false then all cache operations are stubbed out.  Set
      * it to false inside test processes.
      */
@@ -334,12 +326,6 @@
     private final String mPropertyName;
 
     /**
-     * Handle to the {@code mPropertyName} property, transitioning to non-{@code null} once the
-     * property exists on the system.
-     */
-    private volatile SystemProperties.Handle mPropertyHandle;
-
-    /**
      * The name by which this cache is known.  This should normally be the
      * binder call that is being cached, but the constructors default it to
      * the property name.
@@ -369,7 +355,13 @@
     private final LinkedHashMap<Query, Result> mCache;
 
     /**
-     * The last value of the {@code mPropertyHandle} that we observed.
+     * The nonce handler for this cache.
+     */
+    @GuardedBy("mLock")
+    private final NonceHandler mNonce;
+
+    /**
+     * The last nonce value that was observed.
      */
     @GuardedBy("mLock")
     private long mLastSeenNonce = NONCE_UNSET;
@@ -385,6 +377,297 @@
     private final int mMaxEntries;
 
     /**
+     * A class to manage cache keys.  There is a single instance of this class for each unique key
+     * that is shared by all cache instances that use that key.  This class is abstract; subclasses
+     * use different storage mechanisms for the nonces.
+     */
+    private static abstract class NonceHandler {
+        // The name of the nonce.
+        final String mName;
+
+        // A lock to synchronize corking and invalidation.
+        protected final Object mLock = new Object();
+
+        // Count the number of times the property name was invalidated.
+        @GuardedBy("mLock")
+        private int mInvalidated = 0;
+
+        // Count the number of times invalidate or cork calls were nops because the cache was
+        // already corked.
+        @GuardedBy("mLock")
+        private int mCorkedInvalidates = 0;
+
+        // Count the number of corks against this property name.  This is not a statistic.  It
+        // increases when the property is corked and decreases when the property is uncorked.
+        // Invalidation requests are ignored when the cork count is greater than zero.
+        @GuardedBy("mLock")
+        private int mCorks = 0;
+
+        // The methods to get and set a nonce from whatever storage is being used.
+        abstract long getNonce();
+        abstract void setNonce(long value);
+
+        NonceHandler(@NonNull String name) {
+            mName = name;
+        }
+
+        /**
+         * Write the invalidation nonce for the property.
+         */
+        void invalidate() {
+            if (!sEnabled) {
+                if (DEBUG) {
+                    Log.d(TAG, formatSimple("cache invalidate %s suppressed", mName));
+                }
+                return;
+            }
+
+            synchronized (mLock) {
+                if (mCorks > 0) {
+                    if (DEBUG) {
+                        Log.d(TAG, "ignoring invalidation due to cork: " + mName);
+                    }
+                    mCorkedInvalidates++;
+                    return;
+                }
+
+                final long nonce = getNonce();
+                if (nonce == NONCE_DISABLED) {
+                    if (DEBUG) {
+                        Log.d(TAG, "refusing to invalidate disabled cache: " + mName);
+                    }
+                    return;
+                }
+
+                long newValue;
+                do {
+                    newValue = NoPreloadHolder.next();
+                } while (isReservedNonce(newValue));
+                if (DEBUG) {
+                    Log.d(TAG, formatSimple(
+                        "invalidating cache [%s]: [%s] -> [%s]",
+                        mName, nonce, Long.toString(newValue)));
+                }
+                // There is a small race with concurrent disables here.  A compare-and-exchange
+                // property operation would be required to eliminate the race condition.
+                setNonce(newValue);
+                mInvalidated++;
+            }
+        }
+
+        void cork() {
+            if (!sEnabled) {
+                if (DEBUG) {
+                    Log.d(TAG, formatSimple("cache corking %s suppressed", mName));
+                }
+                return;
+            }
+
+            synchronized (mLock) {
+                int numberCorks = mCorks;
+                if (DEBUG) {
+                    Log.d(TAG, formatSimple(
+                        "corking %s: numberCorks=%s", mName, numberCorks));
+                }
+
+                // If we're the first ones to cork this cache, set the cache to the corked state so
+                // existing caches talk directly to their services while we've corked updates.
+                // Make sure we don't clobber a disabled cache value.
+
+                // TODO: we can skip this property write and leave the cache enabled if the
+                // caller promises not to make observable changes to the cache backing state before
+                // uncorking the cache, e.g., by holding a read lock across the cork-uncork pair.
+                // Implement this more dangerous mode of operation if necessary.
+                if (numberCorks == 0) {
+                    final long nonce = getNonce();
+                    if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
+                        setNonce(NONCE_CORKED);
+                    }
+                } else {
+                    mCorkedInvalidates++;
+                }
+                mCorks++;
+                if (DEBUG) {
+                    Log.d(TAG, "corked: " + mName);
+                }
+            }
+        }
+
+        void uncork() {
+            if (!sEnabled) {
+                if (DEBUG) {
+                    Log.d(TAG, formatSimple("cache uncorking %s suppressed", mName));
+                }
+                return;
+            }
+
+            synchronized (mLock) {
+                int numberCorks = --mCorks;
+                if (DEBUG) {
+                    Log.d(TAG, formatSimple(
+                        "uncorking %s: numberCorks=%s", mName, numberCorks));
+                }
+
+                if (numberCorks < 0) {
+                    throw new AssertionError("cork underflow: " + mName);
+                }
+                if (numberCorks == 0) {
+                    // The property is fully uncorked and can be invalidated normally.
+                    invalidate();
+                    if (DEBUG) {
+                        Log.d(TAG, "uncorked: " + mName);
+                    }
+                }
+            }
+        }
+
+        void disable() {
+            if (!sEnabled) {
+                return;
+            }
+            synchronized (mLock) {
+                setNonce(NONCE_DISABLED);
+            }
+        }
+
+        record Stats(int invalidated, int corkedInvalidates) {}
+        Stats getStats() {
+            synchronized (mLock) {
+                return new Stats(mInvalidated, mCorkedInvalidates);
+            }
+        }
+    }
+
+    /**
+     * Manage nonces that are stored in a system property.
+     */
+    private static final class NonceSysprop extends NonceHandler {
+        // A handle to the property, for fast lookups.
+        private volatile SystemProperties.Handle mHandle;
+
+        NonceSysprop(@NonNull String name) {
+            super(name);
+        }
+
+        @Override
+        long getNonce() {
+            if (mHandle == null) {
+                synchronized (mLock) {
+                    mHandle = SystemProperties.find(mName);
+                    if (mHandle == null) {
+                        return NONCE_UNSET;
+                    }
+                }
+            }
+            return mHandle.getLong(NONCE_UNSET);
+        }
+
+        @Override
+        void setNonce(long value) {
+            // Failing to set the nonce is a fatal error.  Failures setting a system property have
+            // been reported; given that the failure is probably transient, this function includes
+            // a retry.
+            final String str = Long.toString(value);
+            RuntimeException failure = null;
+            for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) {
+                try {
+                    SystemProperties.set(mName, str);
+                    if (attempt > 0) {
+                        // This log is not guarded.  Based on known bug reports, it should
+                        // occur once a week or less.  The purpose of the log message is to
+                        // identify the retries as a source of delay that might be otherwise
+                        // be attributed to the cache itself.
+                        Log.w(TAG, "Nonce set after " + attempt + " tries");
+                    }
+                    return;
+                } catch (RuntimeException e) {
+                    if (failure == null) {
+                        failure = e;
+                    }
+                    try {
+                        Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS);
+                    } catch (InterruptedException x) {
+                        // Ignore this exception.  The desired delay is only approximate and
+                        // there is no issue if the sleep sometimes terminates early.
+                    }
+                }
+            }
+            // This point is reached only if SystemProperties.set() fails at least once.
+            // Rethrow the first exception that was received.
+            throw failure;
+        }
+    }
+
+    /**
+     * SystemProperties and shared storage are protected and cannot be written by random
+     * processes.  So, for testing purposes, the NonceTest handler stores the nonce locally.
+     */
+    private static class NonceTest extends NonceHandler {
+        // The saved nonce.
+        private long mValue;
+
+        // If this flag is false, the handler has been shutdown during a test.  Access to the
+        // handler in this state is an error.
+        private boolean mIsActive = true;
+
+        NonceTest(@NonNull String name) {
+            super(name);
+        }
+
+        void shutdown() {
+            // The handler has been discarded as part of test cleanup.  Further access is an
+            // error.
+            mIsActive = false;
+        }
+
+        @Override
+        long getNonce() {
+            if (!mIsActive) {
+                throw new IllegalStateException("handler " + mName + " is shutdown");
+            }
+            return mValue;
+        }
+
+        @Override
+        void setNonce(long value) {
+            if (!mIsActive) {
+                throw new IllegalStateException("handler " + mName + " is shutdown");
+            }
+            mValue = value;
+        }
+    }
+
+    /**
+     * A static list of nonce handlers, indexed by name.  NonceHandlers can be safely shared by
+     * multiple threads, and can therefore be shared by multiple instances of the same cache, and
+     * with static calls (see {@link #invalidateCache}.  Addition and removal are guarded by the
+     * global lock, to ensure that duplicates are not created.
+     */
+    private static final ConcurrentHashMap<String, NonceHandler> sHandlers
+            = new ConcurrentHashMap<>();
+
+    /**
+     * Return the proper nonce handler, based on the property name.
+     */
+    private static NonceHandler getNonceHandler(@NonNull String name) {
+        NonceHandler h = sHandlers.get(name);
+        if (h == null) {
+            synchronized (sGlobalLock) {
+                h = sHandlers.get(name);
+                if (h == null) {
+                    if (name.startsWith("cache_key.test.")) {
+                        h = new NonceTest(name);
+                    } else {
+                        h = new NonceSysprop(name);
+                    }
+                    sHandlers.put(name, h);
+                }
+            }
+        }
+        return h;
+    }
+
+    /**
      * Make a new property invalidated cache.  This constructor names the cache after the
      * property name.  New clients should prefer the constructor that takes an explicit
      * cache name.
@@ -417,6 +700,7 @@
         mPropertyName = propertyName;
         validateCacheKey(mPropertyName);
         mCacheName = cacheName;
+        mNonce = getNonceHandler(mPropertyName);
         mMaxEntries = maxEntries;
         mComputer = new DefaultComputer<>(this);
         mCache = createMap();
@@ -441,6 +725,7 @@
         mPropertyName = createPropertyName(module, api);
         validateCacheKey(mPropertyName);
         mCacheName = cacheName;
+        mNonce = getNonceHandler(mPropertyName);
         mMaxEntries = maxEntries;
         mComputer = computer;
         mCache = createMap();
@@ -484,130 +769,58 @@
     }
 
     /**
-     * SystemProperties are protected and cannot be written (or read, usually) by random
-     * processes.  So, for testing purposes, the methods have a bypass mode that reads and
-     * writes to a HashMap and does not go out to the SystemProperties at all.
-     */
-
-    // If true, the cache might be under test.  If false, there is no testing in progress.
-    private static volatile boolean sTesting = false;
-
-    // If sTesting is true then keys that are under test are in this map.
-    private static final HashMap<String, Long> sTestingPropertyMap = new HashMap<>();
-
-    /**
-     * Enable or disable testing.  The testing property map is cleared every time this
-     * method is called.
+     * Enable or disable testing.  At this time, no action is taken when testing begins.
      * @hide
      */
     @TestApi
     public static void setTestMode(boolean mode) {
-        sTesting = mode;
-        synchronized (sTestingPropertyMap) {
-            sTestingPropertyMap.clear();
+        if (mode) {
+            // No action when testing begins.
+        } else {
+            resetAfterTest();
         }
     }
 
     /**
-     * Enable testing the specific cache key.  Only keys in the map are subject to testing.
-     * There is no method to stop testing a property name.  Just disable the test mode.
-     */
-    private static void testPropertyName(@NonNull String name) {
-        synchronized (sTestingPropertyMap) {
-            sTestingPropertyMap.put(name, (long) NONCE_UNSET);
-        }
-    }
-
-    /**
-     * Enable testing the specific cache key.  Only keys in the map are subject to testing.
-     * There is no method to stop testing a property name.  Just disable the test mode.
+     * Enable testing the specific cache key.  This is a legacy API that will be removed as part of
+     * b/360897450.
      * @hide
      */
     @TestApi
     public void testPropertyName() {
-        testPropertyName(mPropertyName);
-    }
-
-    // Read the system property associated with the current cache.  This method uses the
-    // handle for faster reading.
-    private long getCurrentNonce() {
-        if (sTesting) {
-            synchronized (sTestingPropertyMap) {
-                Long n = sTestingPropertyMap.get(mPropertyName);
-                if (n != null) {
-                    return n;
-                }
-            }
-        }
-
-        SystemProperties.Handle handle = mPropertyHandle;
-        if (handle == null) {
-            handle = SystemProperties.find(mPropertyName);
-            if (handle == null) {
-                return NONCE_UNSET;
-            }
-            mPropertyHandle = handle;
-        }
-        return handle.getLong(NONCE_UNSET);
-    }
-
-    // Write the nonce in a static context.  No handle is available.
-    private static void setNonce(String name, long val) {
-        if (sTesting) {
-            synchronized (sTestingPropertyMap) {
-                Long n = sTestingPropertyMap.get(name);
-                if (n != null) {
-                    sTestingPropertyMap.put(name, val);
-                    return;
-                }
-            }
-        }
-        RuntimeException failure = null;
-        for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) {
-            try {
-                SystemProperties.set(name, Long.toString(val));
-                if (attempt > 0) {
-                    // This log is not guarded.  Based on known bug reports, it should
-                    // occur once a week or less.  The purpose of the log message is to
-                    // identify the retries as a source of delay that might be otherwise
-                    // be attributed to the cache itself.
-                    Log.w(TAG, "Nonce set after " + attempt + " tries");
-                }
-                return;
-            } catch (RuntimeException e) {
-                if (failure == null) {
-                    failure = e;
-                }
-                try {
-                    Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS);
-                } catch (InterruptedException x) {
-                    // Ignore this exception.  The desired delay is only approximate and
-                    // there is no issue if the sleep sometimes terminates early.
-                }
-            }
-        }
-        // This point is reached only if SystemProperties.set() fails at least once.
-        // Rethrow the first exception that was received.
-        throw failure;
-    }
-
-    // Set the nonce in a static context.  No handle is available.
-    private static long getNonce(String name) {
-        if (sTesting) {
-            synchronized (sTestingPropertyMap) {
-                Long n = sTestingPropertyMap.get(name);
-                if (n != null) {
-                    return n;
-                }
-            }
-        }
-        return SystemProperties.getLong(name, NONCE_UNSET);
     }
 
     /**
-     * Forget all cached values.
-     * TODO(216112648) remove this as a public API.  Clients should invalidate caches, not clear
-     * them.
+     * Clean up when testing ends. All NonceTest handlers are erased from the global list and are
+     * poisoned, just in case the test program has retained a handle to one of the associated
+     * caches.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void resetAfterTest() {
+        synchronized (sGlobalLock) {
+            for (Iterator<String> e = sHandlers.keys().asIterator(); e.hasNext(); ) {
+                String s = e.next();
+                final NonceHandler h = sHandlers.get(s);
+                if (h instanceof NonceTest t) {
+                    t.shutdown();
+                    sHandlers.remove(s);
+                }
+            }
+        }
+    }
+
+    // Read the nonce associated with the current cache.
+    @GuardedBy("mLock")
+    private long getCurrentNonce() {
+        return mNonce.getNonce();
+    }
+
+    /**
+     * Forget all cached values.  This is used by a client when the server exits.  Since the
+     * server has exited, the cache values are no longer valid, but the server is no longer
+     * present to invalidate the cache.  Note that this is not necessary if the server is
+     * system_server, because the entire operating system reboots if that process exits.
      * @hide
      */
     public final void clear() {
@@ -674,7 +887,7 @@
     }
 
     /**
-     * Disable the use of this cache in this process.  This method is using internally and during
+     * Disable the use of this cache in this process.  This method is used internally and during
      * testing.  To disable a cache in normal code, use disableLocal().  A disabled cache cannot
      * be re-enabled.
      * @hide
@@ -783,7 +996,7 @@
 
                 if (DEBUG) {
                     if (!mDisabled) {
-                        Log.d(TAG, TextUtils.formatSimple(
+                        Log.d(TAG, formatSimple(
                             "cache %s %s for %s",
                             cacheName(), sNonceName[(int) currentNonce], queryToString(query)));
                     }
@@ -798,7 +1011,7 @@
                     if (cachedResult != null) mHits++;
                 } else {
                     if (DEBUG) {
-                        Log.d(TAG, TextUtils.formatSimple(
+                        Log.d(TAG, formatSimple(
                             "clearing cache %s of %d entries because nonce changed [%s] -> [%s]",
                             cacheName(), mCache.size(),
                             mLastSeenNonce, currentNonce));
@@ -824,7 +1037,7 @@
                     if (currentNonce != afterRefreshNonce) {
                         currentNonce = afterRefreshNonce;
                         if (DEBUG) {
-                            Log.d(TAG, TextUtils.formatSimple(
+                            Log.d(TAG, formatSimple(
                                     "restarting %s %s because nonce changed in refresh",
                                     cacheName(),
                                     queryToString(query)));
@@ -895,10 +1108,7 @@
      * @param name Name of the cache-key property to invalidate
      */
     private static void disableSystemWide(@NonNull String name) {
-        if (!sEnabled) {
-            return;
-        }
-        setNonce(name, NONCE_DISABLED);
+        getNonceHandler(name).disable();
     }
 
     /**
@@ -908,7 +1118,7 @@
      */
     @TestApi
     public void invalidateCache() {
-        invalidateCache(mPropertyName);
+        mNonce.invalidate();
     }
 
     /**
@@ -931,59 +1141,7 @@
      * @hide
      */
     public static void invalidateCache(@NonNull String name) {
-        if (!sEnabled) {
-            if (DEBUG) {
-                Log.w(TAG, TextUtils.formatSimple(
-                    "cache invalidate %s suppressed", name));
-            }
-            return;
-        }
-
-        // Take the cork lock so invalidateCache() racing against corkInvalidations() doesn't
-        // clobber a cork-written NONCE_UNSET with a cache key we compute before the cork.
-        // The property service is single-threaded anyway, so we don't lose any concurrency by
-        // taking the cork lock around cache invalidations.  If we see contention on this lock,
-        // we're invalidating too often.
-        synchronized (sCorkLock) {
-            Integer numberCorks = sCorks.get(name);
-            if (numberCorks != null && numberCorks > 0) {
-                if (DEBUG) {
-                    Log.d(TAG, "ignoring invalidation due to cork: " + name);
-                }
-                final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
-                sCorkedInvalidates.put(name, count + 1);
-                return;
-            }
-            invalidateCacheLocked(name);
-        }
-    }
-
-    @GuardedBy("sCorkLock")
-    private static void invalidateCacheLocked(@NonNull String name) {
-        // There's no race here: we don't require that values strictly increase, but instead
-        // only that each is unique in a single runtime-restart session.
-        final long nonce = getNonce(name);
-        if (nonce == NONCE_DISABLED) {
-            if (DEBUG) {
-                Log.d(TAG, "refusing to invalidate disabled cache: " + name);
-            }
-            return;
-        }
-
-        long newValue;
-        do {
-            newValue = NoPreloadHolder.next();
-        } while (isReservedNonce(newValue));
-        if (DEBUG) {
-            Log.d(TAG, TextUtils.formatSimple(
-                    "invalidating cache [%s]: [%s] -> [%s]",
-                    name, nonce, Long.toString(newValue)));
-        }
-        // There is a small race with concurrent disables here.  A compare-and-exchange
-        // property operation would be required to eliminate the race condition.
-        setNonce(name, newValue);
-        long invalidateCount = sInvalidates.getOrDefault(name, (long) 0);
-        sInvalidates.put(name, ++invalidateCount);
+        getNonceHandler(name).invalidate();
     }
 
     /**
@@ -1000,43 +1158,7 @@
      * @hide
      */
     public static void corkInvalidations(@NonNull String name) {
-        if (!sEnabled) {
-            if (DEBUG) {
-                Log.w(TAG, TextUtils.formatSimple(
-                    "cache cork %s suppressed", name));
-            }
-            return;
-        }
-
-        synchronized (sCorkLock) {
-            int numberCorks = sCorks.getOrDefault(name, 0);
-            if (DEBUG) {
-                Log.d(TAG, TextUtils.formatSimple(
-                        "corking %s: numberCorks=%s", name, numberCorks));
-            }
-
-            // If we're the first ones to cork this cache, set the cache to the corked state so
-            // existing caches talk directly to their services while we've corked updates.
-            // Make sure we don't clobber a disabled cache value.
-
-            // TODO(dancol): we can skip this property write and leave the cache enabled if the
-            // caller promises not to make observable changes to the cache backing state before
-            // uncorking the cache, e.g., by holding a read lock across the cork-uncork pair.
-            // Implement this more dangerous mode of operation if necessary.
-            if (numberCorks == 0) {
-                final long nonce = getNonce(name);
-                if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
-                    setNonce(name, NONCE_CORKED);
-                }
-            } else {
-                final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
-                sCorkedInvalidates.put(name, count + 1);
-            }
-            sCorks.put(name, numberCorks + 1);
-            if (DEBUG) {
-                Log.d(TAG, "corked: " + name);
-            }
-        }
+        getNonceHandler(name).cork();
     }
 
     /**
@@ -1048,34 +1170,7 @@
      * @hide
      */
     public static void uncorkInvalidations(@NonNull String name) {
-        if (!sEnabled) {
-            if (DEBUG) {
-                Log.w(TAG, TextUtils.formatSimple(
-                        "cache uncork %s suppressed", name));
-            }
-            return;
-        }
-
-        synchronized (sCorkLock) {
-            int numberCorks = sCorks.getOrDefault(name, 0);
-            if (DEBUG) {
-                Log.d(TAG, TextUtils.formatSimple(
-                        "uncorking %s: numberCorks=%s", name, numberCorks));
-            }
-
-            if (numberCorks < 1) {
-                throw new AssertionError("cork underflow: " + name);
-            }
-            if (numberCorks == 1) {
-                sCorks.remove(name);
-                invalidateCacheLocked(name);
-                if (DEBUG) {
-                    Log.d(TAG, "uncorked: " + name);
-                }
-            } else {
-                sCorks.put(name, numberCorks - 1);
-            }
-        }
+        getNonceHandler(name).uncork();
     }
 
     /**
@@ -1104,6 +1199,8 @@
         @GuardedBy("mLock")
         private Handler mHandler;
 
+        private NonceHandler mNonce;
+
         public AutoCorker(@NonNull String propertyName) {
             this(propertyName, DEFAULT_AUTO_CORK_DELAY_MS);
         }
@@ -1117,31 +1214,35 @@
         }
 
         public void autoCork() {
+            synchronized (mLock) {
+                if (mNonce == null) {
+                    mNonce = getNonceHandler(mPropertyName);
+                }
+            }
+
             if (getLooper() == null) {
                 // We're not ready to auto-cork yet, so just invalidate the cache immediately.
                 if (DEBUG) {
                     Log.w(TAG, "invalidating instead of autocorking early in init: "
                             + mPropertyName);
                 }
-                PropertyInvalidatedCache.invalidateCache(mPropertyName);
+                mNonce.invalidate();
                 return;
             }
             synchronized (mLock) {
                 boolean alreadyQueued = mUncorkDeadlineMs >= 0;
                 if (DEBUG) {
-                    Log.w(TAG, TextUtils.formatSimple(
+                    Log.d(TAG, formatSimple(
                             "autoCork %s mUncorkDeadlineMs=%s", mPropertyName,
                             mUncorkDeadlineMs));
                 }
                 mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs;
                 if (!alreadyQueued) {
                     getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs);
-                    PropertyInvalidatedCache.corkInvalidations(mPropertyName);
+                    mNonce.cork();
                 } else {
-                    synchronized (sCorkLock) {
-                        final long count = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0);
-                        sCorkedInvalidates.put(mPropertyName, count + 1);
-                    }
+                    // Count this as a corked invalidation.
+                    mNonce.invalidate();
                 }
             }
         }
@@ -1149,7 +1250,7 @@
         private void handleMessage(Message msg) {
             synchronized (mLock) {
                 if (DEBUG) {
-                    Log.w(TAG, TextUtils.formatSimple(
+                    Log.d(TAG, formatSimple(
                             "handleMsesage %s mUncorkDeadlineMs=%s",
                             mPropertyName, mUncorkDeadlineMs));
                 }
@@ -1161,7 +1262,7 @@
                 if (mUncorkDeadlineMs > nowMs) {
                     mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs;
                     if (DEBUG) {
-                        Log.w(TAG, TextUtils.formatSimple(
+                        Log.d(TAG, formatSimple(
                                         "scheduling uncork at %s",
                                         mUncorkDeadlineMs));
                     }
@@ -1169,10 +1270,10 @@
                     return;
                 }
                 if (DEBUG) {
-                    Log.w(TAG, "automatic uncorking " + mPropertyName);
+                    Log.d(TAG, "automatic uncorking " + mPropertyName);
                 }
                 mUncorkDeadlineMs = -1;
-                PropertyInvalidatedCache.uncorkInvalidations(mPropertyName);
+                mNonce.uncork();
             }
         }
 
@@ -1207,7 +1308,7 @@
             Result resultToCompare = recompute(query);
             boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
             if (!nonceChanged && !resultEquals(proposedResult, resultToCompare)) {
-                Log.e(TAG, TextUtils.formatSimple(
+                Log.e(TAG, formatSimple(
                         "cache %s inconsistent for %s is %s should be %s",
                         cacheName(), queryToString(query),
                         proposedResult, resultToCompare));
@@ -1284,17 +1385,9 @@
     /**
      * Returns a list of caches alive at the current time.
      */
-    @GuardedBy("sGlobalLock")
     private static @NonNull ArrayList<PropertyInvalidatedCache> getActiveCaches() {
-        return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet());
-    }
-
-    /**
-     * Returns a list of the active corks in a process.
-     */
-    private static @NonNull ArrayList<Map.Entry<String, Integer>> getActiveCorks() {
-        synchronized (sCorkLock) {
-            return new ArrayList<Map.Entry<String, Integer>>(sCorks.entrySet());
+        synchronized (sGlobalLock) {
+            return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet());
         }
     }
 
@@ -1361,32 +1454,27 @@
             return;
         }
 
-        long invalidateCount;
-        long corkedInvalidates;
-        synchronized (sCorkLock) {
-            invalidateCount = sInvalidates.getOrDefault(mPropertyName, (long) 0);
-            corkedInvalidates = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0);
-        }
+        NonceHandler.Stats stats = mNonce.getStats();
 
         synchronized (mLock) {
-            pw.println(TextUtils.formatSimple("  Cache Name: %s", cacheName()));
-            pw.println(TextUtils.formatSimple("    Property: %s", mPropertyName));
+            pw.println(formatSimple("  Cache Name: %s", cacheName()));
+            pw.println(formatSimple("    Property: %s", mPropertyName));
             final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]
                     + mSkips[NONCE_BYPASS];
-            pw.println(TextUtils.formatSimple(
+            pw.println(formatSimple(
                     "    Hits: %d, Misses: %d, Skips: %d, Clears: %d",
                     mHits, mMisses, skips, mClears));
-            pw.println(TextUtils.formatSimple(
+            pw.println(formatSimple(
                     "    Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d",
                     mSkips[NONCE_CORKED], mSkips[NONCE_UNSET],
                     mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED]));
-            pw.println(TextUtils.formatSimple(
+            pw.println(formatSimple(
                     "    Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
-                    mLastSeenNonce, invalidateCount, corkedInvalidates));
-            pw.println(TextUtils.formatSimple(
+                    mLastSeenNonce, stats.invalidated, stats.corkedInvalidates));
+            pw.println(formatSimple(
                     "    Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
                     mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
-            pw.println(TextUtils.formatSimple("    Enabled: %s", mDisabled ? "false" : "true"));
+            pw.println(formatSimple("    Enabled: %s", mDisabled ? "false" : "true"));
             pw.println("");
 
             // No specific cache was requested.  This is the default, and no details
@@ -1404,23 +1492,7 @@
                 String key = Objects.toString(entry.getKey());
                 String value = Objects.toString(entry.getValue());
 
-                pw.println(TextUtils.formatSimple("      Key: %s\n      Value: %s\n", key, value));
-            }
-        }
-    }
-
-    /**
-     * Dump the corking status.
-     */
-    @GuardedBy("sCorkLock")
-    private static void dumpCorkInfo(PrintWriter pw) {
-        ArrayList<Map.Entry<String, Integer>> activeCorks = getActiveCorks();
-        if (activeCorks.size() > 0) {
-            pw.println("  Corking Status:");
-            for (int i = 0; i < activeCorks.size(); i++) {
-                Map.Entry<String, Integer> entry = activeCorks.get(i);
-                pw.println(TextUtils.formatSimple("    Property Name: %s Count: %d",
-                                entry.getKey(), entry.getValue()));
+                pw.println(formatSimple("      Key: %s\n      Value: %s\n", key, value));
             }
         }
     }
@@ -1441,14 +1513,7 @@
         // then only that cache is reported.
         boolean detail = anyDetailed(args);
 
-        ArrayList<PropertyInvalidatedCache> activeCaches;
-        synchronized (sGlobalLock) {
-            activeCaches = getActiveCaches();
-            if (!detail) {
-                dumpCorkInfo(pw);
-            }
-        }
-
+        ArrayList<PropertyInvalidatedCache> activeCaches = getActiveCaches();
         for (int i = 0; i < activeCaches.size(); i++) {
             PropertyInvalidatedCache currentCache = activeCaches.get(i);
             currentCache.dumpContents(pw, detail, args);
diff --git a/core/java/android/app/usage/UsageEventsQuery.java b/core/java/android/app/usage/UsageEventsQuery.java
index c0f13ca..ecf4cd1 100644
--- a/core/java/android/app/usage/UsageEventsQuery.java
+++ b/core/java/android/app/usage/UsageEventsQuery.java
@@ -75,7 +75,7 @@
     }
 
     /**
-     * Returns the exclusive timpstamp to indicate the end of the range of events.
+     * Returns the exclusive timestamp to indicate the end of the range of events.
      * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
      */
     public @CurrentTimeMillisLong long getEndTimeMillis() {
diff --git a/core/java/android/content/pm/xr.aconfig b/core/java/android/content/pm/xr.aconfig
new file mode 100644
index 0000000..61835c1
--- /dev/null
+++ b/core/java/android/content/pm/xr.aconfig
@@ -0,0 +1,9 @@
+package: "android.xr"
+container: "system"
+
+flag {
+    namespace: "xr"
+    name: "xr_manifest_entries"
+    description: "Adds manifest entries used by Android XR"
+    bug: "364416355"
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 99eeca9..f03a4a9 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -142,3 +142,10 @@
   description: "Controls whether the connected mice's primary buttons, left and right, can be swapped."
   bug: "352598211"
 }
+
+flag {
+  name: "keyboard_a11y_shortcut_control"
+  namespace: "input"
+  description: "Adds shortcuts to toggle and control a11y features"
+  bug: "373458181"
+}
diff --git a/core/java/android/hardware/radio/RadioAlert.aidl b/core/java/android/hardware/radio/RadioAlert.aidl
new file mode 100644
index 0000000..17f4fc7
--- /dev/null
+++ b/core/java/android/hardware/radio/RadioAlert.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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 android.hardware.radio;
+
+/** @hide */
+parcelable RadioAlert;
\ No newline at end of file
diff --git a/core/java/android/hardware/radio/RadioAlert.java b/core/java/android/hardware/radio/RadioAlert.java
new file mode 100644
index 0000000..b55dcd8
--- /dev/null
+++ b/core/java/android/hardware/radio/RadioAlert.java
@@ -0,0 +1,505 @@
+/**
+ * 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 android.hardware.radio;
+
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Emergency Alert Message
+ *
+ * <p>Alert message can be sent from a radio station of technologies such as HD radio to
+ * the radio users for some emergency events (see ITU-T X.1303 bis for more info).
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+public final class RadioAlert implements Parcelable {
+
+    public static final class Geocode implements Parcelable {
+
+        private final String mValueName;
+        private final String mValue;
+
+        /**
+         * Constructor of geocode.
+         *
+         * @param valueName Name of geocode value
+         * @param value Value of geocode
+         * @hide
+         */
+        public Geocode(@NonNull String valueName, @NonNull String value) {
+            mValueName = Objects.requireNonNull(valueName, "Geocode value name can not be null");
+            mValue = Objects.requireNonNull(value, "Geocode value can not be null");
+        }
+
+        private Geocode(Parcel in) {
+            mValueName = in.readString8();
+            mValue = in.readString8();
+        }
+
+        public static final @NonNull Creator<Geocode> CREATOR = new Creator<Geocode>() {
+            @Override
+            public Geocode createFromParcel(Parcel in) {
+                return new Geocode(in);
+            }
+
+            @Override
+            public Geocode[] newArray(int size) {
+                return new Geocode[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeString8(mValueName);
+            dest.writeString8(mValue);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "Gecode [valueName=" + mValueName + ", value=" + mValue + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mValueName, mValue);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Geocode other)) {
+                return false;
+            }
+
+            return Objects.equals(mValueName, other.mValueName)
+                    && Objects.equals(mValue, other.mValue);
+        }
+    }
+
+    public static final class Coordinate implements Parcelable {
+        private final double mLatitude;
+        private final double mLongitude;
+
+        /**
+         * Constructor of coordinate.
+         *
+         * @param latitude Latitude of the coordinate
+         * @param longitude Longitude of the coordinate
+         * @hide
+         */
+        public Coordinate(double latitude, double longitude) {
+            if (latitude < -90.0 || latitude > 90.0) {
+                throw new IllegalArgumentException("Latitude value should be between -90 and 90");
+            }
+            if (longitude < -180.0 || longitude > 180.0) {
+                throw new IllegalArgumentException(
+                        "Longitude value should be between -180 and 180");
+            }
+            mLatitude = latitude;
+            mLongitude = longitude;
+        }
+
+        private Coordinate(Parcel in) {
+            mLatitude = in.readDouble();
+            mLongitude = in.readDouble();
+        }
+
+        public static final @NonNull Creator<Coordinate> CREATOR = new Creator<Coordinate>() {
+            @Override
+            public Coordinate createFromParcel(Parcel in) {
+                return new Coordinate(in);
+            }
+
+            @Override
+            public Coordinate[] newArray(int size) {
+                return new Coordinate[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeDouble(mLatitude);
+            dest.writeDouble(mLongitude);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "Coordinate [latitude=" + mLatitude + ", longitude=" + mLongitude + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mLatitude, mLongitude);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Coordinate other)) {
+                return false;
+            }
+            return mLatitude == other.mLatitude && mLongitude == other.mLongitude;
+        }
+    }
+
+    public static final class Polygon implements Parcelable {
+
+        private final List<Coordinate> mCoordinates;
+
+        /**
+         * Constructor of polygon.
+         *
+         * @param coordinates Coordinates the polygon is composed of
+         * @hide
+         */
+        public Polygon(@NonNull List<Coordinate> coordinates) {
+            Objects.requireNonNull(coordinates, "Coordinates can not be null");
+            if (coordinates.size() < 4) {
+                throw new IllegalArgumentException("Number of coordinates must be at least 4");
+            }
+            if (!coordinates.get(0).equals(coordinates.get(coordinates.size() - 1))) {
+                throw new IllegalArgumentException(
+                        "The last and first coordinates must be the same");
+            }
+            mCoordinates = coordinates;
+        }
+
+        private Polygon(Parcel in) {
+            mCoordinates = new ArrayList<>();
+            in.readTypedList(mCoordinates, Coordinate.CREATOR);
+        }
+
+        public static final @NonNull Creator<Polygon> CREATOR = new Creator<Polygon>() {
+            @Override
+            public Polygon createFromParcel(Parcel in) {
+                return new Polygon(in);
+            }
+
+            @Override
+            public Polygon[] newArray(int size) {
+                return new Polygon[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeTypedList(mCoordinates);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "Polygon [coordinates=" + mCoordinates + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mCoordinates);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Polygon other)) {
+                return false;
+            }
+            return mCoordinates.equals(other.mCoordinates);
+        }
+    }
+
+    public static final class AlertArea implements Parcelable {
+
+        private final List<Polygon> mPolygons;
+        private final List<Geocode> mGeocodes;
+
+        /**
+         * Constructor of alert area.
+         *
+         * @param polygons Polygons used in alert area
+         * @param geocodes Geocodes used in alert area
+         * @hide
+         */
+        public AlertArea(@NonNull List<Polygon> polygons, @NonNull List<Geocode> geocodes) {
+            mPolygons = Objects.requireNonNull(polygons, "Polygons can not be null");
+            mGeocodes = Objects.requireNonNull(geocodes, "Geocodes can not be null");
+        }
+
+        private AlertArea(Parcel in) {
+            mPolygons = new ArrayList<>();
+            mGeocodes = new ArrayList<>();
+            in.readTypedList(mPolygons, Polygon.CREATOR);
+            in.readTypedList(mGeocodes, Geocode.CREATOR);
+        }
+
+        public static final @NonNull Creator<AlertArea> CREATOR = new Creator<AlertArea>() {
+            @Override
+            public AlertArea createFromParcel(Parcel in) {
+                return new AlertArea(in);
+            }
+
+            @Override
+            public AlertArea[] newArray(int size) {
+                return new AlertArea[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeTypedList(mPolygons);
+            dest.writeTypedList(mGeocodes);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "AlertArea [polygons=" + mPolygons + ", geocodes=" + mGeocodes + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPolygons, mGeocodes);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof AlertArea other)) {
+                return false;
+            }
+
+            return mPolygons.equals(other.mPolygons) && mGeocodes.equals(other.mGeocodes);
+        }
+    }
+
+    public static final class AlertInfo implements Parcelable {
+
+        private final List<Integer> mCategoryList;
+        private final int mUrgency;
+        private final int mSeverity;
+        private final int mCertainty;
+        private final String mTextualMessage;
+        private final List<AlertArea> mAreaList;
+
+        /**
+         * Constructor for alert info.
+         *
+         * @param categoryList Array of categories of the subject event of the alert message
+         * @param urgency The urgency of the subject event of the alert message
+         * @param severity The severity of the subject event of the alert message
+         * @param certainty The certainty of the subject event of the alert message
+         * @param textualMessage Textual descriptions of the subject event
+         * @param areaList The array of geographic areas to which the alert info segment in which
+         *                 it appears applies
+         * @hide
+         */
+        public AlertInfo(@NonNull List<Integer> categoryList, int urgency,
+                int severity, int certainty,
+                String textualMessage, @NonNull List<AlertArea> areaList) {
+            mCategoryList = Objects.requireNonNull(categoryList, "Category list can not be null");
+            mUrgency = urgency;
+            mSeverity = severity;
+            mCertainty = certainty;
+            mTextualMessage = textualMessage;
+            mAreaList = Objects.requireNonNull(areaList, "Area list can not be null");
+        }
+
+        private AlertInfo(Parcel in) {
+            mCategoryList = in.readArrayList(Integer.class.getClassLoader(), Integer.class);
+            mUrgency = in.readInt();
+            mSeverity = in.readInt();
+            mCertainty = in.readInt();
+            mTextualMessage = in.readString8();
+            mAreaList = new ArrayList<>();
+            in.readTypedList(mAreaList, AlertArea.CREATOR);
+        }
+
+        public static final @NonNull Creator<AlertInfo> CREATOR = new Creator<AlertInfo>() {
+            @Override
+            public AlertInfo createFromParcel(Parcel in) {
+                return new AlertInfo(in);
+            }
+
+            @Override
+            public AlertInfo[] newArray(int size) {
+                return new AlertInfo[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeList(mCategoryList);
+            dest.writeInt(mUrgency);
+            dest.writeInt(mSeverity);
+            dest.writeInt(mCertainty);
+            dest.writeString8(mTextualMessage);
+            dest.writeTypedList(mAreaList);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "AlertInfo [categoryList=" + mCategoryList + ", urgency=" + mUrgency
+                    + ", severity=" + mSeverity + ", certainty=" + mCertainty
+                    + ", textualMessage=" + mTextualMessage + ", areaList=" + mAreaList + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mCategoryList, mUrgency, mSeverity, mCertainty, mTextualMessage,
+                    mAreaList);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof AlertInfo other)) {
+                return false;
+            }
+
+            return mCategoryList.equals(other.mCategoryList) && mUrgency == other.mUrgency
+                    && mSeverity == other.mSeverity && mCertainty == other.mCertainty
+                    && mTextualMessage.equals(other.mTextualMessage)
+                    && mAreaList.equals(other.mAreaList);
+        }
+    }
+
+    private final int mStatus;
+    private final int mMessageType;
+    private final List<AlertInfo> mInfoList;
+    private final int mScope;
+
+    /**
+     * Constructor of radio alert message.
+     *
+     * @param status Status of alert message
+     * @param messageType Message type of alert message
+     * @param infoList List of alert info
+     * @param scope Scope of alert message
+     * @hide
+     */
+    public RadioAlert(int status, int messageType,
+            @NonNull List<AlertInfo> infoList, int scope) {
+        mStatus = status;
+        mMessageType = messageType;
+        mInfoList = Objects.requireNonNull(infoList, "Alert info list can not be null");
+        mScope = scope;
+    }
+
+    private RadioAlert(Parcel in) {
+        mStatus = in.readInt();
+        mMessageType = in.readInt();
+        mInfoList = in.readParcelableList(new ArrayList<>(), AlertInfo.class.getClassLoader(),
+                AlertInfo.class);
+        mScope = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mStatus);
+        dest.writeInt(mMessageType);
+        dest.writeParcelableList(mInfoList, /* flags= */ 0);
+        dest.writeInt(mScope);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "RadioAlert [status=" + mStatus + ", messageType=" + mMessageType
+                + ", infoList= " + mInfoList + ", scope=" + mScope + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStatus, mMessageType, mInfoList, mScope);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof RadioAlert other)) {
+            return false;
+        }
+
+        return mStatus == other.mStatus && mMessageType == other.mMessageType
+                && mInfoList.equals(other.mInfoList) && mScope == other.mScope;
+    }
+
+    public static final @NonNull Creator<RadioAlert> CREATOR = new Creator<RadioAlert>() {
+        @Override
+        public RadioAlert createFromParcel(Parcel in) {
+            return new RadioAlert(in);
+        }
+
+        @Override
+        public RadioAlert[] newArray(int size) {
+            return new RadioAlert[size];
+        }
+    };
+}
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index 77d6cb7..f1d3957 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.vibrator.Flags;
 import android.util.SparseArray;
@@ -28,6 +29,7 @@
 import java.util.Locale;
 import java.util.Objects;
 import java.util.StringJoiner;
+import java.util.function.Function;
 
 /**
  * A CombinedVibration describes a combination of haptic effects to be performed by one or more
@@ -114,6 +116,17 @@
     public abstract long getDuration();
 
     /**
+     * Gets the estimated duration of the combined vibration in milliseconds.
+     *
+     * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
+     * the estimated duration based on the {@link VibratorInfo}. For all other effects this will
+     * return the same as {@link #getDuration()}.
+     *
+     * @hide
+     */
+    public abstract long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos);
+
+    /**
      * Returns true if this effect could represent a touch haptic feedback.
      *
      * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
@@ -383,6 +396,23 @@
 
         /** @hide */
         @Override
+        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+            if (vibratorInfos == null) {
+                return getDuration();
+            }
+            long maxDuration = 0;
+            for (int i = 0; i < vibratorInfos.size(); i++) {
+                long duration = mEffect.getDuration(vibratorInfos.valueAt(i));
+                if ((duration == Long.MAX_VALUE) || (duration < 0)) {
+                    return duration;
+                }
+                maxDuration = Math.max(maxDuration, duration);
+            }
+            return maxDuration;
+        }
+
+        /** @hide */
+        @Override
         public boolean isHapticFeedbackCandidate() {
             return mEffect.isHapticFeedbackCandidate();
         }
@@ -531,10 +561,27 @@
 
         @Override
         public long getDuration() {
+            return getDuration(idx -> mEffects.valueAt(idx).getDuration());
+        }
+
+        /** @hide */
+        @Override
+        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+            if (vibratorInfos == null) {
+                return getDuration();
+            }
+            return getDuration(idx -> {
+                VibrationEffect effect = mEffects.valueAt(idx);
+                VibratorInfo info = vibratorInfos.get(mEffects.keyAt(idx));
+                return effect.getDuration(info);
+            });
+        }
+
+        private long getDuration(Function<Integer, Long> durationFn) {
             long maxDuration = Long.MIN_VALUE;
             boolean hasUnknownStep = false;
             for (int i = 0; i < mEffects.size(); i++) {
-                long duration = mEffects.valueAt(i).getDuration();
+                long duration = durationFn.apply(i);
                 if (duration == Long.MAX_VALUE) {
                     // If any duration is repeating, this combination duration is also repeating.
                     return duration;
@@ -750,12 +797,21 @@
 
         @Override
         public long getDuration() {
+            return getDuration(CombinedVibration::getDuration);
+        }
+
+        /** @hide */
+        @Override
+        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+            return getDuration(effect -> effect.getDuration(vibratorInfos));
+        }
+
+        private long getDuration(Function<CombinedVibration, Long> durationFn) {
             boolean hasUnknownStep = false;
             long durations = 0;
             final int effectCount = mEffects.size();
             for (int i = 0; i < effectCount; i++) {
-                CombinedVibration effect = mEffects.get(i);
-                long duration = effect.getDuration();
+                long duration = durationFn.apply(mEffects.get(i));
                 if (duration == Long.MAX_VALUE) {
                     // If any duration is repeating, this combination duration is also repeating.
                     return duration;
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index ffc58c5..61dd11f 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -55,6 +55,7 @@
 import java.util.Objects;
 import java.util.StringJoiner;
 import java.util.function.BiFunction;
+import java.util.function.Function;
 
 /**
  * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -565,6 +566,19 @@
     public abstract long getDuration();
 
     /**
+     * Gets the estimated duration of the segment for given vibrator, in milliseconds.
+     *
+     * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
+     * the estimated duration based on the given {@link VibratorInfo}. For all other effects this
+     * will return the same as {@link #getDuration()}.
+     *
+     * @hide
+     */
+    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+        return getDuration();
+    }
+
+    /**
      * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended.
      *
      * <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more
@@ -904,13 +918,23 @@
 
         @Override
         public long getDuration() {
+            return getDuration(VibrationEffectSegment::getDuration);
+        }
+
+        /** @hide */
+        @Override
+        public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+            return getDuration(segment -> segment.getDuration(vibratorInfo));
+        }
+
+        private long getDuration(Function<VibrationEffectSegment, Long> durationFn) {
             if (mRepeatIndex >= 0) {
                 return Long.MAX_VALUE;
             }
             int segmentCount = mSegments.size();
             long totalDuration = 0;
             for (int i = 0; i < segmentCount; i++) {
-                long segmentDuration = mSegments.get(i).getDuration();
+                long segmentDuration = durationFn.apply(mSegments.get(i));
                 if (segmentDuration < 0) {
                     return segmentDuration;
                 }
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 39f8412..b17e82a 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -16,6 +16,17 @@
 
 package android.os.vibrator;
 
+import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_THUD;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.VibrationEffect.EFFECT_CLICK;
+import static android.os.VibrationEffect.EFFECT_DOUBLE_CLICK;
+import static android.os.VibrationEffect.EFFECT_HEAVY_CLICK;
+import static android.os.VibrationEffect.EFFECT_POP;
+import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
+import static android.os.VibrationEffect.EFFECT_THUD;
+import static android.os.VibrationEffect.EFFECT_TICK;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -78,6 +89,32 @@
 
     /** @hide */
     @Override
+    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+        if (vibratorInfo == null) {
+            return getDuration();
+        }
+        return switch (mEffectId) {
+            case EFFECT_TICK,
+                 EFFECT_CLICK,
+                 EFFECT_HEAVY_CLICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_CLICK);
+            case EFFECT_TEXTURE_TICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_TICK);
+            case EFFECT_THUD -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_THUD);
+            case EFFECT_DOUBLE_CLICK -> {
+                long clickDuration = vibratorInfo.getPrimitiveDuration(PRIMITIVE_CLICK);
+                yield clickDuration > 0 ? 2 * clickDuration : getDuration();
+            }
+            default -> getDuration();
+        };
+    }
+
+    private long estimateFromPrimitiveDuration(VibratorInfo vibratorInfo, int primitiveId) {
+        int duration = vibratorInfo.getPrimitiveDuration(primitiveId);
+        // Unsupported primitives should be ignored here.
+        return duration > 0 ? duration : getDuration();
+    }
+
+    /** @hide */
+    @Override
     public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
         if (vibratorInfo.isEffectSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
             return true;
@@ -89,34 +126,30 @@
         }
         // The vibrator does not have hardware support for the effect, but the effect has fallback
         // support. Check if a fallback will be available for the effect ID.
-        switch (mEffectId) {
-            case VibrationEffect.EFFECT_CLICK:
-            case VibrationEffect.EFFECT_DOUBLE_CLICK:
-            case VibrationEffect.EFFECT_HEAVY_CLICK:
-            case VibrationEffect.EFFECT_TICK:
-                // Any of these effects are always supported via some form of fallback.
-                return true;
-            default:
-                return false;
-        }
+        return switch (mEffectId) {
+            // Any of these effects are always supported via some form of fallback.
+            case EFFECT_CLICK,
+                 EFFECT_DOUBLE_CLICK,
+                 EFFECT_HEAVY_CLICK,
+                 EFFECT_TICK -> true;
+            default -> false;
+        };
     }
 
     /** @hide */
     @Override
     public boolean isHapticFeedbackCandidate() {
-        switch (mEffectId) {
-            case VibrationEffect.EFFECT_CLICK:
-            case VibrationEffect.EFFECT_DOUBLE_CLICK:
-            case VibrationEffect.EFFECT_HEAVY_CLICK:
-            case VibrationEffect.EFFECT_POP:
-            case VibrationEffect.EFFECT_TEXTURE_TICK:
-            case VibrationEffect.EFFECT_THUD:
-            case VibrationEffect.EFFECT_TICK:
-                return true;
-            default:
-                // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
-                return false;
-        }
+        return switch (mEffectId) {
+            case EFFECT_CLICK,
+                 EFFECT_DOUBLE_CLICK,
+                 EFFECT_HEAVY_CLICK,
+                 EFFECT_POP,
+                 EFFECT_TEXTURE_TICK,
+                 EFFECT_THUD,
+                 EFFECT_TICK -> true;
+            // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
+            default -> false;
+        };
     }
 
     /** @hide */
@@ -153,27 +186,25 @@
     }
 
     private static boolean isValidEffectStrength(int strength) {
-        switch (strength) {
-            case VibrationEffect.EFFECT_STRENGTH_LIGHT:
-            case VibrationEffect.EFFECT_STRENGTH_MEDIUM:
-            case VibrationEffect.EFFECT_STRENGTH_STRONG:
-                return true;
-            default:
-                return false;
-        }
+        return switch (strength) {
+            case VibrationEffect.EFFECT_STRENGTH_LIGHT,
+                 VibrationEffect.EFFECT_STRENGTH_MEDIUM,
+                 VibrationEffect.EFFECT_STRENGTH_STRONG -> true;
+            default -> false;
+        };
     }
 
     /** @hide */
     @Override
     public void validate() {
         switch (mEffectId) {
-            case VibrationEffect.EFFECT_CLICK:
-            case VibrationEffect.EFFECT_DOUBLE_CLICK:
-            case VibrationEffect.EFFECT_HEAVY_CLICK:
-            case VibrationEffect.EFFECT_POP:
-            case VibrationEffect.EFFECT_TEXTURE_TICK:
-            case VibrationEffect.EFFECT_THUD:
-            case VibrationEffect.EFFECT_TICK:
+            case EFFECT_CLICK:
+            case EFFECT_DOUBLE_CLICK:
+            case EFFECT_HEAVY_CLICK:
+            case EFFECT_POP:
+            case EFFECT_TEXTURE_TICK:
+            case EFFECT_THUD:
+            case EFFECT_TICK:
                 break;
             default:
                 int[] ringtones = VibrationEffect.RINGTONES;
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 3c84bcd..91653ed 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -77,6 +77,16 @@
 
     /** @hide */
     @Override
+    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+        if (vibratorInfo == null) {
+            return getDuration();
+        }
+        int duration = vibratorInfo.getPrimitiveDuration(mPrimitiveId);
+        return duration > 0 ? duration + mDelay : getDuration();
+    }
+
+    /** @hide */
+    @Override
     public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
         return vibratorInfo.isPrimitiveSupported(mPrimitiveId);
     }
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index e6e5a27..88be96a 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -86,6 +86,7 @@
     private final int mDefaultKeyboardVibrationIntensity;
 
     private final boolean mKeyboardVibrationSettingsSupported;
+    private final int mVibrationPipelineMaxDurationMs;
 
     /** @hide */
     public VibrationConfig(@Nullable Resources resources) {
@@ -106,6 +107,8 @@
                 com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger);
         mKeyboardVibrationSettingsSupported = loadBoolean(resources,
                 com.android.internal.R.bool.config_keyboardVibrationSettingsSupported);
+        mVibrationPipelineMaxDurationMs = loadInteger(resources,
+                com.android.internal.R.integer.config_vibrationPipelineMaxDuration, 0);
 
         mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
                 com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -221,6 +224,23 @@
     }
 
     /**
+     * The max duration, in milliseconds, allowed for pipelining vibration requests.
+     *
+     * <p>If the ongoing vibration duration is shorter than this threshold then it should be allowed
+     * to finish before the next vibration can start. If the ongoing vibration is longer than this
+     * then it should be cancelled when it's superseded for the new one.
+     *
+     * @return the max duration allowed for vibration effect to finish before the next request, or
+     * zero to disable effect pipelining.
+     */
+    public int getVibrationPipelineMaxDurationMs() {
+        if (mVibrationPipelineMaxDurationMs < 0) {
+            return 0;
+        }
+        return mVibrationPipelineMaxDurationMs;
+    }
+
+    /**
      * Whether or not vibrations are ignored if the device is on a wireless charger.
      *
      * <p>This may be the case if vibration during wireless charging causes unwanted results, like
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index e1fb4e3..dadc849 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -17,6 +17,7 @@
 package android.os.vibrator;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -58,10 +59,23 @@
      */
     public abstract long getDuration();
 
-   /**
-     * Checks if a given {@link Vibrator} can play this segment as intended. See
-     * {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information about
-     * what counts as supported by a vibrator, and what counts as not.
+    /**
+     * Gets the estimated duration of the segment for given vibrator, in milliseconds.
+     *
+     * <p>For segments with hardware-dependent constants (e.g. primitives), this returns the
+     * estimated duration based on the given {@link VibratorInfo}. For all other effects this will
+     * return the same as {@link #getDuration()}.
+     *
+     * @hide
+     */
+    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+        return getDuration();
+    }
+
+    /**
+     * Checks if a given {@link android.os.Vibrator} can play this segment as intended. See
+     * {@link android.os.Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more
+     * information about what counts as supported by a vibrator, and what counts as not.
      *
      * @hide
      */
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index e3b1221..7ceb948 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -123,4 +123,25 @@
     metadata {
         purpose: PURPOSE_FEATURE
     }
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "haptics"
+    name: "primitive_composition_absolute_delay"
+    is_exported: true
+    description: "Enables functionality to create primitive compositions with absolute delays"
+    bug: "373357740"
+    metadata {
+        purpose: PURPOSE_FEATURE
+    }
+}
+
+flag {
+    namespace: "haptics"
+    name: "vibration_pipeline_enabled"
+    description: "Enables functionality to pipeline vibration effects to avoid cancelling short vibrations"
+    bug: "344494220"
+    metadata {
+        purpose: PURPOSE_FEATURE
+    }
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 0dec13f..e254bf3 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -73,8 +73,11 @@
 public abstract class Layout {
 
     // These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h
-    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 4f;
-    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0.2f;
+    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 0f;
+    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0f;
+    private static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP = 5f;
+    // since we're not using soft light yet, this needs to be much lower than the spec'd 0.8
+    private static final float HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE = 0.5f;
 
     /** @hide */
     @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
@@ -1025,11 +1028,18 @@
 
         var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX,
                 mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR);
+        var cornerRadius = mPaint.density * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP;
+
+        // We set the alpha on the color itself instead of Paint.setAlpha(), because that function
+        // actually mutates the color in... *ehem* very strange ways. Also the color might get reset
+        // for various reasons, which also resets the alpha.
+        var white = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 1f, 1f, 1f);
+        var black = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 0f, 0f, 0f);
 
         var originalTextColor = mPaint.getColor();
         var bgPaint = mWorkPlainPaint;
         bgPaint.reset();
-        bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? Color.WHITE : Color.BLACK);
+        bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? white : black);
         bgPaint.setStyle(Paint.Style.FILL);
 
         int start = getLineStart(firstLine);
@@ -1082,7 +1092,12 @@
                     private void drawRect() {
                         if (!mLineBackground.isEmpty()) {
                             mLineBackground.inset(-padding, -padding);
-                            canvas.drawRect(mLineBackground, bgPaint);
+                            canvas.drawRoundRect(
+                                    mLineBackground,
+                                    cornerRadius,
+                                    cornerRadius,
+                                    bgPaint
+                            );
                         }
                     }
 
@@ -1104,7 +1119,7 @@
                         if (hasColorChanged) {
                             mLastColor = textColor;
 
-                            return isHighContrastTextDark(textColor) ? Color.WHITE : Color.BLACK;
+                            return isHighContrastTextDark(textColor) ? white : black;
                         }
 
                         return bgPaint.getColor();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 83b4971..eb59e21 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1734,9 +1734,7 @@
                             mRTLastReportedPosition.top /*positionTop*/,
                             postScaleX, postScaleY);
 
-                    mRTLastSetCrop.set((int) (clipLeft / postScaleX), (int) (clipTop / postScaleY),
-                            (int) Math.ceil(clipRight / postScaleX),
-                            (int) Math.ceil(clipBottom / postScaleY));
+                    mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom);
                     if (DEBUG_POSITION) {
                         Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] "
                                         + "from scale %f, %f", mRTLastSetCrop.left,
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1e5c6d8..47fc437 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2352,6 +2352,13 @@
      * {@link #RESULT_HIDDEN}.
      * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
      * this does not return result of the request. For result use {@param resultReceiver} instead.
+     *
+     * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the
+     * Input Method is actually shown or hidden. If result is needed, use
+     * {@link android.view.WindowInsetsController#show} instead and set a
+     * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for
+     * the visibility of IME. If result is not needed, use {@link #showSoftInput(View, int)}
+     * instead.
      */
     public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
         return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
@@ -2399,6 +2406,14 @@
                                 & WindowInsets.Type.ime()) == 0) {
                     ImeTracker.forLogging().onProgress(statsToken,
                             ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
+                    if (resultReceiver != null) {
+                        final boolean imeReqVisible =
+                                (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
+                                        & WindowInsets.Type.ime()) != 0;
+                        resultReceiver.send(
+                                imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+                                        : InputMethodManager.RESULT_SHOWN, null);
+                    }
                     // TODO(b/322992891) handle case of SHOW_IMPLICIT
                     viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
                             false /* fromIme */, statsToken);
@@ -2531,6 +2546,13 @@
      * {@link #RESULT_HIDDEN}.
      * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
      * this does not return result of the request. For result use {@param resultReceiver} instead.
+     *
+     * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the
+     * Input Method is actually shown or hidden. If result is needed, use
+     * {@link android.view.WindowInsetsController#hide} instead and set a
+     * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for
+     * the visibility of IME. If result is not needed, use
+     * {@link #hideSoftInputFromView(View, int)} instead.
      */
     public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
             ResultReceiver resultReceiver) {
@@ -2569,6 +2591,14 @@
                 // TODO(b/322992891) handle case of HIDE_IMPLICIT_ONLY
                 final var viewRootImpl = servedView.getViewRootImpl();
                 if (viewRootImpl != null) {
+                    if (resultReceiver != null) {
+                        final boolean imeReqVisible =
+                                (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
+                                        & WindowInsets.Type.ime()) != 0;
+                        resultReceiver.send(
+                                !imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN
+                                        : InputMethodManager.RESULT_HIDDEN, null);
+                    }
                     viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime());
                 }
                 return true;
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index bae8aff..aa4927e 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -73,6 +73,17 @@
 }
 
 flag {
+    name: "consistent_get_current_input_method_info"
+    namespace: "input_method"
+    description: "Use BindingController as the source of truth in getCurrentInputMethodInfo"
+    bug: "355034523"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "ime_switcher_revamp"
     is_exported: true
     namespace: "input_method"
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 8e35843e..05dc910 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -64,7 +64,9 @@
     ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
     ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(
             Flags::enableDesktopWindowingTaskbarRunningApps, true),
+    // TODO: b/369763947 - remove this once ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS is ramped up
     ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
+    ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
     ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false),
     ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
             Flags::enableWindowingTransitionHandlersObservers, false);
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 1812953..45f6480 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -308,6 +308,13 @@
 }
 
 flag {
+    name: "enable_restore_to_previous_size_from_desktop_immersive"
+    namespace: "lse_desktop_experience"
+    description: "Restores the window bounds to their previous size when exiting desktop immersive"
+    bug: "372318163"
+}
+
+flag {
     name: "enable_display_focus_in_shell_transitions"
     namespace: "lse_desktop_experience"
     description: "Creates a shell transition when display focus switches."
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index dd6c879..3c201fc 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -179,6 +179,6 @@
             }
         }
 
-        throw new RuntimeException("Group " + group + "not found in viewer config");
+        throw new RuntimeException("Group " + group + " not found in viewer config");
     }
 }
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 3e597d7..d3b1f97 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -20,16 +20,20 @@
 import android.annotation.Nullable;
 import android.app.Notification.ProgressStyle;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.graphics.drawable.LayerDrawable;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.RemotableViewMethod;
 import android.widget.ProgressBar;
 import android.widget.RemoteViews;
 
 import androidx.annotation.ColorInt;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.NotificationProgressDrawable.Part;
@@ -49,7 +53,13 @@
  */
 @RemoteViews.RemoteView
 public class NotificationProgressBar extends ProgressBar {
+    private static final String TAG = "NotificationProgressBar";
+
     private NotificationProgressModel mProgressModel;
+
+    @Nullable
+    private List<Part> mProgressDrawableParts = null;
+
     @Nullable
     private Drawable mProgressTrackerDrawable = null;
 
@@ -58,7 +68,7 @@
     }
 
     public NotificationProgressBar(Context context, AttributeSet attrs) {
-        this(context, attrs, com.android.internal.R.attr.progressBarStyle);
+        this(context, attrs, R.attr.progressBarStyle);
     }
 
     public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -82,10 +92,42 @@
                 "Bundle shouldn't be null");
 
         mProgressModel = NotificationProgressModel.fromBundle(bundle);
+
+        if (mProgressModel.isIndeterminate()) {
+            final int indeterminateColor = mProgressModel.getIndeterminateColor();
+            setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor));
+        } else {
+            mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(),
+                    mProgressModel.getPoints(),
+                    mProgressModel.getProgress(), mProgressModel.isStyledByProgress());
+
+            try {
+                final NotificationProgressDrawable drawable = getNotificationProgressDrawable();
+                drawable.setParts(mProgressDrawableParts);
+            } catch (IllegalStateException ex) {
+                Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex);
+            }
+        }
     }
 
-    private void setProgressModel(@NonNull NotificationProgressModel model) {
-        mProgressModel = model;
+    @NonNull
+    private NotificationProgressDrawable getNotificationProgressDrawable() {
+        final Drawable d = getProgressDrawable();
+        if (d == null) {
+            throw new IllegalStateException("getProgressDrawable() returns null");
+        }
+        if (!(d instanceof LayerDrawable)) {
+            throw new IllegalStateException("getProgressDrawable() doesn't return a LayerDrawable");
+        }
+
+        final Drawable layer = ((LayerDrawable) d).findDrawableByLayerId(R.id.background);
+        if (!(layer instanceof NotificationProgressDrawable)) {
+            throw new IllegalStateException(
+                    "Couldn't get NotificationProgressDrawable, retrieved drawable is: " + (
+                            layer != null ? layer.toString() : null));
+        }
+
+        return (NotificationProgressDrawable) layer;
     }
 
     /**
@@ -97,7 +139,6 @@
     public void setProgressTrackerIcon(@Nullable Icon icon) {
     }
 
-
     /**
      * Async version of {@link #setProgressTrackerIcon}
      */
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 89ef875..2be7273 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -45,6 +45,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -156,11 +157,18 @@
     }
 
     /**
-     *
+     * Set the segments and points that constitute the drawable.
+     */
+    public void setParts(List<Part> parts) {
+        mParts.clear();
+        mParts.addAll(parts);
+    }
+
+    /**
+     * Set the segments and points that constitute the drawable.
      */
     public void setParts(@NonNull Part... parts) {
-        mParts.clear();
-        mParts.addAll(Arrays.asList(parts));
+        setParts(Arrays.asList(parts));
     }
 
     @Override
@@ -379,7 +387,7 @@
         if (state.mThemeAttrsPoints != null) {
             final TypedArray a = t.resolveAttributes(
                     state.mThemeAttrsPoints, R.styleable.NotificationProgressDrawablePoints);
-            updateSegmentsFromTypedArray(a);
+            updatePointsFromTypedArray(a);
             a.recycle();
         }
     }
@@ -651,9 +659,11 @@
 
         State(@NonNull State orig, @Nullable Resources res) {
             mChangingConfigurations = orig.mChangingConfigurations;
+            mSegSegGap = orig.mSegSegGap;
+            mSegPointGap = orig.mSegPointGap;
+            mStrokeWidth = orig.mStrokeWidth;
             mStrokeColor = orig.mStrokeColor;
             mFadedStrokeColor = orig.mFadedStrokeColor;
-            mStrokeWidth = orig.mStrokeWidth;
             mStrokeDashWidth = orig.mStrokeDashWidth;
             mStrokeDashGap = orig.mStrokeDashGap;
             mPointRadius = orig.mPointRadius;
@@ -791,6 +801,7 @@
         final State state = mState;
 
         mStrokePaint.setStrokeWidth(state.mStrokeWidth);
+        mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth);
 
         if (state.mStrokeDashWidth != 0.0f) {
             final DashPathEffect e = new DashPathEffect(
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
similarity index 60%
copy from packages/SystemUI/res/layout/udfps_bp_view.xml
copy to core/res/res/color-watch-v36/btn_material_filled_background_color.xml
index f1c55ef..8b2afa8 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ 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.
@@ -14,9 +13,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?attr/disabledAlpha"
+          android:color="?attr/materialColorOnSurface" />
+    <item android:state_enabled="true"
+          android:color="?attr/materialColorPrimary" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
new file mode 100644
index 0000000..cefc912
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?attr/primaryContentAlpha"
+          android:color="?attr/materialColorOnSurface" />
+    <item android:state_enabled="true"
+          android:color="?attr/materialColorOnPrimary" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
new file mode 100644
index 0000000..eaf9e7d
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?attr/disabledAlpha"
+          android:color="?attr/materialColorOnSurface" />
+    <item android:state_enabled="true"
+          android:color="?attr/materialColorSurfaceContainer" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
new file mode 100644
index 0000000..94e50fb
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?attr/primaryContentAlpha"
+          android:color="?attr/materialColorOnSurface" />
+    <item android:state_enabled="true"
+          android:color="?attr/materialColorOnSurface" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
new file mode 100644
index 0000000..0029de1
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?attr/colorControlHighlight">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/btn_material_filled_background_color"/>
+            <corners android:radius="?android:attr/buttonCornerRadius"/>
+            <size
+                android:width="@dimen/btn_material_width"
+                android:height="@dimen/btn_material_height" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
new file mode 100644
index 0000000..105f077
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?attr/colorControlHighlight">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+            <corners android:radius="?android:attr/buttonCornerRadius"/>
+            <size
+                android:width="@dimen/btn_material_width"
+                android:height="@dimen/btn_material_height" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml
new file mode 100644
index 0000000..3a6b600
--- /dev/null
+++ b/core/res/res/drawable/notification_progress.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@id/background"
+          android:gravity="center_vertical|fill_horizontal">
+        <com.android.internal.widget.NotificationProgressDrawable
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:segSegGap="@dimen/notification_progress_segSeg_gap"
+            android:segPointGap="@dimen/notification_progress_segPoint_gap">
+            <segments
+                android:color="?attr/colorProgressBackgroundNormal"
+                android:dashGap="@dimen/notification_progress_segments_dash_gap"
+                android:dashWidth="@dimen/notification_progress_segments_dash_width"
+                android:width="@dimen/notification_progress_segments_height" />
+            <points
+                android:color="?attr/colorProgressBackgroundNormal"
+                android:radius="@dimen/notification_progress_points_radius"
+                android:cornerRadius="@dimen/notification_progress_points_corner_radius"
+                android:inset="@dimen/notification_progress_points_inset" />
+        </com.android.internal.widget.NotificationProgressDrawable>
+    </item>
+</layer-list>
diff --git a/core/res/res/layout/notification_template_material_progress.xml b/core/res/res/layout/notification_template_material_progress.xml
index fdcefcc..75827a2 100644
--- a/core/res/res/layout/notification_template_material_progress.xml
+++ b/core/res/res/layout/notification_template_material_progress.xml
@@ -75,12 +75,11 @@
                         />
 
 
-                    <com.android.internal.widget.NotificationProgressBar
-                        android:id="@+id/progress"
+                    <include
                         android:layout_width="0dp"
-                        android:layout_height="@dimen/notification_progress_bar_height"
-                        style="@style/Widget.Material.Light.ProgressBar.Horizontal"
                         android:layout_weight="1"
+                        android:layout_height="@dimen/notification_progress_tracker_height"
+                        layout="@layout/notification_template_notification_progress_bar"
                         />
 
                     <com.android.internal.widget.CachingIconView
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/layout/notification_template_notification_progress_bar.xml
similarity index 61%
rename from packages/SystemUI/res/layout/udfps_bp_view.xml
rename to core/res/res/layout/notification_template_notification_progress_bar.xml
index f1c55ef..3574896 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/layout/notification_template_notification_progress_bar.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2014 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.
@@ -12,11 +12,12 @@
   ~ 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.
+  ~ limitations under the License
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
+
+<com.android.internal.widget.NotificationProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/progress"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+    android:layout_height="@dimen/notification_progress_tracker_height"
+    style="@style/Widget.Material.Notification.NotificationProgressBar"
+    />
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/values-watch-v36/colors.xml
similarity index 60%
copy from packages/SystemUI/res/layout/udfps_bp_view.xml
copy to core/res/res/values-watch-v36/colors.xml
index f1c55ef..4bc2a66 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/values-watch-v36/colors.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ 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.
@@ -14,9 +13,6 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+<!-- TODO(b/372524566): update color token's value to match material3 design. -->
+<resources>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/values-watch-v36/config.xml
similarity index 60%
copy from packages/SystemUI/res/layout/udfps_bp_view.xml
copy to core/res/res/values-watch-v36/config.xml
index f1c55ef..c8f347af 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/values-watch-v36/config.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ 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.
@@ -14,9 +13,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+
+<resources>
+    <!-- Overrides system value -->
+    <dimen name="config_buttonCornerRadius">26dp</dimen>
+</resources>
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
new file mode 100644
index 0000000..ad3c1a3
--- /dev/null
+++ b/core/res/res/values-watch-v36/dimens_material.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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.
+  -->
+<resources>
+    <!-- values for material3 button -->
+    <dimen name="btn_material_width">172dp</dimen>
+    <dimen name="btn_material_height">52dp</dimen>
+    <dimen name="btn_horizontal_edge_padding">14dp</dimen>
+    <dimen name="btn_drawable_padding">6dp</dimen>
+    <dimen name="btn_lineHeight">18sp</dimen>
+    <dimen name="btn_textSize">15sp</dimen>
+
+    <!-- Opacity factor for disabled material3 widget -->
+    <dimen name="disabled_alpha_device_default">0.12</dimen>
+    <dimen name="primary_content_alpha_device_default">0.38</dimen>
+</resources>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
new file mode 100644
index 0000000..32a22bb
--- /dev/null
+++ b/core/res/res/values-watch-v36/styles_material.xml
@@ -0,0 +1,58 @@
+<?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.
+  -->
+
+<resources>
+    <!--  Button Styles  -->
+    <!-- Material Button - Filled  -->
+    <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button">
+        <item name="android:background">@drawable/btn_background_material_filled</item>
+        <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item>
+    </style>
+
+    <!-- Material Button - Filled Tonal(Override system default button styles) -->
+    <style name="Widget.DeviceDefault.Button">
+        <item name="background">@drawable/btn_background_material_filled_tonal</item>
+        <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item>
+        <item name="minHeight">@dimen/btn_material_height</item>
+        <item name="maxWidth">@dimen/btn_material_width</item>
+        <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item>
+        <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item>
+        <item name="android:drawablePadding">@dimen/btn_drawable_padding</item>
+        <item name="android:maxLines">2</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:breakStrategy">simple</item>
+        <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
+        <item name="focusable">true</item>
+        <item name="clickable">true</item>
+        <item name="gravity">center_vertical</item>
+    </style>
+
+    <!--  Text Styles  -->
+    <!-- TextAppearance for Material Button - Filled  -->
+    <style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material">
+        <item name="textColor">@color/btn_material_filled_text_color</item>
+    </style>
+
+    <!-- TextAppearance for Material Button - Filled Tonal  -->
+    <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault">
+        <item name="android:fontFamily">font-family-flex-device-default</item>
+        <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
+        <item name="textSize">@dimen/btn_textSize</item>
+        <item name="textColor">@color/btn_material_filled_tonal_text_color</item>
+        <item name="lineHeight">@dimen/btn_lineHeight</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8a2d767..91b4820 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -287,6 +287,11 @@
          vibration params. -->
     <integer name="config_requestVibrationParamsTimeout">50</integer>
 
+    <!-- The max duration (in milliseconds) that the vibrator service will allow effects to be
+         pipelined (i.e. service will wait for ongoing vibration to finish instead of cancelling it
+         to start the new one). Value should be positive. Zero will disable effect pipelining. -->
+    <integer name="config_vibrationPipelineMaxDuration">25</integer>
+
     <!-- Array containing the usages that should request vibration params before they are played.
          These usages don't have strong latency requirements, e.g. ringtone and notification, and
          can be slightly delayed. -->
@@ -4447,17 +4452,25 @@
     <string-array translatable="false" name="config_defaultPinnerServiceFiles">
     </string-array>
 
-    <!-- True if camera app should be pinned via Pinner Service -->
+    <!-- Note: This config is deprecated, use config_pinnerCameraPinBytes instead.
+         True if camera app should be pinned via Pinner Service -->
     <bool name="config_pinnerCameraApp">false</bool>
 
-    <!-- Bytes that the PinnerService will pin for Home app -->
-    <integer name="config_pinnerHomePinBytes">0</integer>
+    <!-- Default: 60 MB. Bytes that the PinnerService will pin for Home app -->
+    <integer name="config_pinnerHomePinBytes">62914560</integer>
 
-    <!-- True if assistant app should be pinned via Pinner Service -->
+    <!-- Default: 80 MB. Bytes that the PinnerService will pin for Camera app -->
+    <integer name="config_pinnerCameraPinBytes">83886080</integer>
+
+    <!-- Note: This config is deprecated, use config_pinnerAssistantPinBytes instead.
+         True if assistant app should be pinned via Pinner Service -->
     <bool name="config_pinnerAssistantApp">false</bool>
 
-    <!-- Bytes that the PinnerService will pin for WebView -->
-    <integer name="config_pinnerWebviewPinBytes">0</integer>
+     <!-- Default: 60 MB. Bytes that the PinnerService will pin for Assistant -->
+    <integer name="config_pinnerAssistantPinBytes">62914560</integer>
+
+    <!-- Default: 20 MB. Bytes that the PinnerService will pin for WebView -->
+    <integer name="config_pinnerWebviewPinBytes">20971520</integer>
 
     <!-- Maximum memory that PinnerService will pin for apps expressed
          as a percentage of total device memory [0,100].
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b92aa2f..7184d9a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -817,6 +817,22 @@
     <dimen name="notification_progress_tracker_width">40dp</dimen>
     <!-- The size of the progress tracker height -->
     <dimen name="notification_progress_tracker_height">20dp</dimen>
+    <!-- The gap between segments in the notification progress bar -->
+    <dimen name="notification_progress_segSeg_gap">2dp</dimen>
+    <!-- The gap between a segment and a point in the notification progress bar -->
+    <dimen name="notification_progress_segPoint_gap">4dp</dimen>
+    <!-- The dash gap of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_dash_gap">9dp</dimen>
+    <!-- The dash width of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_dash_width">3dp</dimen>
+    <!-- The height of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_height">6dp</dimen>
+    <!-- The radius of the notification progress bar points -->
+    <dimen name="notification_progress_points_radius">10dp</dimen>
+    <!-- The corner radius of the notification progress bar points drawn as rects -->
+    <dimen name="notification_progress_points_corner_radius">4dp</dimen>
+    <!-- The inset of the notification progress bar points drawn as rects -->
+    <dimen name="notification_progress_points_inset">0dp</dimen>
 
     <!-- The maximum size of the small notification icon on low memory devices. -->
     <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index eec6ae3..cb8e4aa 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -502,6 +502,10 @@
 
     <style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
 
+    <style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
+        <item name="progressDrawable">@drawable/notification_progress</item>
+    </style>
+
     <style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
         <item name="lineHeight">20sp</item>
         <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4f63fac..0348b46 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2141,6 +2141,7 @@
   <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
   <java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />
   <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
+  <java-symbol type="integer" name="config_vibrationPipelineMaxDuration" />
   <java-symbol type="integer" name="config_radioScanningTimeout" />
   <java-symbol type="integer" name="config_requestVibrationParamsTimeout" />
   <java-symbol type="array" name="config_requestVibrationParamsForUsages" />
@@ -3471,6 +3472,8 @@
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
   <java-symbol type="bool" name="config_pinnerCameraApp" />
   <java-symbol type="integer" name="config_pinnerHomePinBytes" />
+  <java-symbol type="integer" name="config_pinnerCameraPinBytes" />
+  <java-symbol type="integer" name="config_pinnerAssistantPinBytes" />
   <java-symbol type="bool" name="config_pinnerAssistantApp" />
   <java-symbol type="integer" name="config_pinnerWebviewPinBytes" />
   <java-symbol type="integer" name="config_pinnerMaxPinnedMemoryPercentage" />
@@ -3864,6 +3867,14 @@
   <java-symbol type="dimen" name="notification_progress_icon_size" />
   <java-symbol type="dimen" name="notification_progress_tracker_width" />
   <java-symbol type="dimen" name="notification_progress_tracker_height" />
+  <java-symbol type="dimen" name="notification_progress_segSeg_gap" />
+  <java-symbol type="dimen" name="notification_progress_segPoint_gap" />
+  <java-symbol type="dimen" name="notification_progress_segments_dash_gap" />
+  <java-symbol type="dimen" name="notification_progress_segments_dash_width" />
+  <java-symbol type="dimen" name="notification_progress_segments_height" />
+  <java-symbol type="dimen" name="notification_progress_points_radius" />
+  <java-symbol type="dimen" name="notification_progress_points_corner_radius" />
+  <java-symbol type="dimen" name="notification_progress_points_inset" />
 
   <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/>
   <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index 5636f9b..6d14ccb 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -14,3 +14,5 @@
 # Files related to background activity launches
 per-file Background*Start* = file:/BAL_OWNERS
 
+# Files related to caching
+per-file PropertyInvalidatedCache* = file:/PERFORMANCE_OWNERS
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index b5ee130..228647a 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
@@ -26,6 +27,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -90,11 +92,10 @@
         }
     }
 
-    // Clear the test mode after every test, in case this process is used for other
-    // tests. This also resets the test property map.
+    // Ensure all test nonces are cleared after the test ends.
     @After
     public void tearDown() throws Exception {
-        PropertyInvalidatedCache.setTestMode(false);
+        PropertyInvalidatedCache.resetAfterTest();
     }
 
     // This test is disabled pending an sepolicy change that allows any app to set the
@@ -111,9 +112,6 @@
                 new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
                         new ServerQuery(tester));
 
-        PropertyInvalidatedCache.setTestMode(true);
-        testCache.testPropertyName();
-
         tester.verify(0);
         assertEquals(tester.value(3), testCache.query(3));
         tester.verify(1);
@@ -223,22 +221,16 @@
 
         TestCache(String module, String api) {
             this(module, api, new TestQuery());
-            setTestMode(true);
-            testPropertyName();
         }
 
         TestCache(String module, String api, TestQuery query) {
             super(4, module, api, api, query);
             mQuery = query;
-            setTestMode(true);
-            testPropertyName();
         }
 
         public int getRecomputeCount() {
             return mQuery.getRecomputeCount();
         }
-
-
     }
 
     @Test
@@ -375,4 +367,18 @@
             PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState");
         assertEquals(n1, "cache_key.bluetooth.get_state");
     }
+
+    // It is illegal to continue to use a cache with a test key after calling setTestMode(false).
+    // This test verifies the code detects errors in calling setTestMode().
+    @Test
+    public void testTestMode() {
+        TestCache cache = new TestCache();
+        cache.invalidateCache();
+        PropertyInvalidatedCache.resetAfterTest();
+        try {
+            cache.invalidateCache();
+            fail("expected an IllegalStateException");
+        } catch (IllegalStateException expected) {
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index 64f77b3..5852bee 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 import android.multiuser.Flags;
 import android.platform.test.annotations.IgnoreUnderRavenwood;
@@ -26,6 +27,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -92,17 +94,17 @@
         public Boolean apply(Integer x) {
             return mServer.query(x);
         }
+
         @Override
         public boolean shouldBypassCache(Integer x) {
             return x % 13 == 0;
         }
     }
 
-    // Clear the test mode after every test, in case this process is used for other
-    // tests. This also resets the test property map.
+    // Ensure all test nonces are cleared after the test ends.
     @After
     public void tearDown() throws Exception {
-        IpcDataCache.setTestMode(false);
+        IpcDataCache.resetAfterTest();
     }
 
     // This test is disabled pending an sepolicy change that allows any app to set the
@@ -119,9 +121,6 @@
                 new IpcDataCache<>(4, MODULE, API, "testCache1",
                         new ServerQuery(tester));
 
-        IpcDataCache.setTestMode(true);
-        testCache.testPropertyName();
-
         tester.verify(0);
         assertEquals(tester.value(3), testCache.query(3));
         tester.verify(1);
@@ -165,9 +164,6 @@
         IpcDataCache<Integer, Boolean> testCache =
                 new IpcDataCache<>(config, (x) -> tester.query(x, x % 10 == 9));
 
-        IpcDataCache.setTestMode(true);
-        testCache.testPropertyName();
-
         tester.verify(0);
         assertEquals(tester.value(3), testCache.query(3));
         tester.verify(1);
@@ -205,9 +201,6 @@
         IpcDataCache<Integer, Boolean> testCache =
                 new IpcDataCache<>(config, (x) -> tester.query(x), (x) -> x % 9 == 0);
 
-        IpcDataCache.setTestMode(true);
-        testCache.testPropertyName();
-
         tester.verify(0);
         assertEquals(tester.value(3), testCache.query(3));
         tester.verify(1);
@@ -313,8 +306,6 @@
         TestCache(String module, String api, TestQuery query) {
             super(4, module, api, "testCache7", query);
             mQuery = query;
-            setTestMode(true);
-            testPropertyName();
         }
 
         TestCache(IpcDataCache.Config c) {
@@ -324,8 +315,6 @@
         TestCache(IpcDataCache.Config c, TestQuery query) {
             super(c, query);
             mQuery = query;
-            setTestMode(true);
-            testPropertyName();
         }
 
         int getRecomputeCount() {
@@ -456,4 +445,18 @@
         TestCache ec = new TestCache(e);
         assertEquals(ec.isDisabled(), true);
     }
+
+    // It is illegal to continue to use a cache with a test key after calling setTestMode(false).
+    // This test verifies the code detects errors in calling setTestMode().
+    @Test
+    public void testTestMode() {
+        TestCache cache = new TestCache();
+        cache.invalidateCache();
+        IpcDataCache.resetAfterTest();
+        try {
+            cache.invalidateCache();
+            fail("expected an IllegalStateException");
+        } catch (IllegalStateException expected) {
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 1c00734..6149382 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -9,3 +9,6 @@
 
 # PerformanceHintManager
 per-file PerformanceHintManagerTest.java = file:/ADPF_OWNERS
+
+# Caching
+per-file IpcDataCache* = file:/PERFORMANCE_OWNERS
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 25f9cb7..6311298 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -42,6 +42,7 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StrikethroughSpan;
 
+import androidx.annotation.NonNull;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -697,7 +698,7 @@
 
             if (drawCommand.path != null) {
                 expect.that(drawCommand.path).isEqualTo(selectionPath);
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW);
                 expect.that(drawCommand.paint.getBlendMode()).isNotNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
@@ -750,7 +751,7 @@
 
             if (drawCommand.path != null) {
                 expect.that(drawCommand.path).isEqualTo(selectionPath);
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW);
                 expect.that(drawCommand.paint.getBlendMode()).isNotNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
@@ -802,7 +803,7 @@
 
             if (drawCommand.path != null) {
                 expect.that(drawCommand.path).isEqualTo(selectionPath);
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN);
                 expect.that(drawCommand.paint.getBlendMode()).isNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
@@ -855,7 +856,7 @@
 
             if (drawCommand.path != null) {
                 expect.that(drawCommand.path).isEqualTo(selectionPath);
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN);
                 expect.that(drawCommand.paint.getBlendMode()).isNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
@@ -914,7 +915,7 @@
 
             if (drawCommand.rect != null) {
                 numBackgroundsFound++;
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.BLACK);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.BLACK);
                 expect.that(drawCommand.rect.height()).isAtLeast(LINE_HEIGHT);
                 expect.that(drawCommand.rect.width()).isGreaterThan(0);
                 float expectedY = (numBackgroundsFound) * (LINE_HEIGHT + LINE_DESCENT);
@@ -997,20 +998,38 @@
                 .filter(it -> it.rect != null)
                 .toList();
 
-        expect.that(backgroundCommands.get(0).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(1).paint.getColor()).isEqualTo(Color.WHITE);
-        expect.that(backgroundCommands.get(2).paint.getColor()).isEqualTo(Color.WHITE);
-        expect.that(backgroundCommands.get(3).paint.getColor()).isEqualTo(Color.WHITE);
-        expect.that(backgroundCommands.get(4).paint.getColor()).isEqualTo(Color.WHITE);
-        expect.that(backgroundCommands.get(5).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(6).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(7).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(8).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(9).paint.getColor()).isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(0).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(1).paint.getColor()))
+                .isEqualTo(Color.WHITE);
+        expect.that(removeAlpha(backgroundCommands.get(2).paint.getColor()))
+                .isEqualTo(Color.WHITE);
+        expect.that(removeAlpha(backgroundCommands.get(3).paint.getColor()))
+                .isEqualTo(Color.WHITE);
+        expect.that(removeAlpha(backgroundCommands.get(4).paint.getColor()))
+                .isEqualTo(Color.WHITE);
+        expect.that(removeAlpha(backgroundCommands.get(5).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(6).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(7).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(8).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(9).paint.getColor()))
+                .isEqualTo(Color.BLACK);
 
         expect.that(backgroundCommands.size()).isEqualTo(backgroundRectsDrawn);
     }
 
+    private int removeAlpha(int color) {
+        return Color.rgb(
+                Color.red(color),
+                Color.green(color),
+                Color.blue(color)
+        );
+    }
+
     private static final class MockCanvas extends Canvas {
 
         static class DrawCommand {
@@ -1122,6 +1141,11 @@
             mDrawCommands.add(new DrawCommand(rect, p));
         }
 
+        @Override
+        public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
+            mDrawCommands.add(new DrawCommand(rect, paint));
+        }
+
         List<DrawCommand> getDrawCommands() {
             return mDrawCommands;
         }
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 6419c1e0..79a478a 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -254,7 +254,6 @@
 
         // Colors with 50% opacity
         int fadedGreen = 0x7F00FF00;
-        int fadedBlue = 0x7F0000FF;
         int fadedYellow = 0x7FFFFF00;
 
         List<Part> expected = new ArrayList<>(List.of(
diff --git a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
index 244fcff..37ddfd2 100644
--- a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
@@ -22,6 +22,9 @@
 
 import static org.testng.Assert.assertThrows;
 
+import android.hardware.vibrator.IVibrator;
+import android.util.SparseArray;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -134,6 +137,54 @@
     }
 
     @Test
+    public void testDurationMono_withVibratorSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // Use max duration from all vibrators.
+        assertEquals(10, CombinedVibration.createParallel(
+                VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos));
+        assertEquals(111, CombinedVibration.createParallel(
+                        VibrationEffect.startComposition()
+                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                                .compose())
+                .getDuration(infos));
+    }
+
+    @Test
+    public void testDurationMono_withVibratorNotSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // Use max duration from all vibrators.
+        assertEquals(-1, CombinedVibration.createParallel(
+                VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos));
+        assertEquals(-1, CombinedVibration.createParallel(
+                        VibrationEffect.startComposition()
+                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                                .compose())
+                .getDuration(infos));
+    }
+
+    @Test
     public void testDurationStereo() {
         assertEquals(6, CombinedVibration.startParallel()
                 .addVibrator(1, VibrationEffect.createOneShot(1, 1))
@@ -156,6 +207,75 @@
     }
 
     @Test
+    public void testDurationStereo_withVibratorSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // Use specific vibrator durations, then max effect duration
+        assertEquals(111, CombinedVibration.startParallel()
+                .addVibrator(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .addVibrator(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+        assertEquals(110, CombinedVibration.startParallel()
+                .addVibrator(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+    }
+
+    @Test
+    public void testDurationStereo_withVibratorNotSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // One vibrator does not support primitives
+        assertEquals(-1, CombinedVibration.startParallel()
+                .addVibrator(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .addVibrator(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+        // Invalid vibrator ID
+        assertEquals(-1, CombinedVibration.startParallel()
+                .addVibrator(3, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+    }
+
+    @Test
     public void testDurationSequential() {
         assertEquals(26, CombinedVibration.startSequential()
                 .addNext(1, VibrationEffect.createOneShot(10, 10), 10)
@@ -178,6 +298,59 @@
     }
 
     @Test
+    public void testDurationSequential_withVibratorSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // Add each duration and delay
+        assertEquals(321, CombinedVibration.startSequential()
+                .addNext(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose(), 100)
+                .addNext(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+    }
+
+    @Test
+    public void testDurationSequential_withVibratorNotSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        assertEquals(-1, CombinedVibration.startSequential()
+                .addNext(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose(), 100)
+                .addNext(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+    }
+
+    @Test
     public void testIsHapticFeedbackCandidateMono() {
         assertTrue(CombinedVibration.createParallel(
                 VibrationEffect.createOneShot(1, 1)).isHapticFeedbackCandidate());
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index f5b04ee..8acf2ed 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -1078,6 +1078,52 @@
     }
 
     @Test
+    public void testDuration_withVibratorSupportingPrimitives() {
+        VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+                .build();
+
+        VibrationEffect composition = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                .compose();
+
+        assertEquals(1, VibrationEffect.createOneShot(1, 1).getDuration());
+        assertEquals(10, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info));
+        assertEquals(115, composition.getDuration(info));
+        assertEquals(Long.MAX_VALUE,
+                VibrationEffect.startComposition()
+                        .repeatEffectIndefinitely(composition)
+                        .compose()
+                        .getDuration(info));
+        if (Flags.vendorVibrationEffects()) {
+            assertEquals(-1,
+                    VibrationEffect.createVendorEffect(createNonEmptyBundle()).getDuration(info));
+        }
+    }
+
+    @Test
+    public void testDuration_withVibratorNotSupportingPrimitives() {
+        VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build();
+
+        VibrationEffect composition = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .compose();
+
+        assertEquals(-1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info));
+        assertEquals(-1, composition.getDuration(info));
+        assertEquals(Long.MAX_VALUE,
+                VibrationEffect.startComposition()
+                        .repeatEffectIndefinitely(composition)
+                        .compose()
+                        .getDuration(info));
+    }
+
+    @Test
     public void testAreVibrationFeaturesSupported_allSegmentsSupported() {
         VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 7dd9e55..f9ec5f0 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -24,6 +24,7 @@
 import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertThrows;
 
+import android.hardware.vibrator.IVibrator;
 import android.os.Parcel;
 import android.os.VibrationEffect;
 import android.os.VibratorInfo;
@@ -114,39 +115,82 @@
 
     @Test
     public void testDuration() {
-        assertEquals(-1, new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
-                .getDuration());
-        assertEquals(-1, new PrebakedSegment(
-                VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
-                .getDuration());
-        assertEquals(-1, new PrebakedSegment(
-                VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
-                .getDuration());
-        assertEquals(-1, new PrebakedSegment(
-                VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK).getDuration());
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK).getDuration());
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD).getDuration());
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
                 .getDuration());
     }
 
     @Test
+    public void testDuration_withVibratorSupportingPrimitives_returnsPrimitiveDuration() {
+        int tickDuration = 5;
+        int clickDuration = 10;
+        int thudDuration = 15;
+
+        VibratorInfo vibratorInfo = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, tickDuration)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, clickDuration)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, thudDuration)
+                .build();
+
+        assertEquals(5, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+                .getDuration(vibratorInfo));
+        assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
+                .getDuration(vibratorInfo));
+        assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(20, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(15, createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
+                .getDuration(vibratorInfo));
+
+        // Unknown effects
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0])
+                .getDuration(vibratorInfo));
+    }
+
+    @Test
+    public void testDuration_withVibratorNotSupportingPrimitives_returnsUnknown() {
+        VibratorInfo vibratorInfo = createVibratorInfoWithSupportedEffects(
+                VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_POP);
+
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0])
+                .getDuration(vibratorInfo));
+    }
+
+    @Test
     public void testIsHapticFeedbackCandidate_prebakedConstants_areCandidates() {
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_HEAVY_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_TEXTURE_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
                 .isHapticFeedbackCandidate());
     }
 
@@ -271,8 +315,7 @@
 
     @Test
     public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() {
-        assertFalse(new PrebakedSegment(
-                VibrationEffect.RINGTONES[1], true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertFalse(createSegmentWithFallback(VibrationEffect.RINGTONES[1])
                 .isHapticFeedbackCandidate());
     }
 
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index 97f1d5e..a6d9dc5 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -201,6 +201,22 @@
     }
 
     @Test
+    public void testDuration_withVibratorSupportingPrimitives_returnsVibratorDurationWithDelay() {
+        VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_CLICK, /* durationMs= */ 10);
+        assertEquals(15, new PrimitiveSegment(
+                VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 5).getDuration(vibratorInfo));
+    }
+
+    @Test
+    public void testDuration_withVibratorNotSupportingPrimitive_returnsUnknown() {
+        VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_CLICK);
+        assertEquals(-1, new PrimitiveSegment(
+                VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 5).getDuration(vibratorInfo));
+    }
+
+    @Test
     public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() {
         assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
                 .areVibrationFeaturesSupported(
@@ -252,9 +268,14 @@
     }
 
     private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId) {
+        return createVibratorInfoWithSupportedPrimitive(primitiveId, /* durationMs= */ 10);
+    }
+
+    private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId,
+            int durationMs) {
         return new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
-                .setSupportedPrimitive(primitiveId, 10)
+                .setSupportedPrimitive(primitiveId, durationMs)
                 .build();
     }
 }
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index bea8293..df874bcb 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -195,7 +195,14 @@
 
     @Test
     public void testDuration() {
+        VibratorInfo infoWithSupport =
+                createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
+        VibratorInfo infoWithoutSupport =
+                createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ false);
+
         assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration());
+        assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithSupport));
+        assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithoutSupport));
     }
 
     @Test
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 411074a..914117c 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -213,7 +213,13 @@
 
     @Test
     public void testDuration() {
+        VibratorInfo infoWithSupport = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
+        VibratorInfo infoWithoutSupport =
+                createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
+
         assertEquals(5, new StepSegment(0, 0, 5).getDuration());
+        assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithSupport));
+        assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithoutSupport));
     }
 
     @Test
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index c1d2235..b559a15 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -22,6 +22,7 @@
 
     static_libs: [
         "annotations",
+        "jsr305",
         "framework-annotations-lib",
         "//external/error_prone:error_prone_core",
     ],
diff --git a/errorprone/OWNERS b/errorprone/OWNERS
index bddbdb3..aa8c126 100644
--- a/errorprone/OWNERS
+++ b/errorprone/OWNERS
@@ -1,2 +1 @@
-jsharkey@android.com
-jsharkey@google.com
+colefaust@google.com
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
index 8dc9579..6d5e4484 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
@@ -29,6 +29,7 @@
 import com.google.errorprone.fixes.SuggestedFix;
 import com.google.errorprone.matchers.Description;
 import com.google.errorprone.util.ASTHelpers;
+import com.google.errorprone.util.ErrorProneComment;
 import com.google.errorprone.util.ErrorProneToken;
 import com.google.errorprone.util.ErrorProneTokens;
 import com.sun.source.tree.ClassTree;
@@ -37,7 +38,6 @@
 import com.sun.source.tree.NewClassTree;
 import com.sun.source.tree.Tree;
 import com.sun.source.tree.VariableTree;
-import com.sun.tools.javac.parser.Tokens;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -66,7 +66,7 @@
         final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state);
         final String sourceCode = state.getSourceCode().toString();
         for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) {
-            for (Tokens.Comment comment : token.comments()) {
+            for (ErrorProneComment comment : token.comments()) {
                 if (!javadocableTrees.containsKey(token.pos())) {
                     continue;
                 }
@@ -81,7 +81,7 @@
         return NO_MATCH;
     }
 
-    private static Optional<SuggestedFix> generateFix(Tokens.Comment comment) {
+    private static Optional<SuggestedFix> generateFix(ErrorProneComment comment) {
         final String text = comment.getText();
         if (text.startsWith("/**")) {
             return Optional.empty();
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 96ffa03..52ce8cb 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -67,6 +67,7 @@
 
     private val context = ApplicationProvider.getApplicationContext<Context>()
     private lateinit var positioner: BubblePositioner
+    private lateinit var bubbleLogger: BubbleLogger
     private lateinit var iconFactory: BubbleIconFactory
     private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
     private lateinit var bubbleStackView: BubbleStackView
@@ -96,10 +97,11 @@
                 )
             )
         positioner = BubblePositioner(context, windowManager)
+        bubbleLogger = BubbleLogger(UiEventLoggerFake())
         bubbleData =
             BubbleData(
                 context,
-                BubbleLogger(UiEventLoggerFake()),
+                bubbleLogger,
                 positioner,
                 BubbleEducationController(context),
                 shellExecutor,
@@ -394,6 +396,7 @@
             expandedViewManager,
             bubbleTaskViewFactory,
             positioner,
+            bubbleLogger,
             bubbleStackView,
             null,
             iconFactory,
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 9fdde128..712cc7c 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -29,6 +29,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R
+import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.protolog.ProtoLog
 import com.android.internal.statusbar.IStatusBarService
 import com.android.launcher3.icons.BubbleIconFactory
@@ -70,6 +71,7 @@
     private lateinit var bgExecutor: TestExecutor
     private lateinit var bubbleStackView: BubbleStackView
     private lateinit var bubblePositioner: BubblePositioner
+    private lateinit var bubbleLogger: BubbleLogger
     private lateinit var expandedViewManager: BubbleExpandedViewManager
 
     private val bubbleTaskViewFactory = BubbleTaskViewFactory {
@@ -103,10 +105,11 @@
                 mainExecutor
             )
         bubblePositioner = BubblePositioner(context, windowManager)
+        bubbleLogger = BubbleLogger(UiEventLoggerFake())
         val bubbleData =
             BubbleData(
                 context,
-                mock<BubbleLogger>(),
+                bubbleLogger,
                 bubblePositioner,
                 BubbleEducationController(context),
                 mainExecutor,
@@ -138,7 +141,7 @@
                 WindowManagerShellWrapper(mainExecutor),
                 mock<UserManager>(),
                 mock<LauncherApps>(),
-                mock<BubbleLogger>(),
+                bubbleLogger,
                 mock<TaskStackListenerImpl>(),
                 mock<ShellTaskOrganizer>(),
                 bubblePositioner,
@@ -314,6 +317,7 @@
             expandedViewManager,
             bubbleTaskViewFactory,
             bubblePositioner,
+            bubbleLogger,
             bubbleStackView,
             null /* layerView */,
             iconFactory,
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
index 35d459f..f181ce0 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -27,11 +27,13 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.Bubble
 import com.android.wm.shell.bubbles.BubbleData
 import com.android.wm.shell.bubbles.BubbleExpandedViewManager
+import com.android.wm.shell.bubbles.BubbleLogger
 import com.android.wm.shell.bubbles.BubblePositioner
 import com.android.wm.shell.bubbles.BubbleTaskView
 import com.android.wm.shell.bubbles.BubbleTaskViewFactory
@@ -106,11 +108,12 @@
         bubbleExpandedView.initialize(
             expandedViewManager,
             positioner,
+            BubbleLogger(UiEventLoggerFake()),
             false /* isOverflow */,
             bubbleTaskView,
             mainExecutor,
             bgExecutor,
-            regionSamplingProvider
+            regionSamplingProvider,
         )
 
         getInstrumentation().runOnMainSync(Runnable {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e3fc5c2..e8e25e20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -572,6 +572,7 @@
      * @param expandedViewManager the bubble expanded view manager.
      * @param taskViewFactory the task view factory used to create the task view for the bubble.
      * @param positioner the bubble positioner.
+     * @param bubbleLogger log bubble metrics.
      * @param stackView the view the bubble is added to, iff showing as floating.
      * @param layerView the layer the bubble is added to, iff showing in the bubble bar.
      * @param iconFactory the icon factory used to create images for the bubble.
@@ -581,6 +582,7 @@
             BubbleExpandedViewManager expandedViewManager,
             BubbleTaskViewFactory taskViewFactory,
             BubblePositioner positioner,
+            BubbleLogger bubbleLogger,
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory iconFactory,
@@ -595,6 +597,7 @@
                     expandedViewManager,
                     taskViewFactory,
                     positioner,
+                    bubbleLogger,
                     stackView,
                     layerView,
                     iconFactory,
@@ -616,6 +619,7 @@
                     expandedViewManager,
                     taskViewFactory,
                     positioner,
+                    bubbleLogger,
                     stackView,
                     layerView,
                     iconFactory,
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 eb9cdab..37e8ead 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
@@ -892,7 +892,7 @@
             registerBroadcastReceiver();
             if (isShowingAsBubbleBar()) {
                 mBubbleData.getOverflow().initializeForBubbleBar(
-                        mExpandedViewManager, mBubblePositioner);
+                        mExpandedViewManager, mBubblePositioner, mLogger);
             } else {
                 mBubbleData.getOverflow().initialize(
                         mExpandedViewManager, mStackView, mBubblePositioner);
@@ -1100,6 +1100,7 @@
                     mExpandedViewManager,
                     mBubbleTaskViewFactory,
                     mBubblePositioner,
+                    mLogger,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -1111,6 +1112,7 @@
                     mExpandedViewManager,
                     mBubbleTaskViewFactory,
                     mBubblePositioner,
+                    mLogger,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -1585,6 +1587,7 @@
                         mExpandedViewManager,
                         mBubbleTaskViewFactory,
                         mBubblePositioner,
+                        mLogger,
                         mStackView,
                         mLayerView,
                         mBubbleIconFactory,
@@ -1647,6 +1650,7 @@
                     mExpandedViewManager,
                     mBubbleTaskViewFactory,
                     mBubblePositioner,
+                    mLogger,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -1727,6 +1731,7 @@
                 mExpandedViewManager,
                 mBubbleTaskViewFactory,
                 mBubblePositioner,
+                mLogger,
                 mStackView,
                 mLayerView,
                 mBubbleIconFactory,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 709a7bd..4de9dfa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -495,7 +495,7 @@
     /**
      * When this method is called it is expected that all info in the bubble has completed loading.
      * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager,
-     * BubbleTaskViewFactory, BubblePositioner, BubbleStackView,
+     * BubbleTaskViewFactory, BubblePositioner, BubbleLogger, BubbleStackView,
      * com.android.wm.shell.bubbles.bar.BubbleBarLayerView,
      * com.android.launcher3.icons.BubbleIconFactory, boolean)
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 68c4657..c74412b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -73,17 +73,19 @@
 
     fun initializeForBubbleBar(
         expandedViewManager: BubbleExpandedViewManager,
-        positioner: BubblePositioner
+        positioner: BubblePositioner,
+        bubbleLogger: BubbleLogger,
     ) {
         createBubbleBarExpandedView()
             .initialize(
                 expandedViewManager,
                 positioner,
+                bubbleLogger,
                 /* isOverflow= */ true,
                 /* bubbleTaskView= */ null,
                 /* mainExecutor= */ null,
                 /* backgroundExecutor= */ null,
-                /* regionSamplingProvider= */ null
+                /* regionSamplingProvider= */ null,
             )
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 39fb2f49..96b6043 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -73,6 +73,7 @@
     private final WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
     private final WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
     private final WeakReference<BubblePositioner> mPositioner;
+    private final WeakReference<BubbleLogger> mBubbleLogger;
     private final WeakReference<BubbleStackView> mStackView;
     private final WeakReference<BubbleBarLayerView> mLayerView;
     private final BubbleIconFactory mIconFactory;
@@ -94,6 +95,7 @@
             BubbleExpandedViewManager expandedViewManager,
             BubbleTaskViewFactory taskViewFactory,
             BubblePositioner positioner,
+            BubbleLogger bubbleLogger,
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory factory,
@@ -106,6 +108,7 @@
         mExpandedViewManager = new WeakReference<>(expandedViewManager);
         mTaskViewFactory = new WeakReference<>(taskViewFactory);
         mPositioner = new WeakReference<>(positioner);
+        mBubbleLogger = new WeakReference<>(bubbleLogger);
         mStackView = new WeakReference<>(stackView);
         mLayerView = new WeakReference<>(layerView);
         mIconFactory = factory;
@@ -221,8 +224,9 @@
                 ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s",
                         mBubble.getKey());
                 viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(),
-                        mPositioner.get(), false /* isOverflow */, viewInfo.taskView,
-                        mMainExecutor, mBgExecutor, new RegionSamplingProvider() {
+                        mPositioner.get(), mBubbleLogger.get(), false /* isOverflow */,
+                        viewInfo.taskView, mMainExecutor, mBgExecutor,
+                        new RegionSamplingProvider() {
                             @Override
                             public RegionSamplingHelper createHelper(View sampledView,
                                     RegionSamplingHelper.SamplingCallback callback,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
index e9a5933..c1da94c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -78,6 +78,7 @@
     private WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
     private WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
     private WeakReference<BubblePositioner> mPositioner;
+    private WeakReference<BubbleLogger>  mBubbleLogger;
     private WeakReference<BubbleStackView> mStackView;
     private WeakReference<BubbleBarLayerView> mLayerView;
     private BubbleIconFactory mIconFactory;
@@ -95,6 +96,7 @@
             BubbleExpandedViewManager expandedViewManager,
             BubbleTaskViewFactory taskViewFactory,
             BubblePositioner positioner,
+            BubbleLogger bubbleLogger,
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory factory,
@@ -107,6 +109,7 @@
         mExpandedViewManager = new WeakReference<>(expandedViewManager);
         mTaskViewFactory = new WeakReference<>(taskViewFactory);
         mPositioner = new WeakReference<>(positioner);
+        mBubbleLogger = new WeakReference<>(bubbleLogger);
         mStackView = new WeakReference<>(stackView);
         mLayerView = new WeakReference<>(layerView);
         mIconFactory = factory;
@@ -124,8 +127,9 @@
         }
         if (mLayerView.get() != null) {
             return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
-                    mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
-                    mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor);
+                    mTaskViewFactory.get(), mPositioner.get(), mBubbleLogger.get(),
+                    mLayerView.get(), mIconFactory, mBubble, mSkipInflation, mMainExecutor,
+                    mBackgroundExecutor);
         } else {
             return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
                     mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
@@ -187,6 +191,7 @@
                 BubbleExpandedViewManager expandedViewManager,
                 BubbleTaskViewFactory taskViewFactory,
                 BubblePositioner positioner,
+                BubbleLogger bubbleLogger,
                 BubbleBarLayerView layerView,
                 BubbleIconFactory iconFactory,
                 Bubble b,
@@ -200,9 +205,9 @@
                 LayoutInflater inflater = LayoutInflater.from(c);
                 info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
                         R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
-                info.bubbleBarExpandedView.initialize(
-                        expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView,
-                        mainExecutor, backgroundExecutor, new RegionSamplingProvider() {
+                info.bubbleBarExpandedView.initialize(expandedViewManager, positioner, bubbleLogger,
+                        false /* isOverflow */, bubbleTaskView, mainExecutor, backgroundExecutor,
+                        new RegionSamplingProvider() {
                             @Override
                             public RegionSamplingHelper createHelper(View sampledView,
                                     RegionSamplingHelper.SamplingCallback callback,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 2a90017..84405bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -39,6 +39,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
+import com.android.wm.shell.bubbles.BubbleLogger;
 import com.android.wm.shell.bubbles.BubbleOverflowContainerView;
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleTaskView;
@@ -90,6 +91,7 @@
     private Bubble mBubble;
     private BubbleExpandedViewManager mManager;
     private BubblePositioner mPositioner;
+    private BubbleLogger mBubbleLogger;
     private boolean mIsOverflow;
     private BubbleTaskViewHelper mBubbleTaskViewHelper;
     private BubbleBarMenuViewController mMenuViewController;
@@ -178,6 +180,7 @@
     /** Initializes the view, must be called before doing anything else. */
     public void initialize(BubbleExpandedViewManager expandedViewManager,
             BubblePositioner positioner,
+            BubbleLogger bubbleLogger,
             boolean isOverflow,
             @Nullable BubbleTaskView bubbleTaskView,
             @Nullable Executor mainExecutor,
@@ -185,6 +188,7 @@
             @Nullable RegionSamplingProvider regionSamplingProvider) {
         mManager = expandedViewManager;
         mPositioner = positioner;
+        mBubbleLogger = bubbleLogger;
         mIsOverflow = isOverflow;
         mMainExecutor = mainExecutor;
         mBackgroundExecutor = backgroundExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 52262e6..97397ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.dagger;
 
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
 
@@ -727,7 +728,8 @@
             Transitions transitions,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor) {
-        return Flags.enableDesktopWindowingTransitions()
+        return (Flags.enableDesktopWindowingTransitions() ||
+            ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
                 ? new SpringDragToDesktopTransitionHandler(context, transitions,
                         rootTaskDisplayAreaOrganizer, interactionJankMonitor)
                 : new DefaultDragToDesktopTransitionHandler(context, transitions,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 379e052..8ebe503 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -21,17 +21,39 @@
 import com.android.internal.util.FrameworkStatsLog
 import com.android.window.flags.Flags
 import com.android.wm.shell.EventLogTags
-import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import java.security.SecureRandom
+import java.util.Random
+import java.util.concurrent.atomic.AtomicInteger
+
 
 /** Event logger for logging desktop mode session events */
 class DesktopModeEventLogger {
+    private val random: Random = SecureRandom()
+
+    /** The session id for the current desktop mode session */
+    @VisibleForTesting
+    val currentSessionId: AtomicInteger = AtomicInteger(NO_SESSION_ID)
+
+    private fun generateSessionId() = 1 + random.nextInt(1 shl 20)
+
     /**
-     * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for
-     * entering desktop mode
+     * Logs enter into desktop mode with [enterReason]
      */
-    fun logSessionEnter(sessionId: Int, enterReason: EnterReason) {
+    fun logSessionEnter(enterReason: EnterReason) {
+        val sessionId = generateSessionId()
+        val previousSessionId = currentSessionId.getAndSet(sessionId)
+        if (previousSessionId != NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: Existing desktop mode session id: %s found on desktop "
+                    + "mode enter",
+                previousSessionId
+            )
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging session enter, session: %s reason: %s",
             sessionId,
             enterReason.name
@@ -47,12 +69,20 @@
     }
 
     /**
-     * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for
-     * exiting desktop mode
+     * Logs exit from desktop mode session with [exitReason]
      */
-    fun logSessionExit(sessionId: Int, exitReason: ExitReason) {
+    fun logSessionExit(exitReason: ExitReason) {
+        val sessionId = currentSessionId.getAndSet(NO_SESSION_ID)
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging exit from desktop mode"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging session exit, session: %s reason: %s",
             sessionId,
             exitReason.name
@@ -68,12 +98,20 @@
     }
 
     /**
-     * Logs that the task with update [taskUpdate] was added in the desktop mode session having
-     * session id [sessionId]
+     * Logs that a task with [taskUpdate] was added in a desktop mode session
      */
-    fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) {
+    fun logTaskAdded(taskUpdate: TaskUpdate) {
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging task added"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task added, session: %s taskId: %s",
             sessionId,
             taskUpdate.instanceId
@@ -85,12 +123,20 @@
     }
 
     /**
-     * Logs that the task with update [taskUpdate] was removed in the desktop mode session having
-     * session id [sessionId]
+     * Logs that a task with [taskUpdate] was removed from a desktop mode session
      */
-    fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) {
+    fun logTaskRemoved(taskUpdate: TaskUpdate) {
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging task removed"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task remove, session: %s taskId: %s",
             sessionId,
             taskUpdate.instanceId
@@ -102,12 +148,20 @@
     }
 
     /**
-     * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session
-     * having session id [sessionId]
+     * Logs that a task with [taskUpdate] had it's info changed in a desktop mode session
      */
-    fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) {
+    fun logTaskInfoChanged(taskUpdate: TaskUpdate) {
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging task info changed"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task info changed, session: %s taskId: %s",
             sessionId,
             taskUpdate.instanceId
@@ -119,14 +173,23 @@
     }
 
     /**
-     * Logs that a task resize event is starting with [taskSizeUpdate] within a
-     * Desktop mode [sessionId].
+     * Logs that a task resize event is starting with [taskSizeUpdate] within a Desktop mode
+     * session.
      */
-    fun logTaskResizingStarted(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) {
+    fun logTaskResizingStarted(taskSizeUpdate: TaskSizeUpdate) {
         if (!Flags.enableResizingMetrics()) return
 
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging start of task resizing"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s",
             sessionId,
             taskSizeUpdate.instanceId
@@ -138,14 +201,22 @@
     }
 
     /**
-     * Logs that a task resize event is ending with [taskSizeUpdate] within a
-     * Desktop mode [sessionId].
+     * Logs that a task resize event is ending with [taskSizeUpdate] within a Desktop mode session.
      */
-    fun logTaskResizingEnded(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) {
+    fun logTaskResizingEnded(taskSizeUpdate: TaskSizeUpdate) {
         if (!Flags.enableResizingMetrics()) return
 
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging end of task resizing"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s",
             sessionId,
             taskSizeUpdate.instanceId
@@ -248,6 +319,7 @@
     }
 
     companion object {
+
         /**
          * Describes a task position and dimensions.
          *
@@ -465,5 +537,6 @@
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE
         private const val DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID =
             FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED
+        @VisibleForTesting const val NO_SESSION_ID = 0
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index f847aa89..ed03982 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -35,8 +35,6 @@
 import androidx.core.util.isNotEmpty
 import androidx.core.util.plus
 import androidx.core.util.putAll
-import com.android.internal.logging.InstanceId
-import com.android.internal.logging.InstanceIdSequence
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
@@ -48,8 +46,8 @@
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 
@@ -65,8 +63,6 @@
     private val desktopModeEventLogger: DesktopModeEventLogger
 ) : Transitions.TransitionObserver {
 
-    private val idSequence: InstanceIdSequence by lazy { InstanceIdSequence(Int.MAX_VALUE) }
-
     init {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             shellInit.addInitCallback(this::onInit, this)
@@ -87,15 +83,7 @@
     // following enter reason could be Screen On
     private var wasPreviousTransitionExitByScreenOff: Boolean = false
 
-    // The instanceId for the current logging session
-    private var loggerInstanceId: InstanceId? = null
-
-    private val isSessionActive: Boolean
-        get() = loggerInstanceId != null
-
-    private fun setSessionInactive() {
-        loggerInstanceId = null
-    }
+    @VisibleForTesting var isSessionActive: Boolean = false
 
     fun onInit() {
         transitions.registerObserver(this)
@@ -247,38 +235,32 @@
         ) {
             // Sessions is finishing, log task updates followed by an exit event
             identifyAndLogTaskUpdates(
-                loggerInstanceId!!.id,
                 preTransitionVisibleFreeformTasks,
                 postTransitionVisibleFreeformTasks
             )
 
             desktopModeEventLogger.logSessionExit(
-                loggerInstanceId!!.id,
                 getExitReason(transitionInfo)
             )
-
-            setSessionInactive()
+            isSessionActive = false
         } else if (
             postTransitionVisibleFreeformTasks.isNotEmpty() &&
                 preTransitionVisibleFreeformTasks.isEmpty() &&
                 !isSessionActive
         ) {
             // Session is starting, log enter event followed by task updates
-            loggerInstanceId = idSequence.newInstanceId()
+            isSessionActive = true
             desktopModeEventLogger.logSessionEnter(
-                loggerInstanceId!!.id,
                 getEnterReason(transitionInfo)
             )
 
             identifyAndLogTaskUpdates(
-                loggerInstanceId!!.id,
                 preTransitionVisibleFreeformTasks,
                 postTransitionVisibleFreeformTasks
             )
         } else if (isSessionActive) {
             // Session is neither starting, nor finishing, log task updates if there are any
             identifyAndLogTaskUpdates(
-                loggerInstanceId!!.id,
                 preTransitionVisibleFreeformTasks,
                 postTransitionVisibleFreeformTasks
             )
@@ -291,7 +273,6 @@
 
     /** Compare the old and new state of taskInfos and identify and log the changes */
     private fun identifyAndLogTaskUpdates(
-        sessionId: Int,
         preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
         postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>
     ) {
@@ -302,7 +283,7 @@
             when {
                 // new tasks added
                 previousTaskInfo == null -> {
-                    desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate)
+                    desktopModeEventLogger.logTaskAdded(currentTaskUpdate)
                     Trace.setCounter(
                         Trace.TRACE_TAG_WINDOW_MANAGER,
                         VISIBLE_TASKS_COUNTER_NAME,
@@ -315,14 +296,14 @@
                 // TODO(b/347935387): Log changes only once they are stable.
                 buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size())
                         != currentTaskUpdate ->
-                            desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate)
+                            desktopModeEventLogger.logTaskInfoChanged(currentTaskUpdate)
             }
         }
 
         // find old tasks that were removed
         preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
             if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
-                desktopModeEventLogger.logTaskRemoved(sessionId,
+                desktopModeEventLogger.logTaskRemoved(
                     buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size()))
                 Trace.setCounter(
                     Trace.TRACE_TAG_WINDOW_MANAGER,
@@ -417,13 +398,6 @@
         visibleFreeformTaskInfos.set(taskInfo.taskId, taskInfo)
     }
 
-    @VisibleForTesting fun getLoggerSessionId(): Int? = loggerInstanceId?.id
-
-    @VisibleForTesting
-    fun setLoggerSessionId(id: Int) {
-        loggerInstanceId = InstanceId.fakeInstanceId(id)
-    }
-
     private fun TransitionInfo.Change.requireTaskInfo(): RunningTaskInfo {
         return this.taskInfo ?: throw IllegalStateException("Expected TaskInfo in the Change")
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 52b92a8..61de077 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -378,11 +378,12 @@
 
         private static VisualIndicatorAnimator fadeBoundsIn(
                 @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) {
-            final Rect startBounds = getIndicatorBounds(displayLayout, type);
+            final Rect endBounds = getIndicatorBounds(displayLayout, type);
+            final Rect startBounds = getMinBounds(endBounds);
             view.getBackground().setBounds(startBounds);
 
             final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
-                    view, startBounds, getMaxBounds(startBounds));
+                    view, startBounds, endBounds);
             animator.setInterpolator(new DecelerateInterpolator());
             setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_IN_ANIM);
             return animator;
@@ -390,8 +391,8 @@
 
         private static VisualIndicatorAnimator fadeBoundsOut(
                 @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) {
-            final Rect endBounds = getIndicatorBounds(displayLayout, type);
-            final Rect startBounds = getMaxBounds(endBounds);
+            final Rect startBounds = getIndicatorBounds(displayLayout, type);
+            final Rect endBounds = getMinBounds(startBounds);
             view.getBackground().setBounds(startBounds);
 
             final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
@@ -422,28 +423,35 @@
             return animator;
         }
 
+        /** Calculates the bounds the indicator should have when fully faded in. */
         private static Rect getIndicatorBounds(DisplayLayout layout, IndicatorType type) {
-            final int padding = layout.stableInsets().top;
+            final Rect desktopStableBounds = new Rect();
+            layout.getStableBounds(desktopStableBounds);
+            final int padding = desktopStableBounds.top;
             switch (type) {
                 case TO_FULLSCREEN_INDICATOR:
-                    return new Rect(padding, padding,
-                            layout.width() - padding,
-                            layout.height() - padding);
+                    desktopStableBounds.top += padding;
+                    desktopStableBounds.bottom -= padding;
+                    desktopStableBounds.left += padding;
+                    desktopStableBounds.right -= padding;
+                    return desktopStableBounds;
                 case TO_DESKTOP_INDICATOR:
                     final float adjustmentPercentage = 1f
                             - DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
-                    return new Rect((int) (adjustmentPercentage * layout.width() / 2),
-                            (int) (adjustmentPercentage * layout.height() / 2),
-                            (int) (layout.width() - (adjustmentPercentage * layout.width() / 2)),
-                            (int) (layout.height() - (adjustmentPercentage * layout.height() / 2)));
+                    return new Rect((int) (adjustmentPercentage * desktopStableBounds.width() / 2),
+                            (int) (adjustmentPercentage * desktopStableBounds.height() / 2),
+                            (int) (desktopStableBounds.width()
+                                    - (adjustmentPercentage * desktopStableBounds.width() / 2)),
+                            (int) (desktopStableBounds.height()
+                                    - (adjustmentPercentage * desktopStableBounds.height() / 2)));
                 case TO_SPLIT_LEFT_INDICATOR:
                     return new Rect(padding, padding,
-                            layout.width() / 2 - padding,
-                            layout.height() - padding);
+                            desktopStableBounds.width() / 2 - padding,
+                            desktopStableBounds.height());
                 case TO_SPLIT_RIGHT_INDICATOR:
-                    return new Rect(layout.width() / 2 + padding, padding,
-                            layout.width() - padding,
-                            layout.height() - padding);
+                    return new Rect(desktopStableBounds.width() / 2 + padding, padding,
+                            desktopStableBounds.width() - padding,
+                            desktopStableBounds.height());
                 default:
                     throw new IllegalArgumentException("Invalid indicator type provided.");
             }
@@ -505,17 +513,18 @@
         }
 
         /**
-         * Return the max bounds of a visual indicator
+         * Return the minimum bounds of a visual indicator, to be used at the end of fading out
+         * and the start of fading in.
          */
-        private static Rect getMaxBounds(Rect startBounds) {
-            return new Rect((int) (startBounds.left
-                    - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
-                    (int) (startBounds.top
-                            - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())),
-                    (int) (startBounds.right
-                            + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
-                    (int) (startBounds.bottom
-                            + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())));
+        private static Rect getMinBounds(Rect maxBounds) {
+            return new Rect((int) (maxBounds.left
+                    + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())),
+                    (int) (maxBounds.top
+                            + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())),
+                    (int) (maxBounds.right
+                            - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())),
+                    (int) (maxBounds.bottom
+                            - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())));
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b723337..e078c7e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -1185,7 +1185,13 @@
         val options = createNewWindowOptions(callingTask)
         if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
             wct.startTask(requestedTaskId, options.toBundle())
-            transitions.startTransition(TRANSIT_OPEN, wct, null)
+            val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+                callingTask.displayId, wct, requestedTaskId)
+            val runOnTransit = immersiveTransitionHandler
+                .exitImmersiveIfApplicable(wct, callingTask.displayId)
+            val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
+            addPendingMinimizeTransition(transition, taskToMinimize)
+            runOnTransit?.invoke(transition)
         } else {
             val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
             splitScreenController.startTask(requestedTaskId, splitPosition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index f783b45..5a2abe1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -16,9 +16,9 @@
 
 package com.android.wm.shell.unfold;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
 
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
 
@@ -30,6 +30,7 @@
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -45,6 +46,8 @@
 import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
 import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -56,6 +59,18 @@
  */
 public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener {
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE,
+            DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD,
+            DefaultDisplayChange.DEFAULT_DISPLAY_FOLD,
+    })
+    private @interface DefaultDisplayChange {
+        int DEFAULT_DISPLAY_NO_CHANGE = 0;
+        int DEFAULT_DISPLAY_UNFOLD = 1;
+        int DEFAULT_DISPLAY_FOLD = 2;
+    }
+
     private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
     private final Transitions mTransitions;
     private final Executor mExecutor;
@@ -66,7 +81,10 @@
     @Nullable
     private IBinder mTransition;
 
+    // TODO: b/318803244 - remove when we could guarantee finishing the animation
+    //  after startAnimation callback
     private boolean mAnimationFinished = false;
+    private float mLastAnimationProgress = 0.0f;
     private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>();
 
     public UnfoldTransitionHandler(ShellInit shellInit,
@@ -107,16 +125,6 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull TransitionFinishCallback finishCallback) {
-        if (shouldPlayUnfoldAnimation(info) && transition != mTransition) {
-            // Take over transition that has unfold, we might receive it if no other handler
-            // accepted request in handleRequest, e.g. for rotation + unfold or
-            // TRANSIT_NONE + unfold transitions
-            mTransition = transition;
-
-            ProtoLog.v(WM_SHELL_TRANSITIONS, "UnfoldTransitionHandler: "
-                    + "take over startAnimation");
-        }
-
         if (transition != mTransition) return false;
 
         for (int i = 0; i < mAnimators.size(); i++) {
@@ -158,6 +166,8 @@
 
     @Override
     public void onStateChangeProgress(float progress) {
+        mLastAnimationProgress = progress;
+
         if (mTransition == null) return;
 
         SurfaceControl.Transaction transaction = null;
@@ -182,8 +192,14 @@
 
     @Override
     public void onStateChangeFinished() {
-        mAnimationFinished = true;
         finishTransitionIfNeeded();
+
+        // mLastAnimationProgress is guaranteed to be 0f when folding finishes, see
+        // {@link PhysicsBasedUnfoldTransitionProgressProvider#cancelTransition}.
+        // We can use it as an indication that the next animation progress events will be related
+        // to unfolding, so let's reset mAnimationFinished to 'false' in this case.
+        final boolean isFoldingFinished = mLastAnimationProgress == 0f;
+        mAnimationFinished = !isFoldingFinished;
     }
 
     @Override
@@ -211,6 +227,12 @@
         // Apply changes happening during the unfold animation immediately
         t.apply();
         finishCallback.onTransitionFinished(null);
+
+        if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) {
+            // Force-finish current unfold animation as we are processing folding now which doesn't
+            // have any animations on the Shell side
+            finishTransitionIfNeeded();
+        }
     }
 
     /** Whether `request` contains an unfold action. */
@@ -219,18 +241,25 @@
         if (!ValueAnimator.areAnimatorsEnabled()) return false;
 
         return (request.getType() == TRANSIT_CHANGE
-                && request.getDisplayChange() != null
-                && isUnfoldDisplayChange(request.getDisplayChange()));
+                && getDefaultDisplayChange(request.getDisplayChange())
+                == DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD);
     }
 
-    private boolean isUnfoldDisplayChange(
-            @NonNull TransitionRequestInfo.DisplayChange displayChange) {
+    @DefaultDisplayChange
+    private int getDefaultDisplayChange(
+            @Nullable TransitionRequestInfo.DisplayChange displayChange) {
+        if (displayChange == null) return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
+
+        if (displayChange.getDisplayId() != DEFAULT_DISPLAY) {
+            return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
+        }
+
         if (!displayChange.isPhysicalDisplayChanged()) {
-            return false;
+            return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
         }
 
         if (displayChange.getStartAbsBounds() == null || displayChange.getEndAbsBounds() == null) {
-            return false;
+            return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
         }
 
         // Handle only unfolding, currently we don't have an animation when folding
@@ -239,17 +268,11 @@
         final int startArea = displayChange.getStartAbsBounds().width()
                 * displayChange.getStartAbsBounds().height();
 
-        return endArea > startArea;
+        return endArea > startArea ? DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD
+                : DefaultDisplayChange.DEFAULT_DISPLAY_FOLD;
     }
 
-    /** Whether `transitionInfo` contains an unfold action. */
-    public boolean shouldPlayUnfoldAnimation(@NonNull TransitionInfo transitionInfo) {
-        // Unfold animation won't play when animations are disabled
-        if (!ValueAnimator.areAnimatorsEnabled()) return false;
-        // Only handle transitions that are marked as physical display switch
-        // See PhysicalDisplaySwitchTransitionLauncher for the conditions
-        if ((transitionInfo.getFlags() & TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH) == 0) return false;
-
+    private int getDefaultDisplayChange(@NonNull TransitionInfo transitionInfo) {
         for (int i = 0; i < transitionInfo.getChanges().size(); i++) {
             final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
             // We are interested only in display container changes
@@ -268,11 +291,13 @@
                     * change.getStartAbsBounds().height();
 
             if (afterArea > beforeArea) {
-                return true;
+                return DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD;
+            } else {
+                return DefaultDisplayChange.DEFAULT_DISPLAY_FOLD;
             }
         }
 
-        return false;
+        return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
     }
 
     @Nullable
@@ -293,10 +318,6 @@
     @Override
     public void onFoldStateChanged(boolean isFolded) {
         if (isFolded) {
-            // Reset unfold animation finished flag on folding, so it could be used next time
-            // when we unfold the device as an indication that animation hasn't finished yet
-            mAnimationFinished = false;
-
             // If we are currently animating unfold animation we should finish it because
             // the animation might not start and finish as the device was folded
             finishTransitionIfNeeded();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
index 094af96..a1d4a1a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
@@ -26,6 +26,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.wm.shell.ShellTestCase;
 
 import org.junit.Before;
@@ -45,6 +46,7 @@
     private TestableBubblePositioner mPositioner;
     private BubbleOverflow mOverflow;
     private BubbleExpandedViewManager mExpandedViewManager;
+    private BubbleLogger mBubbleLogger;
 
     @Mock
     private BubbleController mBubbleController;
@@ -58,6 +60,7 @@
         mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(mBubbleController);
         mPositioner = new TestableBubblePositioner(mContext,
                 mContext.getSystemService(WindowManager.class));
+        mBubbleLogger = new BubbleLogger(new UiEventLoggerFake());
         when(mBubbleController.getPositioner()).thenReturn(mPositioner);
         when(mBubbleController.getStackView()).thenReturn(mBubbleStackView);
 
@@ -77,7 +80,7 @@
 
     @Test
     public void test_initialize_forBubbleBar() {
-        mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner);
+        mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner, mBubbleLogger);
 
         assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull();
         assertThat(mOverflow.getExpandedView()).isNull();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index dde9fda..0825b6b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -18,7 +18,10 @@
 
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
+import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations
+import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import com.android.window.flags.Flags
@@ -26,15 +29,16 @@
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.NO_SESSION_ID
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskSizeUpdate
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_MINIMIZE_REASON
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON
-import kotlinx.coroutines.runBlocking
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.mockito.kotlin.eq
@@ -49,40 +53,87 @@
     @JvmField
     @Rule(order = 0)
     val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
-            .mockStatic(FrameworkStatsLog::class.java)
-            .mockStatic(EventLogTags::class.java).build()!!
+        .mockStatic(FrameworkStatsLog::class.java)
+        .mockStatic(EventLogTags::class.java).build()!!
 
     @JvmField
     @Rule(order = 1)
     val setFlagsRule = SetFlagsRule()
 
     @Test
-    fun logSessionEnter_enterReason() = runBlocking {
-        desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER)
+    fun logSessionEnter_logsEnterReasonWithNewSessionId() {
+        desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
 
+        val sessionId = desktopModeEventLogger.currentSessionId.get()
+        assertThat(sessionId).isNotEqualTo(NO_SESSION_ID)
         verify {
             FrameworkStatsLog.write(
                 eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
                 /* event */
                 eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER),
                 /* enter_reason */
-                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER),
+                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER),
                 /* exit_reason */
                 eq(0),
                 /* sessionId */
-                eq(SESSION_ID)
+                eq(sessionId)
             )
         }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellEnterDesktopMode(
-                eq(EnterReason.UNKNOWN_ENTER.reason),
-                eq(SESSION_ID))
+                eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
+                eq(sessionId)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logSessionExit_exitReason() = runBlocking {
-        desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT)
+    fun logSessionEnter_ongoingSession_logsEnterReasonWithNewSessionId() {
+        val previousSessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
+
+        val sessionId = desktopModeEventLogger.currentSessionId.get()
+        assertThat(sessionId).isNotEqualTo(NO_SESSION_ID)
+        assertThat(sessionId).isNotEqualTo(previousSessionId)
+        verify {
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
+                /* event */
+                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER),
+                /* enter_reason */
+                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER),
+                /* exit_reason */
+                eq(0),
+                /* sessionId */
+                eq(sessionId)
+            )
+        }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verify {
+            EventLogTags.writeWmShellEnterDesktopMode(
+                eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
+                eq(sessionId)
+            )
+        }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logSessionExit_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logSessionExit_logsExitReasonAndClearsSessionId() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
 
         verify {
             FrameworkStatsLog.write(
@@ -92,24 +143,39 @@
                 /* enter_reason */
                 eq(0),
                 /* exit_reason */
-                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT),
+                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT),
                 /* sessionId */
-                eq(SESSION_ID)
+                eq(sessionId)
             )
         }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellExitDesktopMode(
-                eq(ExitReason.UNKNOWN_EXIT.reason),
-                eq(SESSION_ID))
+                eq(ExitReason.DRAG_TO_EXIT.reason),
+                eq(sessionId)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID)
     }
 
     @Test
-    fun logTaskAdded_taskUpdate() = runBlocking {
-        desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE)
+    fun logTaskAdded_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logTaskAdded_logsTaskUpdate() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
                 eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
                 /* instance_id */
@@ -125,36 +191,52 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logTaskRemoved_taskUpdate() = runBlocking {
-        desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE)
+    fun logTaskRemoved_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logTaskRemoved_taskUpdate() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
                 eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
                 /* instance_id */
@@ -170,39 +252,57 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logTaskInfoChanged_taskUpdate() = runBlocking {
-        desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE)
+    fun logTaskInfoChanged_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logTaskInfoChanged_taskUpdate() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 /* instance_id */
                 eq(TASK_UPDATE.instanceId),
                 /* uid */
@@ -216,40 +316,51 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() = runBlocking {
-        desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
-            createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT))
+    fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskInfoChanged(
+            createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT)
+        )
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 /* instance_id */
                 eq(TASK_UPDATE.instanceId),
                 /* uid */
@@ -263,42 +374,53 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 /* minimize_reason */
                 eq(MinimizeReason.TASK_LIMIT.reason),
                 /* unminimize_reason */
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(MinimizeReason.TASK_LIMIT.reason),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() = runBlocking {
-        desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
-            createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP))
+    fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskInfoChanged(
+            createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP)
+        )
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 /* instance_id */
                 eq(TASK_UPDATE.instanceId),
                 /* uid */
@@ -312,39 +434,55 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 /* minimize_reason */
                 eq(UNSET_MINIMIZE_REASON),
                 /* unminimize_reason */
                 eq(UnminimizeReason.TASKBAR_TAP.reason),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UnminimizeReason.TASKBAR_TAP.reason),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logTaskResizingStarted_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS)
-    fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() = runBlocking {
-        desktopModeEventLogger.logTaskResizingStarted(sessionId = SESSION_ID, createTaskSizeUpdate())
+    fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
                 /* resize_trigger */
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER),
                 /* resizing_stage */
@@ -352,7 +490,7 @@
                 /* input_method */
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
                 /* desktop_mode_session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 /* instance_id */
                 eq(TASK_SIZE_UPDATE.instanceId),
                 /* uid */
@@ -365,15 +503,27 @@
                 eq(TASK_SIZE_UPDATE.displayArea),
             )
         }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+    }
+
+    @Test
+    fun logTaskResizingEnded_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS)
-    fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() = runBlocking {
-        desktopModeEventLogger.logTaskResizingEnded(sessionId = SESSION_ID, createTaskSizeUpdate())
+    fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
                 /* resize_trigger */
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER),
                 /* resizing_stage */
@@ -381,7 +531,7 @@
                 /* input_method */
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
                 /* desktop_mode_session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 /* instance_id */
                 eq(TASK_SIZE_UPDATE.instanceId),
                 /* uid */
@@ -394,6 +544,14 @@
                 eq(TASK_SIZE_UPDATE.displayArea),
             )
         }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+    }
+
+    private fun startDesktopModeSession(): Int {
+        desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
+        clearInvocations(staticMockMarker(FrameworkStatsLog::class.java))
+        clearInvocations(staticMockMarker(EventLogTags::class.java))
+        return desktopModeEventLogger.currentSessionId.get()
     }
 
     @Test
@@ -428,7 +586,7 @@
     }
 
     private companion object {
-        private const val SESSION_ID = 1
+        private const val sessionId = 1
         private const val TASK_ID = 1
         private const val TASK_UID = 1
         private const val TASK_X = 0
@@ -453,23 +611,12 @@
             DISPLAY_AREA,
         )
 
-        private fun createTaskSizeUpdate(
-            resizeTrigger: ResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
-            inputMethod: InputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
-        ) = TaskSizeUpdate(
-            resizeTrigger,
-            inputMethod,
-            TASK_ID,
-            TASK_UID,
-            TASK_HEIGHT,
-            TASK_WIDTH,
-            DISPLAY_AREA,
-        )
-
         private fun createTaskUpdate(
             minimizeReason: MinimizeReason? = null,
             unminimizeReason: UnminimizeReason? = null,
-        ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
-            unminimizeReason, TASK_COUNT)
+        ) = TaskUpdate(
+            TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
+            unminimizeReason, TASK_COUNT
+        )
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index e7593b5..f25faa5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -59,8 +59,8 @@
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
-import junit.framework.Assert.assertNotNull
-import junit.framework.Assert.assertNull
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -142,8 +142,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
-    verify(desktopModeEventLogger, never()).logTaskAdded(any(), any())
+    verify(desktopModeEventLogger, never()).logSessionEnter(any())
+    verify(desktopModeEventLogger, never()).logTaskAdded(any())
   }
 
   @Test
@@ -228,11 +228,10 @@
   @Test
   fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
     // previous exit to overview transition
-    val previousSessionId = 1
     // add a freeform task
     val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.setLoggerSessionId(previousSessionId)
+    transitionObserver.isSessionActive = true
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
             .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -241,7 +240,8 @@
     callOnTransitionReady(previousTransitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
@@ -259,11 +259,10 @@
   @Test
   fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
     // previous exit to overview transition
-    val previousSessionId = 1
     // add a freeform task
     val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.setLoggerSessionId(previousSessionId)
+    transitionObserver.isSessionActive = true
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
             .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -272,7 +271,8 @@
     callOnTransitionReady(previousTransitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
@@ -290,11 +290,10 @@
   @Test
   fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
     // previous exit to overview transition
-    val previousSessionId = 1
     // add a freeform task
     val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.setLoggerSessionId(previousSessionId)
+    transitionObserver.isSessionActive = true
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
             .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -303,7 +302,8 @@
     callOnTransitionReady(previousTransitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
@@ -324,11 +324,10 @@
     // Tests for AppFromOverview precedence in compared to cancelled Overview
 
     // previous exit to overview transition
-    val previousSessionId = 1
     // add a freeform task
     val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.setLoggerSessionId(previousSessionId)
+    transitionObserver.isSessionActive = true
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
             .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -337,7 +336,8 @@
     callOnTransitionReady(previousTransitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
@@ -379,11 +379,10 @@
   fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() {
     val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
     // Previous Exit reason recorded as Screen Off
-    val sessionId = 1
     transitionObserver.addTaskInfosToCachedMap(freeformTask)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
     callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
     // Enter desktop through back transition, this happens when user enters after dismissing
     // keyguard
     val change = createChange(TRANSIT_TO_FRONT, freeformTask)
@@ -399,11 +398,10 @@
   fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() {
     val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
     // Previous Exit reason recorded as Screen Off
-    val sessionId = 1
     transitionObserver.addTaskInfosToCachedMap(freeformTask)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
     callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
 
     // Enter desktop through app handle drag. This represents cases where instead of moving to
     // desktop right after turning the screen on, we move to fullscreen then move another task
@@ -419,24 +417,22 @@
   }
 
   @Test
-  fun transitSleep_logTaskRemovedAndExitReasonScreenOff_sessionIdNull() {
-    val sessionId = 1
+  fun transitSleep_logTaskRemovedAndExitReasonScreenOff() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
-    val sessionId = 1
+  fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // window mode changing from FREEFORM to FULLSCREEN
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -444,15 +440,14 @@
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
-    val sessionId = 1
+  fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // window mode changing from FREEFORM to FULLSCREEN
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -462,16 +457,14 @@
             .build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(
-        sessionId, ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
-    val sessionId = 1
+  fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // window mode changing from FREEFORM to FULLSCREEN
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -479,16 +472,14 @@
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(
-        sessionId, ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
-    val sessionId = 1
+  fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // window mode changing from FREEFORM to FULLSCREEN
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -496,15 +487,14 @@
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
-    val sessionId = 1
+  fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // recents transition
     val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
@@ -513,31 +503,30 @@
     callOnTransitionReady(transitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
   }
 
   @Test
-  fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
-    val sessionId = 1
+  fun transitClose_logTaskRemovedAndExitReasonTaskFinished() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // task closing
     val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
-    val sessionId = 1
     // add a freeform task to an existing session
     val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // recents transition sent freeform window to back
     val change = createChange(TRANSIT_TO_BACK, taskInfo)
@@ -546,7 +535,8 @@
     callOnTransitionReady(transitionInfo1)
 
     verifyTaskRemovedAndExitLogging(
-        sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
     callOnTransitionReady(transitionInfo2)
@@ -557,10 +547,9 @@
 
   @Test
   fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
-    val sessionId = 1
     // add an existing freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // new freeform task added
     val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
@@ -568,18 +557,16 @@
     callOnTransitionReady(transitionInfo)
 
     verify(desktopModeEventLogger, times(1))
-        .logTaskAdded(eq(sessionId),
-            eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
-    verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
+        .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
+    verify(desktopModeEventLogger, never()).logSessionEnter(any())
   }
 
   @Test
   fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() {
-    val sessionId = 1
     // add an existing freeform task
     val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // task position changed
     val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
@@ -591,18 +578,17 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId),
-            eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1)))
+            eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
+        )
     verifyZeroInteractions(desktopModeEventLogger)
   }
 
   @Test
   fun sessionAlreadyStarted_taskResized_logsTaskUpdate() {
-    val sessionId = 1
     // add an existing freeform task
     val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // task resized
     val newTaskInfo =
@@ -618,23 +604,22 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId),
             eq(
                 DEFAULT_TASK_UPDATE.copy(
                     taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100,
-                    visibleTaskCount = 1)))
+                    visibleTaskCount = 1))
+        )
     verifyZeroInteractions(desktopModeEventLogger)
   }
 
   @Test
   fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() {
-    val sessionId = 1
     // add 2 existing freeform task
     val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM)
     val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
     transitionObserver.addTaskInfosToCachedMap(taskInfo1)
     transitionObserver.addTaskInfosToCachedMap(taskInfo2)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // task 1 position update
     val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
@@ -646,8 +631,9 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(
-                taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2)))
+            eq(DEFAULT_TASK_UPDATE.copy(
+                taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
+        )
     verifyZeroInteractions(desktopModeEventLogger)
 
     // task 2 resize
@@ -666,7 +652,6 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId),
             eq(
                 DEFAULT_TASK_UPDATE.copy(
                     instanceId = 2,
@@ -679,11 +664,10 @@
 
   @Test
   fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
-    val sessionId = 1
     // add two existing freeform tasks
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // new freeform task closed
     val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
@@ -691,9 +675,11 @@
     callOnTransitionReady(transitionInfo)
 
     verify(desktopModeEventLogger, times(1))
-        .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(
-            instanceId = 2, visibleTaskCount = 1)))
-    verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
+        .logTaskRemoved(
+            eq(DEFAULT_TASK_UPDATE.copy(
+                instanceId = 2, visibleTaskCount = 1))
+        )
+    verify(desktopModeEventLogger, never()).logSessionExit(any())
   }
 
   /** Simulate calling the onTransitionReady() method */
@@ -706,10 +692,9 @@
   }
 
   private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
-    val sessionId = transitionObserver.getLoggerSessionId()
-    assertNotNull(sessionId)
-    verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate))
+    assertTrue(transitionObserver.isSessionActive)
+    verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate))
     ExtendedMockito.verify {
         Trace.setCounter(
             eq(Trace.TRACE_TAG_WINDOW_MANAGER),
@@ -725,14 +710,13 @@
   }
 
   private fun verifyTaskRemovedAndExitLogging(
-      sessionId: Int,
       exitReason: ExitReason,
       taskUpdate: TaskUpdate
   ) {
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), eq(taskUpdate))
-    verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), eq(exitReason))
+    assertFalse(transitionObserver.isSessionActive)
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
+    verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
     verifyZeroInteractions(desktopModeEventLogger)
-    assertNull(transitionObserver.getLoggerSessionId())
   }
 
   private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index b3c10d6..6531e2a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -2984,6 +2984,58 @@
       .launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
   }
 
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+  fun openInstance_fromFreeform_minimizesIfNeeded() {
+    setUpLandscapeDisplay()
+    val homeTask = setUpHomeTask()
+    val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+    val oldestTask = freeformTasks.first()
+    val newestTask = freeformTasks.last()
+
+    runOpenInstance(newestTask, freeformTasks[1].taskId)
+
+    val wct = getLatestWct(type = TRANSIT_OPEN)
+    // Home is moved to front of everything.
+    assertThat(
+      wct.hierarchyOps.any { hop ->
+        hop.container == homeTask.token.asBinder() && hop.toTop
+      }
+    ).isTrue()
+    // And the oldest task isn't moved in front of home, effectively minimizing it.
+    assertThat(
+      wct.hierarchyOps.none { hop ->
+        hop.container == oldestTask.token.asBinder() && hop.toTop
+      }
+    ).isTrue()
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+  fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
+    setUpLandscapeDisplay()
+    val homeTask = setUpHomeTask()
+    val freeformTask = setUpFreeformTask()
+    val immersiveTask = setUpFreeformTask()
+    taskRepository.setTaskInFullImmersiveState(
+      displayId = immersiveTask.displayId,
+      taskId = immersiveTask.taskId,
+      immersive = true
+    )
+    val runOnStartTransit = RunOnStartTransitionCallback()
+    val transition = Binder()
+    whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull()))
+      .thenReturn(transition)
+    whenever(mockDesktopFullImmersiveTransitionHandler
+      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))).thenReturn(runOnStartTransit)
+
+    runOpenInstance(immersiveTask, freeformTask.taskId)
+
+    verify(mockDesktopFullImmersiveTransitionHandler)
+      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))
+    runOnStartTransit.assertOnlyInvocation(transition)
+  }
+
   private fun runOpenInstance(
     callingTask: RunningTaskInfo,
     requestedTaskId: Int
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index cf2de91..22da66d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -18,13 +18,13 @@
 
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
 import static android.view.WindowManager.TRANSIT_NONE;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -50,6 +50,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.InOrder;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -140,6 +141,32 @@
     }
 
     @Test
+    public void handleFoldMergeRequest_finishesTheTransition() {
+        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+        // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to
+        // notify about the end of the animation
+        mUnfoldTransitionHandler.startAnimation(
+                mTransition,
+                mock(TransitionInfo.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                finishCallback
+        );
+
+        // Send fold transition request
+        TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class);
+        mUnfoldTransitionHandler.mergeAnimation(new Binder(), createFoldTransitionInfo(),
+                mock(SurfaceControl.Transaction.class), mTransition, mergeFinishCallback);
+
+        // Verify that fold transition is merged into unfold and that unfold is finished
+        final InOrder inOrder = inOrder(mergeFinishCallback, finishCallback);
+        inOrder.verify(mergeFinishCallback).onTransitionFinished(any());
+        inOrder.verify(finishCallback).onTransitionFinished(any());
+    }
+
+    @Test
     public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() {
         TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
         mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
@@ -174,29 +201,13 @@
     }
 
     @Test
-    public void startAnimation_differentTransitionFromRequestWithUnfold_startsAnimation() {
-        mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo());
-        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
-
-        boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
-                mTransition,
-                createUnfoldTransitionInfo(),
-                mock(SurfaceControl.Transaction.class),
-                mock(SurfaceControl.Transaction.class),
-                finishCallback
-        );
-
-        assertThat(animationStarted).isTrue();
-    }
-
-    @Test
     public void startAnimation_differentTransitionFromRequestWithResize_doesNotStartAnimation() {
         mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo());
         TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
 
         boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
                 mTransition,
-                createDisplayResizeTransitionInfo(),
+                createUnfoldTransitionInfo(),
                 mock(SurfaceControl.Transaction.class),
                 mock(SurfaceControl.Transaction.class),
                 finishCallback
@@ -247,6 +258,7 @@
         TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
 
         mShellUnfoldProgressProvider.onStateChangeStarted();
+        mShellUnfoldProgressProvider.onStateChangeProgress(0.5f);
         mShellUnfoldProgressProvider.onStateChangeFinished();
         mUnfoldTransitionHandler.startAnimation(
                 mTransition,
@@ -279,6 +291,8 @@
         clearInvocations(finishCallback);
 
         // Fold
+        mShellUnfoldProgressProvider.onStateChangeProgress(/* progress= */ 0.0f);
+        mShellUnfoldProgressProvider.onStateChangeFinished();
         mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ true);
 
         // Second unfold
@@ -370,6 +384,19 @@
                 triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
     }
 
+    private TransitionInfo createFoldTransitionInfo() {
+        final TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
+
+        final TransitionInfo.Change change = new TransitionInfo.Change(/* container= */ null,
+                /* leash= */ null);
+        change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
+        change.setStartAbsBounds(new Rect(0, 0, 200, 200));
+        change.setEndAbsBounds(new Rect(0, 0, 100, 100));
+        transitionInfo.addChange(change);
+
+        return transitionInfo;
+    }
+
     private TransitionRequestInfo createNoneTransitionInfo() {
         return new TransitionRequestInfo(TRANSIT_NONE,
                 /* triggerTask= */ null, /* remoteTransition= */ null,
@@ -446,17 +473,6 @@
         change.setEndAbsBounds(new Rect(0, 0, 100, 100));
         change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
         transitionInfo.addChange(change);
-        transitionInfo.setFlags(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH);
-        return transitionInfo;
-    }
-
-    private TransitionInfo createDisplayResizeTransitionInfo() {
-        TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
-        TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class));
-        change.setStartAbsBounds(new Rect(0, 0, 10, 10));
-        change.setEndAbsBounds(new Rect(0, 0, 100, 100));
-        change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
-        transitionInfo.addChange(change);
         return transitionInfo;
     }
 
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 28d85bd..a373713 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -242,45 +242,59 @@
     }
 }
 
-SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const {
-    const DirtyStack* frame = mHead;
-    Matrix4 transform;
-    SkRect pretransformResult = bounds;
-    while (true) {
-        SkRect currentBounds = pretransformResult;
-        pretransformResult.setEmpty();
-        switch (frame->type) {
-            case TransformRenderNode: {
-                const RenderProperties& props = frame->renderNode->properties();
-                // Perform clipping
-                if (props.getClipDamageToBounds() && !currentBounds.isEmpty()) {
-                    if (!currentBounds.intersect(
-                                SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
-                        currentBounds.setEmpty();
-                    }
+static void computeClipAndTransformImpl(const DirtyStack* currentFrame, SkRect* crop,
+                                        Matrix4* outMatrix) {
+    SkRect currentCrop = *crop;
+    switch (currentFrame->type) {
+        case TransformRenderNode: {
+            const RenderProperties& props = currentFrame->renderNode->properties();
+            // Perform clipping
+            if (props.getClipDamageToBounds() && !currentCrop.isEmpty()) {
+                if (!currentCrop.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
+                    currentCrop.setEmpty();
                 }
+            }
 
-                // apply all transforms
-                mapRect(props, currentBounds, &pretransformResult);
-                frame->renderNode->applyViewPropertyTransforms(transform);
-            } break;
-            case TransformMatrix4:
-                mapRect(frame->matrix4, currentBounds, &pretransformResult);
-                transform.multiply(*frame->matrix4);
-                break;
-            default:
-                pretransformResult = currentBounds;
-                break;
-        }
-        if (frame->prev == frame) break;
-        frame = frame->prev;
+            // apply all transforms
+            crop->setEmpty();
+            mapRect(props, currentCrop, crop);
+        } break;
+        case TransformMatrix4:
+            crop->setEmpty();
+            mapRect(currentFrame->matrix4, currentCrop, crop);
+            break;
+        default:
+            break;
     }
-    SkRect result;
+
+    if (currentFrame->prev != currentFrame) {
+        computeClipAndTransformImpl(currentFrame->prev, crop, outMatrix);
+    }
+    switch (currentFrame->type) {
+        case TransformRenderNode:
+            currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
+            break;
+        case TransformMatrix4:
+            outMatrix->multiply(*currentFrame->matrix4);
+            break;
+        case TransformNone:
+            // nothing to be done
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d",
+                             currentFrame->type);
+    }
+}
+
+SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const {
+    SkRect cropInGlobal = bounds;
+    outMatrix->loadIdentity();
+    computeClipAndTransformImpl(mHead, &cropInGlobal, outMatrix);
+    SkRect cropInLocal;
     Matrix4 globalToLocal;
-    globalToLocal.loadInverse(transform);
-    mapRect(&globalToLocal, pretransformResult, &result);
-    *outMatrix = transform;
-    return result;
+    globalToLocal.loadInverse(*outMatrix);
+    mapRect(&globalToLocal, cropInGlobal, &cropInLocal);
+    return cropInLocal;
 }
 
 void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 84bd45d..b73380e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -582,20 +582,22 @@
 size_t Bitmap::mTotalBitmapCount = 0;
 
 void Bitmap::traceBitmapCreate() {
+    size_t bytes = getAllocationByteCount();
+    std::lock_guard lock{mLock};
+    mTotalBitmapBytes += bytes;
+    mTotalBitmapCount++;
     if (ATRACE_ENABLED()) {
-        std::lock_guard lock{mLock};
-        mTotalBitmapBytes += getAllocationByteCount();
-        mTotalBitmapCount++;
         ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
         ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
     }
 }
 
 void Bitmap::traceBitmapDelete() {
+    size_t bytes = getAllocationByteCount();
+    std::lock_guard lock{mLock};
+    mTotalBitmapBytes -= getAllocationByteCount();
+    mTotalBitmapCount--;
     if (ATRACE_ENABLED()) {
-        std::lock_guard lock{mLock};
-        mTotalBitmapBytes -= getAllocationByteCount();
-        mTotalBitmapCount--;
         ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
         ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
     }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index eeb4853..961962f 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -85,8 +85,7 @@
     void updateScanningState(IMediaRouter2Manager manager, @JavaPassthrough(annotation="@android.media.MediaRouter2.ScanningState") int scanningState);
 
     void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
-            in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route,
-            in UserHandle transferInitiatorUserHandle, in String transferInitiatorPackageName);
+            in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route);
     void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
             String sessionId, in MediaRoute2Info route);
     void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index b84990b..3499c43 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2770,7 +2770,7 @@
                     || isSystemRouteReselection) {
                 transferToRoute(sessionInfo, route, mClientUser, mClientPackageName);
             } else {
-                requestCreateSession(sessionInfo, route, mClientUser, mClientPackageName);
+                requestCreateSession(sessionInfo, route);
             }
         }
 
@@ -2826,10 +2826,7 @@
          * @param route The {@link MediaRoute2Info route} to transfer to.
          */
         private void requestCreateSession(
-                @NonNull RoutingSessionInfo oldSession,
-                @NonNull MediaRoute2Info route,
-                @NonNull UserHandle transferInitiatorUserHandle,
-                @NonNull String transferInitiatorPackageName) {
+                @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
             if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
                 Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
                 this.onTransferFailed(oldSession, route);
@@ -2840,12 +2837,7 @@
 
             try {
                 mMediaRouterService.requestCreateSessionWithManager(
-                        mClient,
-                        requestId,
-                        oldSession,
-                        route,
-                        transferInitiatorUserHandle,
-                        transferInitiatorPackageName);
+                        mClient, requestId, oldSession, route);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 8fa0e49..7e1dccf 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -524,8 +524,7 @@
             transferToRoute(
                     sessionInfo, route, transferInitiatorUserHandle, transferInitiatorPackageName);
         } else {
-            requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
-                    transferInitiatorPackageName);
+            requestCreateSession(sessionInfo, route);
         }
     }
 
@@ -914,9 +913,7 @@
         }
     }
 
-    private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route,
-            @NonNull UserHandle transferInitiatorUserHandle,
-            @NonNull String transferInitiationPackageName) {
+    private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) {
         if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
             Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
             notifyTransferFailed(oldSession, route);
@@ -927,8 +924,7 @@
 
         try {
             mMediaRouterService.requestCreateSessionWithManager(
-                    mClient, requestId, oldSession, route, transferInitiatorUserHandle,
-                    transferInitiationPackageName);
+                    mClient, requestId, oldSession, route);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index b286182..601e001 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -57,7 +57,7 @@
  * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false)
  * 2. User doesn't set any title or icon for button.
  */
-public class ActionButtonsPreference extends Preference {
+public class ActionButtonsPreference extends Preference implements GroupSectionDividerMixin {
 
     private static final String TAG = "ActionButtonPreference";
     private static final boolean mIsAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 6cd777e..10769ec 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -43,7 +43,7 @@
  * Banner message is a banner displaying important information (permission request, page error etc),
  * and provide actions for user to address. It requires a user action to be dismissed.
  */
-public class BannerMessagePreference extends Preference {
+public class BannerMessagePreference extends Preference implements GroupSectionDividerMixin {
 
     public enum AttentionLevel {
         HIGH(0, R.color.banner_background_attention_high, R.color.banner_accent_attention_high),
diff --git a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
index eb14746..84ff1bb 100644
--- a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
+++ b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
@@ -30,7 +30,7 @@
     attrs: AttributeSet? = null,
     defStyleAttr: Int = 0,
     defStyleRes: Int = 0
-) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
 
     init {
         layoutResource = R.layout.settingslib_expressive_preference_card
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index cd8f584..12890fe 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -21,6 +21,7 @@
         "SettingsLibColor",
         "androidx.preference_preference",
         "lottie",
+        "SettingsLibSettingsTheme",
         "settingslib_illustrationpreference_flags_lib",
     ],
 
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index a0599bb..adc4f316 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -55,7 +55,7 @@
 /**
  * IllustrationPreference is a preference that can play lottie format animation
  */
-public class IllustrationPreference extends Preference {
+public class IllustrationPreference extends Preference implements GroupSectionDividerMixin {
 
     private static final String TAG = "IllustrationPreference";
 
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index e3f8fbb..2e3ee32 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -20,8 +20,6 @@
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingTop="@dimen/settingslib_switchbar_margin"
     android:paddingBottom="@dimen/settingslib_switchbar_margin"
     android:orientation="vertical">
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
index 255b2c9..3e0e184 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -20,8 +20,6 @@
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingTop="@dimen/settingslib_switchbar_margin"
     android:paddingBottom="@dimen/settingslib_switchbar_margin"
     android:orientation="vertical">
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
index 4425ef0..f75d9b2 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
@@ -20,8 +20,6 @@
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:paddingVertical="@dimen/settingslib_expressive_space_small1"
     android:orientation="vertical">
 
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index bf34db9..7c0eaea 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -18,11 +18,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
-    android:layout_width="match_parent"
-    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:layout_width="match_parent">
 
     <TextView
         android:id="@+id/switch_text"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
index bef6e35..fa908a4 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
@@ -18,6 +18,10 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:importantForAccessibility="no">
 
     <com.android.settingslib.widget.MainSwitchBar
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index d895c87..3394874 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -36,7 +36,8 @@
  * This component is used as the main switch of the page
  * to enable or disable the prefereces on the page.
  */
-public class MainSwitchPreference extends TwoStatePreference implements OnCheckedChangeListener {
+public class MainSwitchPreference extends TwoStatePreference
+        implements OnCheckedChangeListener, GroupSectionDividerMixin {
 
     private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>();
 
diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
index e27838c..1412c84 100644
--- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
+++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
@@ -27,6 +27,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.atomic.AtomicBoolean
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -53,7 +54,7 @@
         enableCatalystScreen()
         assertThat(preferenceScreenCreator.isFlagEnabled(context)).isTrue()
         val catalystScreen = dumpPreferenceScreen()
-        Log.i("Catalyst", catalystScreen)
+        Log.i(TAG, catalystScreen)
 
         disableCatalystScreen()
         assertThat(preferenceScreenCreator.isFlagEnabled(context)).isFalse()
@@ -83,11 +84,26 @@
     }
 
     private fun dumpPreferenceScreen(): String {
+        // Dump threads for troubleshooting when the test thread is stuck.
+        // Latest junit Timeout rule supports similar feature but it is not yet available on AOSP.
+        val taskFinished = AtomicBoolean()
+        Thread {
+                Thread.sleep(20000)
+                if (!taskFinished.get()) dumpThreads()
+            }
+            .apply {
+                isDaemon = true
+                start()
+            }
+
         @Suppress("UNCHECKED_CAST")
         val clazz = preferenceScreenCreator.fragmentClass() as Class<PreferenceFragmentCompat>
         val builder = StringBuilder()
         FragmentScenario.launch(clazz).use {
-            it.onFragment { fragment -> fragment.preferenceScreen.toString(builder) }
+            it.onFragment { fragment ->
+                taskFinished.set(true)
+                fragment.preferenceScreen.toString(builder)
+            }
         }
         return builder.toString()
     }
@@ -120,4 +136,16 @@
         }
         builder.append(indent).append("}\n")
     }
+
+    companion object {
+        const val TAG = "CatalystScreenTestCase"
+
+        fun dumpThreads() {
+            for ((thread, stack) in Thread.getAllStackTraces()) {
+                Log.i(TAG, "$thread")
+                for (frame in stack) Log.i(TAG, "  $frame")
+                Log.i(TAG, "")
+            }
+        }
+    }
 }
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt
new file mode 100644
index 0000000..ba5f5cf
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.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.settingslib.widget
+
+/**
+ * A base interface to indicate that a class should not have rounded corners.
+ *
+ * Classes implementing this interface will be treated as already handle rounded corners.
+ */
+interface GroupSectionDividerMixin
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
new file mode 100644
index 0000000..535d80f
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.settingslib.widget
+
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.PreferenceScreen
+import androidx.recyclerview.widget.RecyclerView
+
+/** Base class for Settings to use PreferenceFragmentCompat */
+open abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
+
+    override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> {
+        if (SettingsThemeHelper.isExpressiveTheme(requireContext()))
+            return SettingsPreferenceGroupAdapter(preferenceScreen)
+        return super.onCreateAdapter(preferenceScreen)
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
new file mode 100644
index 0000000..98b7f76
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -0,0 +1,206 @@
+/*
+ * 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.settingslib.widget
+
+import android.os.Handler
+import android.os.Looper
+import androidx.annotation.DrawableRes
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceGroupAdapter
+import androidx.preference.PreferenceViewHolder
+import com.android.settingslib.widget.theme.R
+
+/**
+ * A custom adapter for displaying settings preferences in a list, handling rounded corners
+ * for preference items within a group.
+ */
+open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
+    preferenceGroup: PreferenceGroup
+) : PreferenceGroupAdapter(preferenceGroup) {
+
+    private val mPreferenceGroup = preferenceGroup
+    private var mRoundCornerMappingList: ArrayList<Int> = ArrayList()
+
+    private var mNormalPaddingStart = 0
+    private var mGroupPaddingStart = 0
+    private var mNormalPaddingEnd = 0
+    private var mGroupPaddingEnd = 0
+
+    private val mHandler = Handler(Looper.getMainLooper())
+
+    private val syncRunnable = Runnable { updatePreferences() }
+
+    init {
+        val context = preferenceGroup.context
+        mNormalPaddingStart =
+            context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1)
+        mGroupPaddingStart = mNormalPaddingStart * 2
+        mNormalPaddingEnd =
+            context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1)
+        mGroupPaddingEnd = mNormalPaddingEnd * 2
+        updatePreferences()
+    }
+
+    override fun onPreferenceHierarchyChange(preference: Preference) {
+        super.onPreferenceHierarchyChange(preference)
+
+        // Post after super class has posted their sync runnable to update preferences.
+        mHandler.removeCallbacks(syncRunnable)
+        mHandler.post(syncRunnable)
+    }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) {
+        super.onBindViewHolder(holder, position)
+        updateBackground(holder, position)
+    }
+
+    private fun updatePreferences() {
+        val oldList = ArrayList(mRoundCornerMappingList)
+        mRoundCornerMappingList = ArrayList()
+        mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup)
+        if (mRoundCornerMappingList != oldList) {
+            notifyDataSetChanged()
+        }
+    }
+
+    private fun mappingPreferenceGroup(cornerStyles: MutableList<Int>, group: PreferenceGroup) {
+        cornerStyles.clear()
+        cornerStyles.addAll(MutableList(itemCount) { 0 })
+
+        // the first item in to group
+        var startIndex = -1
+        // the last item in the group
+        var endIndex = -1
+        var currentParent: PreferenceGroup? = group
+        for (i in 0 until itemCount) {
+            when (val pref = getItem(i)) {
+                // the preference has round corner background, so we don't need to handle it.
+                is GroupSectionDividerMixin -> {
+                    cornerStyles[i] = 0
+                    startIndex = -1
+                    endIndex = -1
+                }
+
+                // PreferenceCategory should not have round corner background.
+                is PreferenceCategory -> {
+                    cornerStyles[i] = 0
+                    startIndex = -1
+                    endIndex = -1
+                    currentParent = pref
+                }
+
+                // ExpandablePreference is PreferenceGroup but it should handle round corner
+                is Expandable -> {
+                    // When ExpandablePreference is expanded, we treat is as the first item.
+                    if (pref.isExpanded()) {
+                        currentParent = pref as? PreferenceGroup
+                        startIndex = i
+                        cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP or ROUND_CORNER_CENTER
+                        endIndex = -1
+                    }
+                }
+
+                else -> {
+                    val parent = pref?.parent
+
+                    // item in the group should have round corner background.
+                    cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_CENTER
+                    if (parent === currentParent) {
+                        // find the first item in the group
+                        if (startIndex == -1) {
+                            startIndex = i
+                            cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP
+                        }
+
+                        // find the last item in the group, if we find the new last item, we should
+                        // remove the old last item round corner.
+                        if (endIndex == -1 || endIndex < i) {
+                            if (endIndex != -1) {
+                                cornerStyles[endIndex] =
+                                    cornerStyles[endIndex] and ROUND_CORNER_BOTTOM.inv()
+                            }
+                            endIndex = i
+                            cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM
+                        }
+                    } else {
+                        // this item is new group, we should reset the index.
+                        currentParent = parent
+                        startIndex = i
+                        cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP
+                        endIndex = i
+                        cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM
+                    }
+                }
+            }
+        }
+    }
+
+    /** handle roundCorner background  */
+    private fun updateBackground(holder: PreferenceViewHolder, position: Int) {
+        @DrawableRes val backgroundRes = getRoundCornerDrawableRes(position, false /* isSelected*/)
+
+        val v = holder.itemView
+        val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart
+        val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd
+
+        v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom)
+        v.setBackgroundResource(backgroundRes)
+    }
+
+    @DrawableRes
+    protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int {
+        val cornerType = mRoundCornerMappingList[position]
+
+        if ((cornerType and ROUND_CORNER_CENTER) == 0) {
+            return 0
+        }
+
+        return when {
+            (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) == 0 -> {
+                // the first
+                if (isSelected) R.drawable.settingslib_round_background_top_selected
+                else R.drawable.settingslib_round_background_top
+            }
+
+            (cornerType and ROUND_CORNER_BOTTOM) != 0 && (cornerType and ROUND_CORNER_TOP) == 0 -> {
+                // the last
+                if (isSelected) R.drawable.settingslib_round_background_bottom_selected
+                else R.drawable.settingslib_round_background_bottom
+            }
+
+            (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) != 0 -> {
+                // the only one preference
+                if (isSelected) R.drawable.settingslib_round_background_selected
+                else R.drawable.settingslib_round_background
+            }
+
+            else -> {
+                // in the center
+                if (isSelected) R.drawable.settingslib_round_background_center_selected
+                else R.drawable.settingslib_round_background_center
+            }
+        }
+    }
+
+    companion object {
+        private const val ROUND_CORNER_CENTER: Int = 1
+        private const val ROUND_CORNER_TOP: Int = 1 shl 1
+        private const val ROUND_CORNER_BOTTOM: Int = 1 shl 2
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
index 9b1ccef..62573fe 100644
--- a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
+++ b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
@@ -32,7 +32,7 @@
     attrs: AttributeSet? = null,
     defStyleAttr: Int = 0,
     defStyleRes: Int = 0
-) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
 
     private val iconTint: Int = context.getColor(
         com.android.settingslib.widget.theme.R.color.settingslib_materialColorOnSecondaryContainer
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 79c3ff9..5a8763f 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -149,3 +149,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "audio_sharing_developer_option"
+    namespace: "cross_device_experiences"
+    description: "Gates whether to enable audio sharing developer option"
+    bug: "368401233"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/StatementService/Parser/Android.bp b/packages/StatementService/Parser/Android.bp
new file mode 100644
index 0000000..c8af134
--- /dev/null
+++ b/packages/StatementService/Parser/Android.bp
@@ -0,0 +1,26 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "StatementServiceParser",
+    use_resource_processor: true,
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    target_sdk_version: "29",
+}
diff --git a/packages/StatementService/Parser/AndroidManifest.xml b/packages/StatementService/Parser/AndroidManifest.xml
new file mode 100644
index 0000000..a3a99ac
--- /dev/null
+++ b/packages/StatementService/Parser/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?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.statementservice.parser">
+</manifest>
\ No newline at end of file
diff --git a/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt
new file mode 100644
index 0000000..4314f86
--- /dev/null
+++ b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+@file:JvmName("DalComponentParser")
+
+package com.android.statementservice.parser
+
+import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
+import android.os.PatternMatcher.PATTERN_LITERAL
+import android.os.PatternMatcher.PATTERN_PREFIX
+import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
+
+/**
+ * Parses a DAL component matching expression to Android's {@link android.os.PatternMatcher} type
+ * and pattern. Matching expressions support the following wildcards:
+ *
+ *  1) An asterisk (*) matches zero to as many characters as possible
+ *  2) A question mark (?) matches any single character.
+ *
+ * Matching one to many characters can be done with a question mark followed by an asterisk (?+).
+ *
+ * @param expression A matching expression string from a DAL relation extension component used for
+ *                   matching a URI part. This must be a non-empty string and all characters in the
+ *                   string should be decoded.
+ *
+ * @return Returns a Pair containing a {@link android.os.PatternMatcher} type and pattern.
+ */
+fun parseMatchingExpression(expression: String): Pair<Int, String> {
+    if (expression.isNullOrEmpty()) {
+        throw IllegalArgumentException("Matching expressions cannot be an empty string")
+    }
+    var count = 0
+    var isAdvanced = expression.contains("?*")
+    val pattern = buildString {
+        for (char in expression) {
+            when (char) {
+                '*' -> {
+                    if (this.endsWith('.') && !this.endsWith("\\.")) {
+                        append('+')
+                    } else {
+                        count += 1
+                        append(".*")
+                    }
+                }
+                '?' -> {
+                    count += 1
+                    append('.')
+                }
+                '.' -> {
+                    append("\\.")
+                }
+                '[', ']', '{', '}' -> {
+                    if (isAdvanced) {
+                        append('\\')
+                    }
+                    append(char)
+                }
+                else -> append(char)
+            }
+        }
+    }
+    if (count == 0) {
+        return Pair(PATTERN_LITERAL, pattern)
+    }
+    if (count == 1 && pattern.endsWith(".*")) {
+        return Pair(PATTERN_PREFIX, pattern.dropLast(2))
+    }
+    if (isAdvanced) {
+        return Pair(PATTERN_ADVANCED_GLOB, pattern)
+    }
+    return Pair(PATTERN_SIMPLE_GLOB, pattern)
+}
\ No newline at end of file
diff --git a/packages/StatementService/TEST_MAPPING b/packages/StatementService/TEST_MAPPING
new file mode 100644
index 0000000..0714c93
--- /dev/null
+++ b/packages/StatementService/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit" : [
+    {
+      "name": "StatementServiceTests"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/StatementService/tests/Android.bp b/packages/StatementService/tests/Android.bp
new file mode 100644
index 0000000..ec1bd96
--- /dev/null
+++ b/packages/StatementService/tests/Android.bp
@@ -0,0 +1,30 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_framework_android_packages",
+}
+
+android_test {
+    name: "StatementServiceTests",
+    use_resource_processor: true,
+    test_suites: ["general-tests"],
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.runner",
+        "StatementServiceParser",
+        "truth",
+    ],
+}
diff --git a/packages/StatementService/tests/AndroidManifest.xml b/packages/StatementService/tests/AndroidManifest.xml
new file mode 100644
index 0000000..bde0f95
--- /dev/null
+++ b/packages/StatementService/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.statementservice.test">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.statementservice.test" />
+</manifest>
diff --git a/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt
new file mode 100644
index 0000000..44a56ec
--- /dev/null
+++ b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.statementservice.parser
+
+import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
+import android.os.PatternMatcher.PATTERN_LITERAL
+import android.os.PatternMatcher.PATTERN_PREFIX
+import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DalComponentParserTest {
+
+    @Test
+    fun parseExpressions() {
+        validateParsedExpression("foobar", PATTERN_LITERAL, "foobar")
+        validateParsedExpression("foo.bar", PATTERN_LITERAL, "foo\\.bar")
+        validateParsedExpression("foo*", PATTERN_PREFIX, "foo")
+        validateParsedExpression("*bar", PATTERN_SIMPLE_GLOB, ".*bar")
+        validateParsedExpression("foo*bar", PATTERN_SIMPLE_GLOB, "foo.*bar")
+        validateParsedExpression("foo.*bar", PATTERN_SIMPLE_GLOB, "foo\\..*bar")
+        validateParsedExpression("*foo*bar", PATTERN_SIMPLE_GLOB, ".*foo.*bar")
+        validateParsedExpression("foo?bar", PATTERN_SIMPLE_GLOB, "foo.bar")
+        validateParsedExpression("foo.?bar", PATTERN_SIMPLE_GLOB, "foo\\..bar")
+        validateParsedExpression("?bar", PATTERN_SIMPLE_GLOB, ".bar")
+        validateParsedExpression("foo?", PATTERN_SIMPLE_GLOB, "foo.")
+        validateParsedExpression("fo?b*r", PATTERN_SIMPLE_GLOB, "fo.b.*r")
+        validateParsedExpression("?*bar", PATTERN_ADVANCED_GLOB, ".+bar")
+        validateParsedExpression("foo?*bar", PATTERN_ADVANCED_GLOB, "foo.+bar")
+        validateParsedExpression("foo?*bar*", PATTERN_ADVANCED_GLOB, "foo.+bar.*")
+        validateParsedExpression("foo*?bar", PATTERN_SIMPLE_GLOB, "foo.*.bar")
+
+        // set matches are not supported in DAL
+        validateParsedExpression("foo[a-z]", PATTERN_LITERAL, "foo[a-z]")
+        validateParsedExpression("foo[a-z]+", PATTERN_LITERAL, "foo[a-z]+")
+        validateParsedExpression("foo[a-z]*", PATTERN_PREFIX, "foo[a-z]")
+        validateParsedExpression("[a-z]*bar", PATTERN_SIMPLE_GLOB, "[a-z].*bar")
+        validateParsedExpression("foo[a-z]?bar", PATTERN_SIMPLE_GLOB, "foo[a-z].bar")
+        validateParsedExpression("foo[a-z]?*bar", PATTERN_ADVANCED_GLOB, "foo\\[a-z\\].+bar")
+
+        // range matches are not supported in DAL
+        validateParsedExpression("fo{2}", PATTERN_LITERAL, "fo{2}")
+        validateParsedExpression("fo{2}+", PATTERN_LITERAL, "fo{2}+")
+        validateParsedExpression("fo{2}*", PATTERN_PREFIX, "fo{2}")
+        validateParsedExpression("fo{2}*bar", PATTERN_SIMPLE_GLOB, "fo{2}.*bar")
+        validateParsedExpression("fo{2}?*", PATTERN_ADVANCED_GLOB, "fo\\{2\\}.+")
+        validateParsedExpression("foo{2}?*bar", PATTERN_ADVANCED_GLOB, "foo\\{2\\}.+bar")
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun parseEmptyExpression() {
+        parseMatchingExpression("")
+    }
+
+    private fun validateParsedExpression(given: String, expectedType: Int, expectedFilter: String) {
+        val (type, filter) = parseMatchingExpression(given)
+        assertThat(filter).isEqualTo(expectedFilter)
+        assertThat(type).isEqualTo(expectedType)
+        assertThat(filter).isEqualTo(expectedFilter)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2863531..3c560fd 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -87,7 +87,6 @@
     srcs: [
         "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
         "tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
-        "tests/src/**/systemui/doze/DozeMachineTest.java",
         "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
         "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
         "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
@@ -175,7 +174,6 @@
         "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt",
         "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt",
         "tests/src/**/systemui/controls/ui/DetailDialogTest.kt",
-        "tests/src/**/systemui/doze/DozeMachineTest.kt",
         "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt",
         "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
         "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java",
@@ -318,9 +316,6 @@
         "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt",
         "tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt",
         "tests/src/**/keyguard/ClockEventControllerTest.kt",
-        "tests/src/**/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt",
-        "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.kt",
-        "tests/src/**/keyguard/LegacyLockIconViewControllerTest.java",
         "tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt",
@@ -417,7 +412,6 @@
         "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
         "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
         "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
-        "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.java",
         "tests/src/**/keyguard/CarrierTextManagerTest.java",
         "tests/src/**/systemui/ScreenDecorationsTest.java",
         "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
@@ -899,9 +893,9 @@
         "androidx.compose.runtime_runtime",
     ],
     libs: [
-        "android.test.runner.stubs.system",
-        "android.test.base.stubs.system",
-        "android.test.mock.stubs.system",
+        "android.test.runner.impl",
+        "android.test.base.impl",
+        "android.test.mock.impl",
         "truth",
     ],
 
@@ -936,9 +930,9 @@
         "androidx.compose.runtime_runtime",
     ],
     libs: [
-        "android.test.runner.stubs.system",
-        "android.test.base.stubs.system",
-        "android.test.mock.stubs.system",
+        "android.test.runner.impl",
+        "android.test.base.impl",
+        "android.test.mock.impl",
         "truth",
     ],
 
@@ -974,9 +968,9 @@
         "androidx.compose.runtime_runtime",
     ],
     libs: [
-        "android.test.runner.stubs.system",
-        "android.test.base.stubs.system",
-        "android.test.mock.stubs.system",
+        "android.test.runner.impl",
+        "android.test.base.impl",
+        "android.test.mock.impl",
     ],
     auto_gen_config: true,
     plugins: [
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1892944..540115d 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -395,9 +395,9 @@
 }
 
 flag {
-    name: "status_bar_ron_chips"
+    name: "status_bar_notification_chips"
     namespace: "systemui"
-    description: "Show rich ongoing notifications as chips in the status bar"
+    description: "Show promoted ongoing notifications as chips in the status bar"
     bug: "361346412"
 }
 
@@ -1487,3 +1487,13 @@
   description: "Enables notes role qs tile which opens default notes role app in app bubbles"
   bug: "357863750"
 }
+
+flag {
+   name: "media_projection_request_attribution_fix"
+   namespace: "systemui"
+   description: "Ensure MediaProjection consent requests are properly attributed"
+   bug: "373581993"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+   }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index 9dc9348..4cf2642 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -486,8 +486,8 @@
         endState: State,
         windowBackgroundLayer: GradientDrawable,
         fadeWindowBackgroundLayer: Boolean = true,
-        useSpring: Boolean = false,
         drawHole: Boolean = false,
+        useSpring: Boolean = false,
     ): Animation {
         val transitionContainer = controller.transitionContainer
         val transitionContainerOverlay = transitionContainer.overlay
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 96c47cc..4ab5261 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -765,7 +765,7 @@
                     alpha = { outlineAlpha },
                     modifier =
                         Modifier.requiredSize(dpSize).thenIf(
-                            dragDropState.draggingItemIndex != index
+                            dragDropState.draggingItemKey != item.key
                         ) {
                             Modifier.animateItem(
                                 placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
@@ -778,7 +778,7 @@
                         dragDropState = dragDropState,
                         selected = selected,
                         enabled = item.isWidgetContent(),
-                        index = index,
+                        key = item.key,
                     ) { isDragging ->
                         CommunalContent(
                             modifier = Modifier.fillMaxSize(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 101385f..0718bc3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -38,8 +38,9 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.round
 import androidx.compose.ui.unit.toOffset
 import androidx.compose.ui.unit.toSize
 import com.android.systemui.Flags.communalWidgetResizing
@@ -93,7 +94,7 @@
     private val scope: CoroutineScope,
     private val updateDragPositionForRemove: (offset: Offset) -> Boolean,
 ) {
-    var draggingItemIndex by mutableStateOf<Int?>(null)
+    var draggingItemKey by mutableStateOf<Any?>(null)
         private set
 
     var isDraggingToRemove by mutableStateOf(false)
@@ -105,6 +106,8 @@
     private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
     private var dragStartPointerOffset by mutableStateOf(Offset.Zero)
 
+    private var previousTargetItemKey: Any? = null
+
     internal val draggingItemOffset: Offset
         get() =
             draggingItemLayoutInfo?.let { item ->
@@ -112,7 +115,7 @@
             } ?: Offset.Zero
 
     private val draggingItemLayoutInfo: LazyGridItemInfo?
-        get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex }
+        get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.key == draggingItemKey }
 
     /**
      * Called when dragging is initiated.
@@ -137,7 +140,7 @@
             .firstItemAtOffset(normalizedOffset - contentOffset)
             ?.apply {
                 dragStartPointerOffset = normalizedOffset - this.offset.toOffset()
-                draggingItemIndex = index
+                draggingItemKey = key
                 draggingItemInitialOffset = this.offset.toOffset()
                 return true
             }
@@ -146,16 +149,19 @@
     }
 
     internal fun onDragInterrupted() {
-        draggingItemIndex?.let {
+        draggingItemKey?.let {
             if (isDraggingToRemove) {
-                contentListState.onRemove(it)
+                contentListState.onRemove(
+                    contentListState.list.indexOfFirst { it.key == draggingItemKey }
+                )
                 isDraggingToRemove = false
                 updateDragPositionForRemove(Offset.Zero)
             }
             // persist list editing changes on dragging ends
             contentListState.onSaveList()
-            draggingItemIndex = null
+            draggingItemKey = null
         }
+        previousTargetItemKey = null
         draggingItemDraggedDelta = Offset.Zero
         draggingItemInitialOffset = Offset.Zero
         dragStartPointerOffset = Offset.Zero
@@ -170,15 +176,29 @@
         val startOffset = draggingItem.offset.toOffset() + draggingItemOffset
         val endOffset = startOffset + draggingItem.size.toSize()
         val middleOffset = startOffset + (endOffset - startOffset) / 2f
+        val draggingBoundingBox =
+            IntRect(draggingItem.offset + draggingItemOffset.round(), draggingItem.size)
 
         val targetItem =
-            state.layoutInfo.visibleItemsInfo
-                .asSequence()
-                .filter { item -> contentListState.isItemEditable(item.index) }
-                .filter { item -> draggingItem.index != item.index }
-                .firstItemAtOffset(middleOffset)
+            if (communalWidgetResizing()) {
+                state.layoutInfo.visibleItemsInfo.findLast { item ->
+                    val itemBoundingBox = IntRect(item.offset, item.size)
+                    draggingItemKey != item.key &&
+                        contentListState.isItemEditable(item.index) &&
+                        draggingBoundingBox.contains(itemBoundingBox.center)
+                }
+            } else {
+                state.layoutInfo.visibleItemsInfo
+                    .asSequence()
+                    .filter { item -> contentListState.isItemEditable(item.index) }
+                    .filter { item -> draggingItem.index != item.index }
+                    .firstItemAtOffset(middleOffset)
+            }
 
-        if (targetItem != null) {
+        if (
+            targetItem != null &&
+                (!communalWidgetResizing() || targetItem.key != previousTargetItemKey)
+        ) {
             val scrollToIndex =
                 if (targetItem.index == state.firstVisibleItemIndex) {
                     draggingItem.index
@@ -187,6 +207,14 @@
                 } else {
                     null
                 }
+            if (communalWidgetResizing()) {
+                // Keep track of the previous target item, to avoid rapidly oscillating between
+                // items if the target item doesn't visually move as a result of the index change.
+                // In this case, even after the index changes, we'd still be colliding with the
+                // element, so it would be selected as the target item the next time this function
+                // runs again, which would trigger us to revert the index change we recently made.
+                previousTargetItemKey = targetItem.key
+            }
             if (scrollToIndex != null) {
                 scope.launch {
                     // this is needed to neutralize automatic keeping the first item first.
@@ -196,20 +224,17 @@
             } else {
                 contentListState.onMove(draggingItem.index, targetItem.index)
             }
-            draggingItemIndex = targetItem.index
             isDraggingToRemove = false
-        } else {
+        } else if (targetItem == null) {
             val overscroll = checkForOverscroll(startOffset, endOffset)
             if (overscroll != 0f) {
                 scrollChannel.trySend(overscroll)
             }
             isDraggingToRemove = checkForRemove(startOffset)
+            previousTargetItemKey = null
         }
     }
 
-    private val LazyGridItemInfo.offsetEnd: IntOffset
-        get() = this.offset + this.size
-
     /** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */
     private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float {
         return when {
@@ -237,7 +262,7 @@
     viewModel: BaseCommunalViewModel,
 ): Modifier {
     return this.then(
-        pointerInput(dragDropState, contentOffset) {
+        Modifier.pointerInput(dragDropState, contentOffset) {
             detectDragGesturesAfterLongPress(
                 onDrag = { change, offset ->
                     change.consume()
@@ -273,7 +298,7 @@
 @Composable
 fun LazyGridItemScope.DraggableItem(
     dragDropState: GridDragDropState,
-    index: Int,
+    key: Any,
     enabled: Boolean,
     selected: Boolean,
     modifier: Modifier = Modifier,
@@ -283,7 +308,7 @@
         return content(false)
     }
 
-    val dragging = index == dragDropState.draggingItemIndex
+    val dragging = key == dragDropState.draggingItemKey
     val itemAlpha: Float by
         animateFloatAsState(
             targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index 9444664..71230f9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -17,8 +17,9 @@
 package com.android.systemui.keyguard.ui.composable.modifier
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
@@ -41,15 +42,17 @@
     params: BurnInParameters,
     isClock: Boolean = false,
 ): Modifier {
-    val translationYState = remember { mutableStateOf(0F) }
-    viewModel.updateBurnInParams(params.copy(translationY = { translationYState.value }))
+    val cachedYTranslation = remember { mutableFloatStateOf(0f) }
+    LaunchedEffect(Unit) {
+        viewModel.updateBurnInParams(params.copy(translationY = { cachedYTranslation.floatValue }))
+    }
 
     val burnIn = viewModel.movement
     val translationX by
         burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
     val translationY by
         burnIn.map { it.translationY.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
-    translationYState.value = translationY
+    cachedYTranslation.floatValue = translationY
     val scaleViewModel by
         burnIn
             .map { BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly) }
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
index 234c7a0..bf15b4f 100644
--- a/packages/SystemUI/docs/scene.md
+++ b/packages/SystemUI/docs/scene.md
@@ -63,7 +63,7 @@
 NOTE: in case these instructions become stale and don't actually enable the
 framework, please make sure `SceneContainerFlag.isEnabled` in the
 [`SceneContainerFlag.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt)
-file evalutes to `true`.
+file evaluates to `true`.
 
 1.  Set a collection of **aconfig flags** to `true` by running the following
     commands:
@@ -73,7 +73,6 @@
     $ adb shell device_config override systemui com.android.systemui.migrate_clocks_to_blueprint true
     $ adb shell device_config override systemui com.android.systemui.notification_avalanche_throttle_hun true
     $ adb shell device_config override systemui com.android.systemui.predictive_back_sysui true
-    $ adb shell device_config override systemui com.android.systemui.device_entry_udfps_refactor true
     $ adb shell device_config override systemui com.android.systemui.scene_container true
     ```
 2.  **Restart** System UI by issuing the following command:
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
deleted file mode 100644
index 13306be..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import org.junit.Assert.assertFalse
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-class UdfpsBpViewControllerTest : SysuiTestCase() {
-
-    @JvmField @Rule var rule = MockitoJUnit.rule()
-
-    @Mock lateinit var udfpsBpView: UdfpsBpView
-    @Mock lateinit var statusBarStateController: StatusBarStateController
-    @Mock lateinit var shadeInteractor: ShadeInteractor
-    @Mock lateinit var systemUIDialogManager: SystemUIDialogManager
-    @Mock lateinit var dumpManager: DumpManager
-    @Mock lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
-
-    private lateinit var udfpsBpViewController: UdfpsBpViewController
-
-    @Before
-    fun setup() {
-        udfpsBpViewController =
-            UdfpsBpViewController(
-                udfpsBpView,
-                statusBarStateController,
-                shadeInteractor,
-                systemUIDialogManager,
-                dumpManager,
-                udfpsOverlayInteractor,
-            )
-    }
-
-    @TestableLooper.RunWithLooper(setAsMainLooper = true)
-    @Test
-    fun testShouldNeverPauseAuth() {
-        assertFalse(udfpsBpViewController.shouldPauseAuth())
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 437a4b3..21c6583 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -16,34 +16,17 @@
 
 package com.android.systemui.biometrics;
 
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
-import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.graphics.Rect;
-import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricRequestConstants;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.SensorProperties;
@@ -58,13 +41,9 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.testing.TestableLooper.RunWithLooper;
-import android.util.Pair;
-import android.view.HapticFeedbackConstants;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewRootImpl;
 import android.view.accessibility.AccessibilityManager;
 
@@ -75,15 +54,11 @@
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.biometrics.udfps.InteractionEvent;
-import com.android.systemui.biometrics.udfps.NormalizedTouchData;
 import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
-import com.android.systemui.biometrics.udfps.TouchProcessorResult;
 import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel;
 import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -102,7 +77,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.power.shared.model.WakeSleepReason;
 import com.android.systemui.power.shared.model.WakefulnessState;
-import com.android.systemui.res.R;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.VibratorHelper;
@@ -130,7 +104,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
-import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -204,16 +177,6 @@
     private FeatureFlags mFeatureFlags;
     // Stuff for configuring mocks
     @Mock
-    private UdfpsView mUdfpsView;
-    @Mock
-    private UdfpsBpView mBpView;
-    @Mock
-    private UdfpsFpmEmptyView mFpmEmptyView;
-    @Mock
-    private UdfpsKeyguardViewLegacy mKeyguardView;
-    private final UdfpsAnimationViewController mUdfpsKeyguardViewController =
-            mock(UdfpsKeyguardViewControllerLegacy.class);
-    @Mock
     private SystemUIDialogManager mSystemUIDialogManager;
     @Mock
     private ActivityTransitionAnimator mActivityTransitionAnimator;
@@ -241,8 +204,6 @@
     @Captor
     private ArgumentCaptor<View> mViewCaptor;
     @Captor
-    private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
-    @Captor
     private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
     @Captor
     private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
@@ -287,18 +248,9 @@
         mContext.getOrCreateTestableResources()
                 .addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false);
 
-        when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
-                .thenReturn(mUdfpsView);
-        when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
-                .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
-        when(mLayoutInflater.inflate(R.layout.udfps_bp_view, null))
-                .thenReturn(mBpView);
-        when(mLayoutInflater.inflate(R.layout.udfps_fpm_empty_view, null))
-                .thenReturn(mFpmEmptyView);
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
         when(mSessionTracker.getSessionId(anyInt())).thenReturn(
                 (new InstanceIdSequence(1 << 20)).newInstanceId());
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
 
         final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
         componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
@@ -327,7 +279,6 @@
         // Create a fake background executor.
         mBiometricExecutor = new FakeExecutor(new FakeSystemClock());
 
-        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         initUdfpsController(mOpticalProps);
     }
 
@@ -349,7 +300,6 @@
                 mFalsingManager,
                 mPowerManager,
                 mAccessibilityManager,
-                mLockscreenShadeTransitionController,
                 mScreenLifecycle,
                 mVibrator,
                 mUdfpsHapticsSimulator,
@@ -390,101 +340,6 @@
     }
 
     @Test
-    public void dozeTimeTick() throws RemoteException {
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-        mUdfpsController.dozeTimeTick();
-        verify(mUdfpsView).dozeTimeTick();
-    }
-
-    @Test
-    public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
-        // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN notify keyguard authenticate to dismiss the keyguard
-        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
-    }
-
-    @Test
-    public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
-        onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */);
-    }
-
-    @Test
-    public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice_ignoreStale()
-            throws RemoteException {
-        onActionMoveTouch_whenCanDismissLockScreen_entersDevice(true /* stale */);
-    }
-
-    public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice(boolean stale)
-            throws RemoteException {
-        // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // WHEN ACTION_MOVE is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
-        if (stale) {
-            mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
-            mFgExecutor.runAllReady();
-        }
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
-        mBiometricExecutor.runAllReady();
-        moveEvent.recycle();
-
-        // THEN notify keyguard authenticate to dismiss the keyguard
-        verify(mStatusBarKeyguardViewManager, stale ? never() : times(1))
-                .notifyKeyguardAuthenticated(anyBoolean());
-    }
-
-    @Test
-    public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException {
-        // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
-
-        // GIVEN that the overlay is showing
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.second);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
-        mBiometricExecutor.runAllReady();
-        moveEvent.recycle();
-
-        // THEN notify keyguard authenticate to dismiss the keyguard
-        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
-    }
-
-    @Test
     public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
         // GIVEN overlay was showing and the udfps bouncer is showing
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
@@ -524,61 +379,6 @@
     }
 
     @Test
-    public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception {
-        final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)};
-        final int[] displayWidth = new int[]{1080, 1440};
-        final int[] displayHeight = new int[]{1920, 2560};
-        final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]};
-        final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90};
-        final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0],
-                sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0],
-                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
-
-        for (int i1 = 0; i1 <= 1; ++i1) {
-            for (int i2 = 0; i2 <= 1; ++i2) {
-                for (int i3 = 0; i3 <= 1; ++i3) {
-                    for (int i4 = 0; i4 <= 1; ++i4) {
-                        for (int i5 = 0; i5 <= 1; ++i5) {
-                            final UdfpsOverlayParams newParams = new UdfpsOverlayParams(
-                                    sensorBounds[i1], sensorBounds[i1], displayWidth[i2],
-                                    displayHeight[i3], scaleFactor[i4], rotation[i5],
-                                    FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
-
-                            if (newParams.equals(oldParams)) {
-                                continue;
-                            }
-
-                            // Initialize the overlay with old parameters.
-                            mUdfpsController.updateOverlayParams(mOpticalProps, oldParams);
-
-                            // Show the overlay.
-                            reset(mWindowManager);
-                            mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID,
-                                    mOpticalProps.sensorId,
-                                    BiometricRequestConstants.REASON_ENROLL_ENROLLING,
-                                    mUdfpsOverlayControllerCallback);
-
-                            mFgExecutor.runAllReady();
-                            verify(mWindowManager).addView(mViewCaptor.capture(), any());
-                            when(mViewCaptor.getValue().getParent())
-                                    .thenReturn(mock(ViewGroup.class));
-
-                            // Update overlay parameters.
-                            reset(mWindowManager);
-                            mUdfpsController.updateOverlayParams(mOpticalProps, newParams);
-                            mFgExecutor.runAllReady();
-
-                            // Ensure the overlay was recreated.
-                            verify(mWindowManager).removeView(any());
-                            verify(mWindowManager).addView(any(), any());
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
     public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception {
         final Rect sensorBounds = new Rect(10, 10, 20, 20);
         final int displayWidth = 1080;
@@ -595,7 +395,6 @@
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
                 BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
-        verify(mWindowManager).addView(any(), any());
 
         // Update overlay with the same parameters.
         mUdfpsController.updateOverlayParams(mOpticalProps,
@@ -606,870 +405,4 @@
         // Ensure the overlay was not recreated.
         verify(mWindowManager, never()).removeView(any());
     }
-
-    private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor,
-            float major) {
-        MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
-        pp.id = 1;
-        MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
-        pc.x = x;
-        pc.y = y;
-        pc.touchMinor = minor;
-        pc.touchMajor = major;
-        return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp},
-                new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0);
-    }
-
-    private static class TestParams {
-        public final FingerprintSensorPropertiesInternal sensorProps;
-
-        TestParams(FingerprintSensorPropertiesInternal sensorProps) {
-            this.sensorProps = sensorProps;
-        }
-    }
-
-    private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) {
-        for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
-                mUltrasonicProps)) {
-            initUdfpsController(sensorProps);
-            testParamsConsumer.accept(new TestParams(sensorProps));
-        }
-    }
-
-    @Test
-    public void onTouch_propagatesTouchInNativeOrientationAndResolution() {
-        runWithAllParams(
-                this::onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized);
-    }
-
-    private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized(
-            TestParams testParams) throws RemoteException {
-        reset(mUdfpsView);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
-        final int pointerId = 0;
-        final int displayWidth = 1080;
-        final int displayHeight = 1920;
-        final float scaleFactor = 1f; // This means the native resolution is 1440x2560.
-        final float touchMinor = 10f;
-        final float touchMajor = 20f;
-        final float orientation = 30f;
-
-        // Expecting a touch at the very bottom right corner in native orientation and resolution.
-        final float expectedX = displayWidth / scaleFactor;
-        final float expectedY = displayHeight / scaleFactor;
-        final float expectedMinor = touchMinor / scaleFactor;
-        final float expectedMajor = touchMajor / scaleFactor;
-
-        // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
-        // GIVEN a valid touch on sensor
-        NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth,
-                displayHeight, touchMinor, touchMajor, orientation, 0L, 0L);
-        TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch(
-                InteractionEvent.DOWN, 1, touchData);
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorDownResult);
-
-        // Show the overlay.
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // Test ROTATION_0
-        mUdfpsController.updateOverlayParams(testParams.sensorProps,
-                new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_0,
-                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
-        MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
-                touchMajor);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
-                eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
-                anyBoolean());
-
-        // Test ROTATION_90
-        reset(mFingerprintManager);
-        mUdfpsController.updateOverlayParams(testParams.sensorProps,
-                new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_90,
-                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
-        event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
-                eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
-                anyBoolean());
-
-        // Test ROTATION_270
-        reset(mFingerprintManager);
-        mUdfpsController.updateOverlayParams(testParams.sensorProps,
-                new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_270,
-                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
-        event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
-                eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
-                anyBoolean());
-
-        // Test ROTATION_180
-        reset(mFingerprintManager);
-        mUdfpsController.updateOverlayParams(testParams.sensorProps,
-                new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_180,
-                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
-        // ROTATION_180 is not supported. It should be treated like ROTATION_0.
-        event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
-                eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-    @Test
-    public void fingerDown() {
-        runWithAllParams(this::fingerDownParameterized);
-    }
-
-    private void fingerDownParameterized(TestParams testParams) throws RemoteException {
-        reset(mUdfpsView, mFingerprintManager, mLatencyTracker,
-                mKeyguardUpdateMonitor);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
-                InteractionEvent.DOWN, 1 /* pointerId */, touchData);
-
-        initUdfpsController(testParams.sensorProps);
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        // WHEN ACTION_DOWN is received
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultDown);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN the touch provider is notified about onPointerDown.
-        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
-
-        // AND display configuration begins
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-            verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
-        } else {
-            verify(mLatencyTracker, never()).onActionStart(
-                    eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-            verify(mUdfpsView, never()).configureDisplay(any());
-        }
-        verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // AND onDisplayConfigured notifies FingerprintManager about onUiReady
-            mOnDisplayConfiguredCaptor.getValue().run();
-            mBiometricExecutor.runAllReady();
-            InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
-            inOrder.verify(mFingerprintManager).onUdfpsUiEvent(
-                    eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID),
-                    eq(testParams.sensorProps.sensorId));
-            inOrder.verify(mLatencyTracker).onActionEnd(
-                    eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-        } else {
-            verify(mFingerprintManager, never()).onUdfpsUiEvent(
-                    eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt());
-            verify(mLatencyTracker, never()).onActionEnd(
-                    eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-        }
-    }
-
-    @Test
-    public void aodInterrupt() {
-        runWithAllParams(this::aodInterruptParameterized);
-    }
-
-    private void aodInterruptParameterized(TestParams testParams) throws RemoteException {
-        mUdfpsController.cancelAodSendFingerUpAction();
-        reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor);
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        // GIVEN that the overlay is showing and screen is on and fp is running
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        // WHEN fingerprint is requested because of AOD interrupt
-        mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
-        mFgExecutor.runAllReady();
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // THEN display configuration begins
-            // AND onDisplayConfigured notifies FingerprintManager about onUiReady
-            verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
-            mOnDisplayConfiguredCaptor.getValue().run();
-        } else {
-            verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture());
-        }
-        mBiometricExecutor.runAllReady();
-
-        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
-    }
-
-    @Test
-    public void tryAodSendFingerUp_displayConfigurationChanges() {
-        runWithAllParams(this::cancelAodInterruptParameterized);
-    }
-
-    private void cancelAodInterruptParameterized(TestParams testParams) throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN AOD interrupt
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-            // WHEN it is cancelled
-            mUdfpsController.tryAodSendFingerUp();
-            // THEN the display is unconfigured
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-            // WHEN it is cancelled
-            mUdfpsController.tryAodSendFingerUp();
-            // THEN the display configuration is unchanged.
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void onFingerUp_displayConfigurationChange() {
-        runWithAllParams(this::onFingerUp_displayConfigurationParameterized);
-    }
-
-    private void onFingerUp_displayConfigurationParameterized(TestParams testParams)
-            throws RemoteException {
-        reset(mUdfpsView);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // GIVEN AOD interrupt
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-
-            // WHEN up-action received
-            when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                    touchProcessorResult.second);
-            MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
-            mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
-            mBiometricExecutor.runAllReady();
-            upEvent.recycle();
-            mFgExecutor.runAllReady();
-
-            // THEN the display is unconfigured
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
-            // WHEN up-action received
-            when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                    touchProcessorResult.second);
-            MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
-            mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
-            mBiometricExecutor.runAllReady();
-            upEvent.recycle();
-            mFgExecutor.runAllReady();
-
-            // THEN the display configuration is unchanged.
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void onAcquiredGood_displayConfigurationChange() {
-        runWithAllParams(this::onAcquiredGood_displayConfigurationParameterized);
-    }
-
-    private void onAcquiredGood_displayConfigurationParameterized(TestParams testParams)
-            throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN overlay is showing
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-            // WHEN ACQUIRED_GOOD received
-            mOverlayController.onAcquired(testParams.sensorProps.sensorId,
-                    BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
-            mFgExecutor.runAllReady();
-            // THEN the display is unconfigured
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-            // WHEN ACQUIRED_GOOD received
-            mOverlayController.onAcquired(testParams.sensorProps.sensorId,
-                    BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
-            mFgExecutor.runAllReady();
-            // THEN the display configuration is unchanged.
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void aodInterruptTimeout() {
-        runWithAllParams(this::aodInterruptTimeoutParameterized);
-    }
-
-    private void aodInterruptTimeoutParameterized(TestParams testParams) throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN AOD interrupt
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        mFgExecutor.runAllReady();
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-        }
-        // WHEN it times out
-        mFgExecutor.advanceClockToNext();
-        mFgExecutor.runAllReady();
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // THEN the display is unconfigured.
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            // THEN the display configuration is unchanged.
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void aodInterruptCancelTimeoutActionOnFingerUp() {
-        runWithAllParams(this::aodInterruptCancelTimeoutActionOnFingerUpParameterized);
-    }
-
-    private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams)
-            throws RemoteException {
-        reset(mUdfpsView);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        // GIVEN AOD interrupt
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        mFgExecutor.runAllReady();
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // Configure UdfpsView to accept the ACTION_UP event
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-        }
-
-        // WHEN ACTION_UP is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.second);
-        MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
-        mBiometricExecutor.runAllReady();
-        upEvent.recycle();
-
-        // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        mFgExecutor.runAllReady();
-
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // Configure UdfpsView to accept the finger up event
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-        }
-
-        // WHEN it times out
-        mFgExecutor.advanceClockToNext();
-        mFgExecutor.runAllReady();
-
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // THEN the display should be unconfigured once. If the timeout action is not
-            // cancelled, the display would be unconfigured twice which would cause two
-            // FP attempts.
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void aodInterruptScreenOff() {
-        runWithAllParams(this::aodInterruptScreenOffParameterized);
-    }
-
-    private void aodInterruptScreenOffParameterized(TestParams testParams) throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN screen off
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOff();
-        mFgExecutor.runAllReady();
-
-        // WHEN aod interrupt is received
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-
-        // THEN display doesn't get configured because it's off
-        verify(mUdfpsView, never()).configureDisplay(any());
-    }
-
-    @Test
-    public void aodInterrupt_fingerprintNotRunning() {
-        runWithAllParams(this::aodInterrupt_fingerprintNotRunningParameterized);
-    }
-
-    private void aodInterrupt_fingerprintNotRunningParameterized(TestParams testParams)
-            throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN showing overlay
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-
-        // WHEN aod interrupt is received when the fingerprint service isn't running
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-
-        // THEN display doesn't get configured because it's off
-        verify(mUdfpsView, never()).configureDisplay(any());
-    }
-
-    @Test
-    public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true);
-
-        // WHEN ACTION_HOVER is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
-        MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
-        mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent);
-        enterEvent.recycle();
-
-        // THEN context click haptic is played
-        verify(mVibrator).performHapticFeedback(
-                any(),
-                eq(HapticFeedbackConstants.CONTEXT_CLICK)
-        );
-    }
-
-    @Test
-    public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN NO haptic played
-        verify(mVibrator, never()).performHapticFeedback(any(), anyInt());
-    }
-
-    @Test
-    public void fingerDown_falsingManagerInformed() throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN falsing manager is informed of the touch
-        verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION);
-    }
-
-    private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent(
-            InteractionEvent event1, InteractionEvent event2, boolean a11y)
-            throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
-                event1, 1 /* pointerId */, touchData);
-        final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch(
-                event2, 1 /* pointerId */, touchData);
-
-        // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
-        initUdfpsController(mOpticalProps);
-
-        // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
-        // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
-        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y);
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        if (a11y) {
-            verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
-        } else {
-            verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-        }
-
-        return new Pair<>(processorResultDown, processorResultUp);
-    }
-
-    @Test
-    public void onTouch_forwardToKeyguard() throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
-                InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData);
-
-        // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultDown);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-
-        // THEN the touch is forwarded to Keyguard
-        verify(mStatusBarKeyguardViewManager).onTouch(downEvent);
-    }
-
-    @Test
-    public void onTouch_pilferPointer() throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-
-        // WHEN ACTION_MOVE is received after
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.second);
-        event.setAction(ACTION_MOVE);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-
-        // THEN only pilfer once on the initial down
-        verify(mInputManager).pilferPointers(any());
-    }
-
-    @Test
-    public void onTouch_doNotPilferPointer() throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultUnchanged =
-                new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED,
-                        -1 /* pointerId */, touchData);
-
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // WHEN ACTION_DOWN is received and touch is not within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultUnchanged);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN the touch is NOT pilfered
-        verify(mInputManager, never()).pilferPointers(any());
-    }
-
-    @Test
-    public void onDownTouchReceivedWithoutPreviousUp() throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown =
-                new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
-                        -1 /* pointerId */, touchData);
-
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // WHEN ACTION_DOWN is received and touch is within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultDown);
-        MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent);
-        mBiometricExecutor.runAllReady();
-        firstDownEvent.recycle();
-
-        // And another ACTION_DOWN is received without an ACTION_UP before
-        MotionEvent secondDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, secondDownEvent);
-        mBiometricExecutor.runAllReady();
-        secondDownEvent.recycle();
-
-        // THEN the touch is still processed
-        verify(mFingerprintManager, times(2)).onPointerDown(anyLong(), anyInt(), anyInt(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-    @Test
-    public void onAodDownAndDownTouchReceived() throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown =
-                new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
-                        -1 /* pointerId */, touchData);
-
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // WHEN fingerprint is requested because of AOD interrupt
-        // GIVEN there's been an AoD interrupt
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-        mScreenObserver.onScreenTurnedOn();
-        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
-        mFgExecutor.runAllReady();
-
-        // and an ACTION_DOWN is received and touch is within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultDown);
-        MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent);
-        mBiometricExecutor.runAllReady();
-        firstDownEvent.recycle();
-
-        // THEN the touch is only processed once
-        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-    @Test
-    public void onTouch_pilferPointerWhenAltBouncerShowing()
-            throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
-
-        // WHEN alternate bouncer is showing
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // WHEN ACTION_DOWN is received and touch is not within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN the touch is pilfered
-        verify(mInputManager).pilferPointers(any());
-    }
-
-    @Test
-    public void onTouch_doNotProcessTouchWhenPullingUpBouncer()
-            throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
-
-        // GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS
-        when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true);
-        when(mLockscreenShadeTransitionController.getFractionToShade()).thenReturn(1f);
-
-        // WHEN ACTION_MOVE is received and touch is within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
-        mBiometricExecutor.runAllReady();
-        moveEvent.recycle();
-
-        // THEN the touch is NOT processed
-        verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-
-    @Test
-    public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible()
-            throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        // GIVEN swipe down for QS
-        when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false);
-        when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f);
-
-        // WHEN ACTION_MOVE is received and touch is within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
-        mBiometricExecutor.runAllReady();
-        moveEvent.recycle();
-
-        // THEN the touch is still processed
-        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-    @Test
-    public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
-        // GIVEN UDFPS overlay is showing
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        // GIVEN there's been an AoD interrupt
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-        mScreenObserver.onScreenTurnedOn();
-        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
-
-        // THEN finger is considered down
-        assertTrue(mUdfpsController.isFingerDown());
-
-        // WHEN udfps receives an ACQUIRED_GOOD after the display is configured
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-        verify(mFingerprintManager).setUdfpsOverlayController(
-                mUdfpsOverlayControllerCaptor.capture());
-        mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD);
-        mFgExecutor.runAllReady();
-
-        // THEN is fingerDown should be FALSE
-        assertFalse(mUdfpsController.isFingerDown());
-    }
-
-    @Test
-    public void playHaptic_onAodInterrupt_onAcquiredBad_performHapticFeedback()
-            throws RemoteException {
-        // GIVEN UDFPS overlay is showing
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        // GIVEN there's been an AoD interrupt
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
-        mScreenObserver.onScreenTurnedOn();
-        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
-
-        // THEN vibrate is used
-        verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
-    }
-
-    @Test
-    public void onAcquiredCalbacks() {
-        runWithAllParams(
-                this::ultrasonicCallbackOnAcquired);
-    }
-
-    public void ultrasonicCallbackOnAcquired(TestParams testParams) throws RemoteException{
-        if (testParams.sensorProps.sensorType
-                == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC) {
-            reset(mUdfpsView);
-
-            UdfpsController.Callback callbackMock = mock(UdfpsController.Callback.class);
-            mUdfpsController.addCallback(callbackMock);
-
-            // GIVEN UDFPS overlay is showing
-            mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                    BiometricRequestConstants.REASON_AUTH_KEYGUARD,
-                    mUdfpsOverlayControllerCallback);
-            mFgExecutor.runAllReady();
-
-            verify(mFingerprintManager).setUdfpsOverlayController(
-                    mUdfpsOverlayControllerCaptor.capture());
-            mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_START);
-            mFgExecutor.runAllReady();
-
-            verify(callbackMock).onFingerDown();
-
-            mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD);
-            mFgExecutor.runAllReady();
-
-            verify(callbackMock).onFingerUp();
-        }
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
deleted file mode 100644
index 7986051..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2020 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.biometrics;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityTransitionAnimator;
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionChangeEvent;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import org.junit.Before;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase {
-    // Dependencies
-    protected @Mock UdfpsKeyguardViewLegacy mView;
-    protected @Mock Context mResourceContext;
-    protected @Mock StatusBarStateController mStatusBarStateController;
-    protected @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
-    protected @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    protected @Mock LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    protected @Mock DumpManager mDumpManager;
-    protected @Mock DelayableExecutor mExecutor;
-    protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    protected @Mock KeyguardStateController mKeyguardStateController;
-    protected @Mock KeyguardViewMediator mKeyguardViewMediator;
-    protected @Mock ConfigurationController mConfigurationController;
-    protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
-    protected @Mock SystemUIDialogManager mDialogManager;
-    protected @Mock UdfpsController mUdfpsController;
-    protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator;
-    protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-    protected @Mock ShadeInteractor mShadeInteractor;
-    protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
-    protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
-    protected @Mock SelectedUserInteractor mSelectedUserInteractor;
-    protected @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor;
-    protected @Mock UdfpsOverlayInteractor mUdfpsOverlayInteractor;
-
-    protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
-
-    protected UdfpsKeyguardViewControllerLegacy mController;
-
-    // Capture listeners so that they can be used to send events
-    private @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
-    protected StatusBarStateController.StateListener mStatusBarStateListener;
-
-    private @Captor ArgumentCaptor<KeyguardStateController.Callback>
-            mKeyguardStateControllerCallbackCaptor;
-    protected KeyguardStateController.Callback mKeyguardStateControllerCallback;
-
-    private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.KeyguardViewManagerCallback>
-            mKeyguardViewManagerCallbackArgumentCaptor;
-    protected StatusBarKeyguardViewManager.KeyguardViewManagerCallback mKeyguardViewManagerCallback;
-
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
-        when(mView.getContext()).thenReturn(mResourceContext);
-        when(mResourceContext.getString(anyInt())).thenReturn("test string");
-        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false);
-        when(mView.getUnpausedAlpha()).thenReturn(255);
-        when(mShadeExpansionStateManager.addExpansionListener(any())).thenReturn(
-                new ShadeExpansionChangeEvent(0, false, false));
-        mController = createUdfpsKeyguardViewController();
-    }
-
-    protected void sendStatusBarStateChanged(int statusBarState) {
-        mStatusBarStateListener.onStateChanged(statusBarState);
-    }
-
-    protected void captureStatusBarStateListeners() {
-        verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture());
-        mStatusBarStateListener = mStateListenerCaptor.getValue();
-    }
-
-    protected void captureKeyguardStateControllerCallback() {
-        verify(mKeyguardStateController).addCallback(
-                mKeyguardStateControllerCallbackCaptor.capture());
-        mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue();
-    }
-
-    public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
-        return createUdfpsKeyguardViewController(false);
-    }
-
-    public void captureKeyGuardViewManagerCallback() {
-        verify(mStatusBarKeyguardViewManager).addCallback(
-                mKeyguardViewManagerCallbackArgumentCaptor.capture());
-        mKeyguardViewManagerCallback = mKeyguardViewManagerCallbackArgumentCaptor.getValue();
-    }
-
-    protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController(
-            boolean useModernBouncer) {
-        UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy(
-                mView,
-                mStatusBarStateController,
-                mStatusBarKeyguardViewManager,
-                mKeyguardUpdateMonitor,
-                mDumpManager,
-                mLockscreenShadeTransitionController,
-                mConfigurationController,
-                mKeyguardStateController,
-                mUnlockedScreenOffAnimationController,
-                mDialogManager,
-                mUdfpsController,
-                mActivityTransitionAnimator,
-                mPrimaryBouncerInteractor,
-                mAlternateBouncerInteractor,
-                mUdfpsKeyguardAccessibilityDelegate,
-                mSelectedUserInteractor,
-                mKeyguardTransitionInteractor,
-                mShadeInteractor,
-                mUdfpsOverlayInteractor);
-        return controller;
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
deleted file mode 100644
index 98d8b05..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2020 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.biometrics;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.TestableLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class UdfpsKeyguardViewLegacyControllerTest extends
-        UdfpsKeyguardViewLegacyControllerBaseTest {
-    @Override
-    public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
-        return createUdfpsKeyguardViewController(/* useModernBouncer */ false);
-    }
-
-    @Test
-    public void testShouldPauseAuth_bouncerShowing() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
-        when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
-        when(mView.getUnpausedAlpha()).thenReturn(0);
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testRegistersStatusBarStateListenersOnAttached() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-    }
-
-    @Test
-    public void testViewControllerQueriesSBStateOnAttached() {
-        mController.onViewAttached();
-        verify(mStatusBarStateController).getState();
-
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
-        captureStatusBarStateListeners();
-
-        mController.onViewAttached();
-        verify(mView, atLeast(1)).setPauseAuth(true);
-    }
-
-    @Test
-    public void testListenersUnregisteredOnDetached() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-        captureKeyguardStateControllerCallback();
-        mController.onViewDetached();
-
-        verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
-        verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback);
-    }
-
-    @Test
-    public void testShouldPauseAuthUnpausedAlpha0() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        when(mView.getUnpausedAlpha()).thenReturn(0);
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldNotPauseAuthOnKeyguard() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        assertFalse(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void onBiometricAuthenticated_pauseAuth() {
-        // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard)
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        // WHEN biometric is authenticated
-        captureKeyguardStateControllerCallback();
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardStateControllerCallback.onUnlockedChanged();
-
-        // THEN pause auth
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldPauseAuthIsLaunchTransitionFadingAway() {
-        // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard)
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        // WHEN isLaunchTransitionFadingAway=true
-        captureKeyguardStateControllerCallback();
-        when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true);
-        mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged();
-
-        // THEN pause auth
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldPauseAuthOnShadeLocked() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
-
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldPauseAuthOnShade() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        // WHEN not on keyguard yet (shade = home)
-        sendStatusBarStateChanged(StatusBarState.SHADE);
-
-        // THEN pause auth
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldPauseAuthAnimatingScreenOffFromShade() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        // WHEN transitioning from home/shade => keyguard + animating screen off
-        mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD);
-        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true);
-
-        // THEN pause auth
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testDoNotPauseAuthAnimatingScreenOffFromLS() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        // WHEN animating screen off transition from LS => AOD
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true);
-
-        // THEN don't pause auth
-        assertFalse(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testOverrideShouldPauseAuthOnShadeLocked() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
-        assertTrue(mController.shouldPauseAuth());
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
deleted file mode 100644
index 29a6e56..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
-import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
-import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
-import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-@TestableLooper.RunWithLooper
-@kotlinx.coroutines.ExperimentalCoroutinesApi
-class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
-    UdfpsKeyguardViewLegacyControllerBaseTest() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private val keyguardBouncerRepository = kosmos.fakeKeyguardBouncerRepository
-    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
-
-    @Before
-    override fun setUp() {
-        allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
-        MockitoAnnotations.initMocks(this)
-        super.setUp()
-    }
-
-    override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewControllerLegacy {
-        mPrimaryBouncerInteractor = kosmos.primaryBouncerInteractor
-        mAlternateBouncerInteractor = kosmos.alternateBouncerInteractor
-        mKeyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
-        mUdfpsOverlayInteractor = kosmos.udfpsOverlayInteractor
-        return createUdfpsKeyguardViewController(/* useModernBouncer */ true)
-    }
-
-    @Test
-    fun bouncerExpansionChange_fadeIn() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            captureKeyguardStateControllerCallback()
-            Mockito.reset(mView)
-
-            // WHEN status bar expansion is 0
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            // THEN alpha is 0
-            verify(mView).unpausedAlpha = 0
-
-            job.cancel()
-        }
-
-    @Test
-    fun bouncerExpansionChange_pauseAuth() =
-        testScope.runTest {
-            // GIVEN view is attached + on the keyguard
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-            Mockito.reset(mView)
-
-            // WHEN panelViewExpansion changes to hide
-            whenever(mView.unpausedAlpha).thenReturn(0)
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            // THEN pause auth is updated to PAUSE
-            verify(mView, Mockito.atLeastOnce()).setPauseAuth(true)
-
-            job.cancel()
-        }
-
-    @Test
-    fun bouncerExpansionChange_unpauseAuth() =
-        testScope.runTest {
-            // GIVEN view is attached + on the keyguard + panel expansion is 0f
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-            Mockito.reset(mView)
-
-            // WHEN panelViewExpansion changes to expanded
-            whenever(mView.unpausedAlpha).thenReturn(255)
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
-            runCurrent()
-
-            // THEN pause auth is updated to NOT pause
-            verify(mView, Mockito.atLeastOnce()).setPauseAuth(false)
-
-            job.cancel()
-        }
-
-    @Test
-    fun shadeLocked_showAlternateBouncer_unpauseAuth() =
-        testScope.runTest {
-            // GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing)
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED)
-
-            // WHEN alternate bouncer is requested
-            val job = mController.listenForAlternateBouncerVisibility(this)
-            keyguardBouncerRepository.setAlternateVisible(true)
-            runCurrent()
-
-            // THEN udfps view will animate in & pause auth is updated to NOT pause
-            verify(mView).animateInUdfpsBouncer(any())
-            assertFalse(mController.shouldPauseAuth())
-
-            job.cancel()
-        }
-
-    /** After migration to MODERN_BOUNCER, replaces UdfpsKeyguardViewControllerTest version */
-    @Test
-    fun shouldPauseAuthBouncerShowing() =
-        testScope.runTest {
-            // GIVEN view attached and we're on the keyguard
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-
-            // WHEN the bouncer expansion is VISIBLE
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            // THEN UDFPS shouldPauseAuth == true
-            assertTrue(mController.shouldPauseAuth())
-
-            job.cancel()
-        }
-
-    @Test
-    fun shouldHandleTouchesChange() =
-        testScope.runTest {
-            val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
-
-            // GIVEN view is attached + on the keyguard
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-            whenever(mView.setPauseAuth(true)).thenReturn(true)
-            whenever(mView.unpausedAlpha).thenReturn(0)
-
-            // WHEN panelViewExpansion changes to expanded
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            // THEN UDFPS auth is paused and should not handle touches
-            assertThat(mController.shouldPauseAuth()).isTrue()
-            assertThat(shouldHandleTouches!!).isFalse()
-
-            job.cancel()
-        }
-
-    @Test
-    fun shouldHandleTouchesOnDetach() =
-        testScope.runTest {
-            val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
-
-            // GIVEN view is attached + on the keyguard
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-            whenever(mView.setPauseAuth(true)).thenReturn(true)
-            whenever(mView.unpausedAlpha).thenReturn(0)
-
-            // WHEN panelViewExpansion changes to expanded
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            mController.onViewDetached()
-
-            // THEN UDFPS auth is paused and should not handle touches
-            assertThat(mController.shouldPauseAuth()).isTrue()
-            assertThat(shouldHandleTouches!!).isFalse()
-
-            job.cancel()
-        }
-
-    @Test
-    fun fadeFromDialogSuggestedAlpha() =
-        testScope.runTest {
-            // GIVEN view is attached and status bar expansion is 1f
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
-            runCurrent()
-            Mockito.reset(mView)
-
-            // WHEN dialog suggested alpha is .6f
-            whenever(mView.dialogSuggestedAlpha).thenReturn(.6f)
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-
-            // THEN alpha is updated based on dialog suggested alpha
-            verify(mView).unpausedAlpha = (.6f * 255).toInt()
-
-            job.cancel()
-        }
-
-    @Test
-    fun transitionToFullShadeProgress() =
-        testScope.runTest {
-            // GIVEN view is attached and status bar expansion is 1f
-            mController.onViewAttached()
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
-            runCurrent()
-            Mockito.reset(mView)
-            whenever(mView.dialogSuggestedAlpha).thenReturn(1f)
-
-            // WHEN we're transitioning to the full shade
-            val transitionProgress = .6f
-            mController.setTransitionToFullShadeProgress(transitionProgress)
-
-            // THEN alpha is between 0 and 255
-            verify(mView).unpausedAlpha = ((1f - transitionProgress) * 255).toInt()
-
-            job.cancel()
-        }
-
-    @Test
-    fun aodToLockscreen_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForLockscreenAodTransitions(this)
-
-            // WHEN transitioning from lockscreen to aod
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(.3f),
-                    eq(.3f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
-                )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(1f),
-                    eq(1f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
-                )
-
-            job.cancel()
-        }
-
-    @Test
-    fun lockscreenToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForLockscreenAodTransitions(this)
-
-            // WHEN transitioning from lockscreen to aod
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(.3f),
-                    eq(.3f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
-                )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(1f),
-                    eq(1f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
-                )
-
-            job.cancel()
-        }
-
-    @Test
-    fun goneToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForGoneToAodTransition(this)
-
-            // WHEN transitioning from lockscreen to aod
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(.3f),
-                    eq(.3f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
-                )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(1f),
-                    eq(1f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
-                )
-
-            job.cancel()
-        }
-
-    @Test
-    fun aodToOccluded_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForAodToOccludedTransitions(this)
-
-            // WHEN transitioning from aod to occluded
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.OCCLUDED,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(eq(.7f), eq(.7f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE))
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.OCCLUDED,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE))
-
-            job.cancel()
-        }
-
-    @Test
-    fun occludedToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForOccludedToAodTransition(this)
-
-            // WHEN transitioning from occluded to aod
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.OCCLUDED,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(.3f),
-                    eq(.3f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
-                )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.OCCLUDED,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(1f),
-                    eq(1f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
-                )
-
-            job.cancel()
-        }
-
-    @Test
-    fun cancelledAodToLockscreen_dozeAmountChangedToZero() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            val job = mController.listenForLockscreenAodTransitions(this)
-            runCurrent()
-            Mockito.reset(mView)
-
-            // WHEN aod to lockscreen transition is cancelled
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.LOCKSCREEN,
-                    value = 1f,
-                    transitionState = TransitionState.CANCELED
-                )
-            )
-            runCurrent()
-            // ... and WHEN the next transition is from lockscreen => occluded
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.OCCLUDED,
-                    value = .4f,
-                    transitionState = TransitionState.STARTED
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is updated to zero
-            verify(mView)
-                .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
-            job.cancel()
-        }
-
-    @Test
-    fun cancelledLockscreenToAod_dozeAmountNotUpdatedToZero() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            val job = mController.listenForLockscreenAodTransitions(this)
-            runCurrent()
-            Mockito.reset(mView)
-
-            // WHEN lockscreen to aod transition is cancelled
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.CANCELED
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is NOT updated to zero
-            verify(mView, never()).onDozeAmountChanged(eq(0f), eq(0f), anyInt())
-            job.cancel()
-        }
-
-    @Test
-    fun dreamingToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForDreamingToAodTransitions(this)
-            // WHEN dreaming to aod transition in progress
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.DREAMING,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is updated to
-            verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF))
-            job.cancel()
-        }
-
-    @Test
-    fun alternateBouncerToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForAlternateBouncerToAodTransitions(this)
-            // WHEN alternate bouncer to aod transition in progress
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is updated to
-            verify(mView)
-                .onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
-            job.cancel()
-        }
-
-    @Test
-    fun bouncerToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForPrimaryBouncerToAodTransitions(this)
-            // WHEN alternate bouncer to aod transition in progress
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is updated to
-            verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF))
-            job.cancel()
-        }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 68cfa28..82ff617 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -16,12 +16,9 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.keyguardUpdateMonitor
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
@@ -61,12 +58,6 @@
         underTest = kosmos.alternateBouncerInteractor
     }
 
-    @Test(expected = IllegalStateException::class)
-    @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun enableUdfpsRefactor_deprecatedShowMethod_throwsIllegalStateException() {
-        underTest.show()
-    }
-
     @Test
     @DisableSceneContainer
     fun canShowAlternateBouncer_false_dueToTransitionState() =
@@ -101,21 +92,6 @@
         }
 
     @Test
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun canShowAlternateBouncerForFingerprint_givenCanShow() {
-        givenCanShowAlternateBouncer()
-        assertTrue(underTest.canShowAlternateBouncerForFingerprint())
-    }
-
-    @Test
-    fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() {
-        givenCanShowAlternateBouncer()
-        kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(false)
-
-        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
-    }
-
-    @Test
     fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() {
         givenCanShowAlternateBouncer()
         kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
@@ -140,15 +116,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun show_whenCanShow() {
-        givenCanShowAlternateBouncer()
-
-        assertTrue(underTest.show())
-        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
-    }
-
-    @Test
     fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() {
         givenCanShowAlternateBouncer()
         whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true)
@@ -165,15 +132,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun show_whenCannotShow() {
-        givenCannotShowAlternateBouncer()
-
-        assertFalse(underTest.show())
-        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
-    }
-
-    @Test
     fun hide_wasPreviouslyShowing() {
         kosmos.keyguardBouncerRepository.setAlternateVisible(true)
 
@@ -190,7 +148,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     fun canShowAlternateBouncerForFingerprint_rearFps() {
         givenCanShowAlternateBouncer()
         kosmos.fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer
@@ -198,29 +155,6 @@
         assertFalse(underTest.canShowAlternateBouncerForFingerprint())
     }
 
-    @Test
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun alternateBouncerUiAvailable_fromMultipleSources() {
-        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
-        // GIVEN there are two different sources indicating the alternate bouncer is available
-        underTest.setAlternateBouncerUIAvailable(true, "source1")
-        underTest.setAlternateBouncerUIAvailable(true, "source2")
-        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
-        // WHEN one of the sources no longer says the UI is available
-        underTest.setAlternateBouncerUIAvailable(false, "source1")
-
-        // THEN alternate bouncer UI is still available (from the other source)
-        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
-        // WHEN all sources say the UI is not available
-        underTest.setAlternateBouncerUIAvailable(false, "source2")
-
-        // THEN alternate boucer UI is not available
-        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-    }
-
     private fun givenAlternateBouncerSupported() {
         kosmos.givenAlternateBouncerSupported()
     }
@@ -228,8 +162,4 @@
     private fun givenCanShowAlternateBouncer() {
         kosmos.givenCanShowAlternateBouncer()
     }
-
-    private fun givenCannotShowAlternateBouncer() {
-        kosmos.givenCannotShowAlternateBouncer()
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index ee65fbd..1e86516 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal
 
+import android.os.UserHandle
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
@@ -77,7 +78,11 @@
     @Before
     fun setUp() {
         with(kosmos) {
-            fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT)
+            fakeSettings.putIntForUser(
+                Settings.System.SCREEN_OFF_TIMEOUT,
+                SCREEN_TIMEOUT,
+                UserHandle.USER_CURRENT,
+            )
             kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
 
             underTest =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
index fad52e0..ba43a23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -38,9 +38,9 @@
 
 import android.app.ActivityManager;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.UiThreadTest;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 2a2a82d..b5ea305 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -100,18 +99,14 @@
                 FakeTrustRepository(),
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
-                faceAuthInteractor
+                faceAuthInteractor,
             )
 
         alternateBouncerInteractor =
             AlternateBouncerInteractor(
-                mock(StatusBarStateController::class.java),
-                mock(KeyguardStateController::class.java),
                 bouncerRepository,
                 FakeFingerprintPropertyRepository(),
-                biometricSettingsRepository,
                 FakeSystemClock(),
-                keyguardUpdateMonitor,
                 { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
                 { mock(KeyguardInteractor::class.java) },
                 { mock(KeyguardTransitionInteractor::class.java) },
@@ -121,13 +116,12 @@
 
         underTest =
             DeviceEntrySideFpsOverlayInteractor(
-                testScope.backgroundScope,
                 mContext,
                 deviceEntryFingerprintAuthRepository,
                 kosmos.sceneInteractor,
                 primaryBouncerInteractor,
                 alternateBouncerInteractor,
-                keyguardUpdateMonitor
+                keyguardUpdateMonitor,
             )
     }
 
@@ -142,7 +136,7 @@
                 isShowing = true,
                 isAnimatingAway = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isTrue()
         }
@@ -158,7 +152,7 @@
                 isShowing = false,
                 isAnimatingAway = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -169,13 +163,12 @@
         testScope.runTest {
             underTest =
                 DeviceEntrySideFpsOverlayInteractor(
-                    testScope.backgroundScope,
                     mContext,
                     deviceEntryFingerprintAuthRepository,
                     kosmos.sceneInteractor,
                     primaryBouncerInteractor,
                     alternateBouncerInteractor,
-                    keyguardUpdateMonitor
+                    keyguardUpdateMonitor,
                 )
 
             val showIndicatorForDeviceEntry by
@@ -185,7 +178,7 @@
             updateBouncerScene(
                 isActive = true,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isTrue()
         }
@@ -196,13 +189,12 @@
         testScope.runTest {
             underTest =
                 DeviceEntrySideFpsOverlayInteractor(
-                    testScope.backgroundScope,
                     mContext,
                     deviceEntryFingerprintAuthRepository,
                     kosmos.sceneInteractor,
                     primaryBouncerInteractor,
                     alternateBouncerInteractor,
-                    keyguardUpdateMonitor
+                    keyguardUpdateMonitor,
                 )
 
             val showIndicatorForDeviceEntry by
@@ -212,7 +204,7 @@
             updateBouncerScene(
                 isActive = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -228,7 +220,7 @@
                 isShowing = true,
                 isAnimatingAway = false,
                 fpsDetectionRunning = false,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -245,7 +237,7 @@
                 isShowing = true,
                 isAnimatingAway = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = false
+                isUnlockingWithFpAllowed = false,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -261,7 +253,7 @@
             updateBouncerScene(
                 isActive = true,
                 fpsDetectionRunning = false,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -277,7 +269,7 @@
             updateBouncerScene(
                 isActive = true,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = false
+                isUnlockingWithFpAllowed = false,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -294,7 +286,7 @@
                 isShowing = true,
                 isAnimatingAway = true,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -325,7 +317,7 @@
                 isShowing = true,
                 isAnimatingAway = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
 
             // Another request to show indicator for deviceEntryFingerprintAuthRepository update
@@ -355,7 +347,7 @@
             .thenReturn(isUnlockingWithFpAllowed)
         mContext.orCreateTestableResources.addOverride(
             R.bool.config_show_sidefps_hint_on_bouncer,
-            true
+            true,
         )
     }
 
@@ -366,7 +358,7 @@
     ) {
         kosmos.sceneInteractor.changeScene(
             if (isActive) Scenes.Bouncer else Scenes.Lockscreen,
-            "reason"
+            "reason",
         )
 
         whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
@@ -375,7 +367,7 @@
             .thenReturn(isUnlockingWithFpAllowed)
         mContext.orCreateTestableResources.addOverride(
             R.bool.config_show_sidefps_hint_on_bouncer,
-            true
+            true,
         )
         runCurrent()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
index c4eabd8..3800608 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard.ui.binder
 
-import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import android.view.View
 import android.view.layoutInflater
@@ -24,7 +23,6 @@
 import android.view.windowManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -63,7 +61,7 @@
                 kosmos.mockedLayoutInflater.inflate(
                     eq(R.layout.alternate_bouncer),
                     isNull(),
-                    anyBoolean()
+                    anyBoolean(),
                 )
             )
             .thenReturn(mockedAltBouncerView)
@@ -71,7 +69,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     fun addViewToWindowManager() {
         testScope.runTest {
             kosmos.givenCanShowAlternateBouncer()
@@ -85,7 +82,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     fun viewRemovedImmediatelyIfAlreadyAttachedToWindow() {
         testScope.runTest {
             kosmos.givenCanShowAlternateBouncer()
@@ -107,7 +103,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     fun viewNotRemovedUntilAttachedToWindow() {
         testScope.runTest {
             kosmos.givenCanShowAlternateBouncer()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index c1bd378..5d95480 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -19,7 +19,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
@@ -34,7 +33,6 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @ExperimentalCoroutinesApi
 @RunWith(AndroidJUnit4::class)
@@ -49,7 +47,6 @@
     @Test
     fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
         testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
             fingerprintPropertyRepository.supportsUdfps()
@@ -64,28 +61,7 @@
             assertThat(alternateBouncerWindowRequired).isTrue()
 
             transitionRepository.sendTransitionSteps(
-                listOf(
-                    stepFromAlternateBouncer(1.0f, TransitionState.FINISHED),
-                ),
-                testScope,
-            )
-            assertThat(alternateBouncerWindowRequired).isFalse()
-        }
-
-    @Test
-    fun deviceEntryUdfpsFlagDisabled_alternateBouncerWindowRequiredFalse() =
-        testScope.runTest {
-            mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-            val alternateBouncerWindowRequired by
-                collectLastValue(underTest.alternateBouncerWindowRequired)
-            fingerprintPropertyRepository.supportsUdfps()
-            transitionRepository.sendTransitionSteps(
-                listOf(
-                    stepFromAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromAlternateBouncer(.4f),
-                    stepFromAlternateBouncer(.6f),
-                    stepFromAlternateBouncer(1f),
-                ),
+                listOf(stepFromAlternateBouncer(1.0f, TransitionState.FINISHED)),
                 testScope,
             )
             assertThat(alternateBouncerWindowRequired).isFalse()
@@ -94,7 +70,6 @@
     @Test
     fun lockscreenTransition_alternateBouncerWindowRequiredFalse() =
         testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
             fingerprintPropertyRepository.supportsUdfps()
@@ -113,7 +88,6 @@
     @Test
     fun rearFps_alternateBouncerWindowRequiredFalse() =
         testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
             fingerprintPropertyRepository.supportsRearFps()
@@ -131,7 +105,7 @@
 
     private fun stepFromAlternateBouncer(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return step(
             from = KeyguardState.ALTERNATE_BOUNCER,
@@ -143,7 +117,7 @@
 
     private fun stepFromDozingToLockscreen(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return step(
             from = KeyguardState.DOZING,
@@ -157,14 +131,14 @@
         from: KeyguardState,
         to: KeyguardState,
         value: Float,
-        transitionState: TransitionState
+        transitionState: TransitionState,
     ): TransitionStep {
         return TransitionStep(
             from = from,
             to = to,
             value = value,
             transitionState = transitionState,
-            ownerName = "AlternateBouncerViewModelTest"
+            ownerName = "AlternateBouncerViewModelTest",
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 7798f46..b23262a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -34,9 +34,9 @@
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.service.quicksettings.Tile;
-import android.testing.UiThreadTest;
 import android.widget.ImageView;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
new file mode 100644
index 0000000..cdf6bda
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileMapperTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
+    private val mapper by lazy {
+        HearingDevicesTileMapper(
+            context.orCreateTestableResources
+                .apply { addOverride(R.drawable.qs_hearing_devices_icon, TestStubDrawable()) }
+                .resources,
+            context.theme,
+        )
+    }
+
+    @Test
+    fun map_anyActiveHearingDevice_anyPairedHearingDevice_activeState() {
+        val tileState: QSTileState =
+            mapper.map(
+                qsTileConfig,
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = true,
+                    isAnyPairedHearingDevice = true,
+                ),
+            )
+        val expectedState =
+            createHearingDevicesTileState(
+                QSTileState.ActivationState.ACTIVE,
+                context.getString(R.string.quick_settings_hearing_devices_connected),
+            )
+        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun map_noActiveHearingDevice_anyPairedHearingDevice_inactiveState() {
+        val tileState: QSTileState =
+            mapper.map(
+                qsTileConfig,
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = false,
+                    isAnyPairedHearingDevice = true,
+                ),
+            )
+        val expectedState =
+            createHearingDevicesTileState(
+                QSTileState.ActivationState.INACTIVE,
+                context.getString(R.string.quick_settings_hearing_devices_disconnected),
+            )
+        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun map_noActiveHearingDevice_noPairedHearingDevice_inactiveState() {
+        val tileState: QSTileState =
+            mapper.map(
+                qsTileConfig,
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = false,
+                    isAnyPairedHearingDevice = false,
+                ),
+            )
+        val expectedState =
+            createHearingDevicesTileState(QSTileState.ActivationState.INACTIVE, secondaryLabel = "")
+        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+    }
+
+    private fun createHearingDevicesTileState(
+        activationState: QSTileState.ActivationState,
+        secondaryLabel: String,
+    ): QSTileState {
+        val label = context.getString(R.string.quick_settings_hearing_devices_label)
+        val iconRes = R.drawable.qs_hearing_devices_icon
+        return QSTileState(
+            { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+            iconRes,
+            label,
+            activationState,
+            secondaryLabel,
+            setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+            label,
+            null,
+            QSTileState.SideViewIcon.Chevron,
+            QSTileState.EnabledState.ENABLED,
+            Switch::class.qualifiedName,
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
new file mode 100644
index 0000000..1dfa2cd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.statusbar.policy.fakeBluetoothController
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val testUser = UserHandle.of(1)
+
+    private val controller = kosmos.fakeBluetoothController
+    private lateinit var underTest: HearingDevicesTileDataInteractor
+
+    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var checker: HearingDevicesChecker
+
+    @Before
+    fun setup() {
+        underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker)
+    }
+
+    @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+    @Test
+    fun availability_flagEnabled_returnTrue() =
+        testScope.runTest {
+            val availability by collectLastValue(underTest.availability(testUser))
+
+            assertThat(availability).isTrue()
+        }
+
+    @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+    @Test
+    fun availability_flagDisabled_returnFalse() =
+        testScope.runTest {
+            val availability by collectLastValue(underTest.availability(testUser))
+
+            assertThat(availability).isFalse()
+        }
+
+    @Test
+    fun tileData_bluetoothStateChanged_dataMatchesChecker() =
+        testScope.runTest {
+            val flowValues: List<HearingDevicesTileModel> by
+                collectValues(
+                    underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+            controller.isBluetoothEnabled = false
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+            controller.isBluetoothEnabled = true
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(2)
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
+            controller.isBluetoothEnabled = true
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(3)
+
+            assertThat(flowValues.map { it.isAnyPairedHearingDevice })
+                .containsExactly(false, true, true)
+                .inOrder()
+            assertThat(flowValues.map { it.isAnyActiveHearingDevice })
+                .containsExactly(false, false, true)
+                .inOrder()
+        }
+
+    @Test
+    fun tileData_bluetoothDeviceChanged_dataMatchesChecker() =
+        testScope.runTest {
+            val flowValues: List<HearingDevicesTileModel> by
+                collectValues(
+                    underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+            controller.onBluetoothDevicesChanged()
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+            controller.onBluetoothDevicesChanged()
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(2)
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
+            controller.onBluetoothDevicesChanged()
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(3)
+
+            assertThat(flowValues.map { it.isAnyPairedHearingDevice })
+                .containsExactly(false, true, true)
+                .inOrder()
+            assertThat(flowValues.map { it.isAnyActiveHearingDevice })
+                .containsExactly(false, false, true)
+                .inOrder()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..00ee1c3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.platform.test.annotations.EnabledOnRavenwood
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
+import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.verify
+
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val inputHandler = FakeQSTileIntentUserInputHandler()
+
+    private lateinit var underTest: HearingDevicesTileUserActionInteractor
+
+    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var dialogManager: HearingDevicesDialogManager
+
+    @Before
+    fun setUp() {
+        underTest =
+            HearingDevicesTileUserActionInteractor(
+                testScope.coroutineContext,
+                inputHandler,
+                dialogManager,
+            )
+    }
+
+    @Test
+    fun handleClick_launchDialog() =
+        testScope.runTest {
+            val input =
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = true,
+                    isAnyPairedHearingDevice = true,
+                )
+
+            underTest.handleInput(QSTileInputTestKtx.click(input))
+
+            verify(dialogManager).showDialog(anyOrNull(), eq(LAUNCH_SOURCE_QS_TILE))
+        }
+
+    @Test
+    fun handleLongClick_launchSettings() =
+        testScope.runTest {
+            val input =
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = true,
+                    isAnyPairedHearingDevice = true,
+                )
+
+            underTest.handleInput(QSTileInputTestKtx.longClick(input))
+
+            QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+                assertThat(it.intent.action).isEqualTo(Settings.ACTION_HEARING_DEVICES_SETTINGS)
+            }
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 6f2302a..9fe5299 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -30,7 +30,6 @@
 import android.view.ViewTreeObserver
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityContainerController
-import com.android.keyguard.LegacyLockIconViewController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
 import com.android.systemui.Flags
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
@@ -54,7 +53,6 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.DragDownHelper
@@ -71,7 +69,6 @@
 import com.android.systemui.statusbar.phone.DozeScrimController
 import com.android.systemui.statusbar.phone.DozeServiceHost
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -80,6 +77,7 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.emptyFlow
@@ -98,11 +96,10 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
-import java.util.Optional
-import org.mockito.Mockito.`when` as whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -125,12 +122,10 @@
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var ambientState: AmbientState
     @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
-    @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
     @Mock private lateinit var quickSettingsController: QuickSettingsControllerImpl
     @Mock
     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
-    @Mock private lateinit var lockIconViewController: LegacyLockIconViewController
     @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
     @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
     @Mock
@@ -144,7 +139,7 @@
     @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
     @Mock
     private lateinit var unfoldTransitionProgressProvider:
-            Optional<UnfoldTransitionProgressProvider>
+        Optional<UnfoldTransitionProgressProvider>
     @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
     @Mock lateinit var dragDownHelper: DragDownHelper
     @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -176,20 +171,17 @@
         MockitoAnnotations.initMocks(this)
         whenever(view.bottom).thenReturn(VIEW_BOTTOM)
         whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
-                .thenReturn(mock(ViewGroup::class.java))
+            .thenReturn(mock(ViewGroup::class.java))
         whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
-                .thenReturn(keyguardBouncerComponent)
+            .thenReturn(keyguardBouncerComponent)
         whenever(keyguardBouncerComponent.securityContainerController)
-                .thenReturn(keyguardSecurityContainerController)
+            .thenReturn(keyguardSecurityContainerController)
         whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
-                .thenReturn(emptyFlow<TransitionStep>())
+            .thenReturn(emptyFlow<TransitionStep>())
 
         featureFlagsClassic = FakeFeatureFlagsClassic()
         featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
         featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
-        if (!SceneContainerFlag.isEnabled) {
-            mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        }
         mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
 
         testScope = TestScope()
@@ -208,9 +200,7 @@
                 panelExpansionInteractor,
                 ShadeExpansionStateManager(),
                 stackScrollLayoutController,
-                statusBarKeyguardViewManager,
                 statusBarWindowStateController,
-                lockIconViewController,
                 centralSurfaces,
                 dozeServiceHost,
                 dozeScrimController,
@@ -233,7 +223,7 @@
                 quickSettingsController,
                 primaryBouncerInteractor,
                 alternateBouncerInteractor,
-                mock(BouncerViewBinder::class.java)
+                mock(BouncerViewBinder::class.java),
             )
         underTest.setupExpandedStatusBar()
         underTest.setDragDownHelper(dragDownHelper)
@@ -294,7 +284,7 @@
             whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(true)
+                .thenReturn(true)
             whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true)
 
             val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -309,7 +299,7 @@
             underTest.setStatusBarViewController(phoneStatusBarViewController)
             whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(true)
+                .thenReturn(true)
             // Item we're testing
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(false)
 
@@ -327,7 +317,7 @@
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
             // Item we're testing
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(false)
+                .thenReturn(false)
 
             val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
 
@@ -341,7 +331,7 @@
             underTest.setStatusBarViewController(phoneStatusBarViewController)
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(true)
+                .thenReturn(true)
             // Item we're testing
             whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
 
@@ -358,7 +348,7 @@
             whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(true)
+                .thenReturn(true)
 
             // Down event first
             interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -379,7 +369,7 @@
             // GIVEN touch dispatcher in a state that returns true
             underTest.setStatusBarViewController(phoneStatusBarViewController)
             whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
-                    .thenReturn(true)
+                .thenReturn(true)
             assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
 
             // WHEN launch animation is running for 2 seconds
@@ -432,47 +422,13 @@
     }
 
     @Test
-    fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() {
-        // down event should be intercepted by keyguardViewManager
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(true)
-
-        // Then touch should not be intercepted
-        val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
-        assertThat(shouldIntercept).isTrue()
-    }
-
-    @Test
-    @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-    fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() {
-        // GIVEN dozing
-        whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
-        // AND alternate bouncer doesn't want the touch
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
-        // AND quick settings controller doesn't want it
-        whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
-                .thenReturn(false)
-        // AND the lock icon wants the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true)
-
-        // THEN touch should NOT be intercepted by NotificationShade
-        assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
-    }
-
-    @Test
     @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() {
         // GIVEN dozing
         whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
-        // AND alternate bouncer doesn't want the touch
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
-        // AND the lock icon does NOT want the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
         // AND quick settings controller doesn't want it
         whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
-                .thenReturn(false)
+            .thenReturn(false)
 
         // THEN touch should be intercepted by NotificationShade
         assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -483,14 +439,9 @@
     fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() {
         // GIVEN dozing
         whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
-        // AND alternate bouncer doesn't want the touch
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
-        // AND the lock icon does NOT want the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
         // AND quick settings controller DOES want it
         whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
-                .thenReturn(true)
+            .thenReturn(true)
 
         // THEN touch should be intercepted by NotificationShade
         assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -503,20 +454,13 @@
         whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
         // AND pulsing
         whenever(dozeServiceHost.isPulsing()).thenReturn(true)
-        // AND status bar doesn't want it
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
-        // AND shade is not fully expanded (mock is false by default)
-        // AND the lock icon does NOT want the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
         // AND quick settings controller DOES want it
         whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
-                .thenReturn(true)
+            .thenReturn(true)
         // AND bouncer is not showing
         whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
         // AND panel view controller wants it
-        whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT))
-                .thenReturn(true)
+        whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)).thenReturn(true)
 
         // THEN touch should be intercepted by NotificationShade
         assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -589,12 +533,10 @@
         underTest.setupCommunalHubLayout()
 
         // Simluate attaching the view so flow collection starts.
-        val onAttachStateChangeListenerArgumentCaptor = ArgumentCaptor.forClass(
-            View.OnAttachStateChangeListener::class.java
-        )
-        verify(view, atLeast(1)).addOnAttachStateChangeListener(
-            onAttachStateChangeListenerArgumentCaptor.capture()
-        )
+        val onAttachStateChangeListenerArgumentCaptor =
+            ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+        verify(view, atLeast(1))
+            .addOnAttachStateChangeListener(onAttachStateChangeListenerArgumentCaptor.capture())
         for (listener in onAttachStateChangeListenerArgumentCaptor.allValues) {
             listener.onViewAttachedToWindow(view)
         }
@@ -608,7 +550,7 @@
     @RequiresFlagsDisabled(Flags.FLAG_COMMUNAL_HUB)
     fun doesNotSetupCommunalHubLayout_whenFlagDisabled() {
         whenever(mGlanceableHubContainerController.communalAvailable())
-                .thenReturn(MutableStateFlow(false))
+            .thenReturn(MutableStateFlow(false))
 
         val mockCommunalPlaceholder = mock(View::class.java)
         val fakeViewIndex = 20
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index ca29dd9..9093b2b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -23,7 +23,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityContainerController
-import com.android.keyguard.LegacyLockIconViewController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
 import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
@@ -57,7 +56,6 @@
 import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.DozeScrimController
 import com.android.systemui.statusbar.phone.DozeServiceHost
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -104,11 +102,9 @@
     @Mock
     private lateinit var notificationStackScrollLayoutController:
         NotificationStackScrollLayoutController
-    @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
     @Mock
     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
-    @Mock private lateinit var lockIconViewController: LegacyLockIconViewController
     @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
     @Mock private lateinit var ambientState: AmbientState
     @Mock private lateinit var shadeLogger: ShadeLogger
@@ -161,7 +157,6 @@
         val featureFlags = FakeFeatureFlags()
         featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
         featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         testScope = TestScope()
         controller =
@@ -176,9 +171,7 @@
                 panelExpansionInteractor,
                 ShadeExpansionStateManager(),
                 notificationStackScrollLayoutController,
-                statusBarKeyguardViewManager,
                 statusBarWindowStateController,
-                lockIconViewController,
                 centralSurfaces,
                 dozeServiceHost,
                 dozeScrimController,
@@ -221,48 +214,18 @@
         }
 
     @Test
-    fun testInterceptTouchWhenShowingAltAuth() =
-        testScope.runTest {
-            captureInteractionEventHandler()
-
-            // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
-            whenever(statusBarStateController.isDozing).thenReturn(false)
-            whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())).thenReturn(true)
-            whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
-
-            // THEN we should intercept touch
-            assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue()
-        }
-
-    @Test
     fun testNoInterceptTouch() =
         testScope.runTest {
             captureInteractionEventHandler()
 
-            // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
+            // WHEN not dozing, drag down helper doesn't want to intercept
             whenever(statusBarStateController.isDozing).thenReturn(false)
-            whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any()))
-                .thenReturn(false)
             whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
 
             // THEN we shouldn't intercept touch
             assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse()
         }
 
-    @Test
-    fun testHandleTouchEventWhenShowingAltAuth() =
-        testScope.runTest {
-            captureInteractionEventHandler()
-
-            // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
-            whenever(statusBarStateController.isDozing).thenReturn(false)
-            whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true)
-            whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
-
-            // THEN we should handle the touch
-            assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue()
-        }
-
     private fun captureInteractionEventHandler() {
         verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
         interactionEventHandler = interactionEventHandlerCaptor.value
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
similarity index 74%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
index 118dea6..69a7627 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
 
 import android.content.packageManager
 import android.graphics.drawable.BitmapDrawable
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -40,13 +40,13 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
-class DemoRonChipViewModelTest : SysuiTestCase() {
+class DemoNotifChipViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val commandRegistry = kosmos.commandRegistry
     private val pw = PrintWriter(StringWriter())
 
-    private val underTest = kosmos.demoRonChipViewModel
+    private val underTest = kosmos.demoNotifChipViewModel
 
     @Before
     fun setUp() {
@@ -56,61 +56,61 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_flagOff_hidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            addDemoRonChip()
+            addDemoNotifChip()
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_noPackage_hidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            commandRegistry.onShellCommand(pw, arrayOf("demo-ron"))
+            commandRegistry.onShellCommand(pw, arrayOf("demo-notif"))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_hasPackage_shown() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_hasText_shownWithText() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
             commandRegistry.onShellCommand(
                 pw,
-                arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test")
+                arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"),
             )
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_supportsColor() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
             commandRegistry.onShellCommand(
                 pw,
-                arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343")
+                arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"),
             )
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
@@ -119,28 +119,28 @@
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_hasHideArg_hidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
             // First, show a chip
-            addDemoRonChip()
+            addDemoNotifChip()
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
 
             // Then, hide the chip
-            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide"))
+            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide"))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
-    private fun addDemoRonChip() {
-        Companion.addDemoRonChip(commandRegistry, pw)
+    private fun addDemoNotifChip() {
+        addDemoNotifChip(commandRegistry, pw)
     }
 
     companion object {
-        fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
-            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+        fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
+            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
         }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
new file mode 100644
index 0000000..eb5d931
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.statusbar.chips.notification.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.testKosmos
+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.runner.RunWith
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+class NotifChipsViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val activeNotificationListRepository = kosmos.activeNotificationListRepository
+
+    private val underTest = kosmos.notifChipsViewModel
+
+    @Test
+    fun chips_noNotifs_empty() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            setNotifs(emptyList())
+
+            assertThat(latest).isEmpty()
+        }
+
+    @Test
+    fun chips_notifMissingStatusBarChipIconView_empty() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = null)))
+
+            assertThat(latest).isEmpty()
+        }
+
+    @Test
+    fun chips_oneNotif_statusBarIconViewMatches() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val icon = mock<StatusBarIconView>()
+            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
+
+            assertThat(latest).hasSize(1)
+            val chip = latest!![0]
+            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+            assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
+        }
+
+    @Test
+    fun chips_twoNotifs_twoChips() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val firstIcon = mock<StatusBarIconView>()
+            val secondIcon = mock<StatusBarIconView>()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(key = "notif1", statusBarChipIcon = firstIcon),
+                    activeNotificationModel(key = "notif2", statusBarChipIcon = secondIcon),
+                )
+            )
+
+            assertThat(latest).hasSize(2)
+            assertIsNotifChip(latest!![0], firstIcon)
+            assertIsNotifChip(latest!![1], secondIcon)
+        }
+
+    private fun setNotifs(notifs: List<ActiveNotificationModel>) {
+        activeNotificationListRepository.activeNotifications.value =
+            ActiveNotificationsStore.Builder()
+                .apply { notifs.forEach { addIndividualNotif(it) } }
+                .build()
+        testScope.runCurrent()
+    }
+
+    companion object {
+        fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
+            assertThat(latest)
+                .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 26ce7b9..e96def6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -25,7 +25,6 @@
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
@@ -40,7 +39,8 @@
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -66,13 +66,11 @@
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
-/**
- * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled.
- */
+/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
-@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+@DisableFlags(StatusBarNotifChips.FLAG_NAME)
 class OngoingActivityChipsViewModelTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
@@ -99,11 +97,11 @@
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
-        kosmos.demoRonChipViewModel.start()
+        kosmos.demoNotifChipViewModel.start()
         val icon =
             BitmapDrawable(
                 context.resources,
-                Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
+                Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888),
             )
         whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
     }
@@ -325,7 +323,7 @@
             latest: OngoingActivityChipModel?,
             chipView: View,
             dialog: SystemUIDialog,
-            kosmos: Kosmos
+            kosmos: Kosmos,
         ): DialogInterface.OnClickListener {
             // Capture the action that would get invoked when the user clicks "Stop" on the dialog
             lateinit var dialogStopAction: DialogInterface.OnClickListener
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
similarity index 77%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 631120b..b12d7c5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -24,7 +24,6 @@
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
@@ -34,10 +33,13 @@
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -46,6 +48,10 @@
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog
 import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
@@ -67,14 +73,12 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
-/**
- * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled.
- */
+/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is enabled. */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
-class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
+@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val systemClock = kosmos.fakeSystemClock
@@ -83,6 +87,7 @@
     private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
     private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
     private val callRepo = kosmos.ongoingCallRepository
+    private val activeNotificationListRepository = kosmos.activeNotificationListRepository
 
     private val pw = PrintWriter(StringWriter())
 
@@ -103,16 +108,16 @@
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
-        kosmos.demoRonChipViewModel.start()
+        kosmos.demoNotifChipViewModel.start()
         val icon =
             BitmapDrawable(
                 context.resources,
-                Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
+                Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888),
             )
         whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
     }
 
-    // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the
+    // Even though the `primaryChip` flow isn't used when the notifs flag is on, still test that the
     // flow has the right behavior to verify that we don't break any existing functionality.
 
     @Test
@@ -249,13 +254,13 @@
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
-            addDemoRonChip(commandRegistry, pw)
+            addDemoNotifChip(commandRegistry, pw)
 
             val latest by collectLastValue(underTest.chips)
 
             assertIsScreenRecordChip(latest!!.primary)
             assertIsCallChip(latest!!.secondary)
-            // Demo RON chip is dropped
+            // Demo notif chip is dropped
         }
 
     @Test
@@ -288,10 +293,101 @@
         }
 
     @Test
+    fun chips_singleNotifChip_primaryIsNotifSecondaryIsHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val icon = mock<StatusBarIconView>()
+            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
+
+            assertIsNotifChip(latest!!.primary, icon)
+            assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chips_twoNotifChips_primaryAndSecondaryAreNotifsInOrder() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val firstIcon = mock<StatusBarIconView>()
+            val secondIcon = mock<StatusBarIconView>()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+                    activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
+                )
+            )
+
+            assertIsNotifChip(latest!!.primary, firstIcon)
+            assertIsNotifChip(latest!!.secondary, secondIcon)
+        }
+
+    @Test
+    fun chips_threeNotifChips_topTwoShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val firstIcon = mock<StatusBarIconView>()
+            val secondIcon = mock<StatusBarIconView>()
+            val thirdIcon = mock<StatusBarIconView>()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+                    activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
+                    activeNotificationModel(key = "thirdNotif", statusBarChipIcon = thirdIcon),
+                )
+            )
+
+            assertIsNotifChip(latest!!.primary, firstIcon)
+            assertIsNotifChip(latest!!.secondary, secondIcon)
+        }
+
+    @Test
+    fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            val firstIcon = mock<StatusBarIconView>()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+                    activeNotificationModel(
+                        key = "secondNotif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                    ),
+                )
+            )
+
+            assertIsCallChip(latest!!.primary)
+            assertIsNotifChip(latest!!.secondary, firstIcon)
+        }
+
+    @Test
+    fun chips_screenRecordAndCallAndNotifs_notifsNotShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            screenRecordState.value = ScreenRecordModel.Recording
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                    )
+                )
+            )
+
+            assertIsScreenRecordChip(latest!!.primary)
+            assertIsCallChip(latest!!.secondary)
+        }
+
+    @Test
     fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
         testScope.runTest {
             // Start with just the lowest priority chip shown
-            addDemoRonChip(commandRegistry, pw)
+            addDemoNotifChip(commandRegistry, pw)
             // And everything else hidden
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
             mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -299,7 +395,7 @@
 
             val latest by collectLastValue(underTest.primaryChip)
 
-            assertIsDemoRonChip(latest)
+            assertIsDemoNotifChip(latest)
 
             // WHEN the higher priority call chip is added
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
@@ -333,7 +429,7 @@
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
-            addDemoRonChip(commandRegistry, pw)
+            addDemoNotifChip(commandRegistry, pw)
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -355,15 +451,15 @@
             // WHEN the higher priority call is removed
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            // THEN the lower priority demo RON is used
-            assertIsDemoRonChip(latest)
+            // THEN the lower priority demo notif is used
+            assertIsDemoNotifChip(latest)
         }
 
     @Test
     fun chips_movesChipsAroundAccordingToPriority() =
         testScope.runTest {
             // Start with just the lowest priority chip shown
-            addDemoRonChip(commandRegistry, pw)
+            addDemoNotifChip(commandRegistry, pw)
             // And everything else hidden
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
             mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -371,16 +467,16 @@
 
             val latest by collectLastValue(underTest.chips)
 
-            assertIsDemoRonChip(latest!!.primary)
+            assertIsDemoNotifChip(latest!!.primary)
             assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
 
             // WHEN the higher priority call chip is added
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
-            // THEN the higher priority call chip is used as primary and demo ron is demoted to
+            // THEN the higher priority call chip is used as primary and demo notif is demoted to
             // secondary
             assertIsCallChip(latest!!.primary)
-            assertIsDemoRonChip(latest!!.secondary)
+            assertIsDemoNotifChip(latest!!.secondary)
 
             // WHEN the higher priority media projection chip is added
             mediaProjectionState.value =
@@ -391,7 +487,7 @@
                 )
 
             // THEN the higher priority media projection chip is used as primary and call is demoted
-            // to secondary (and demo RON is dropped altogether)
+            // to secondary (and demo notif is dropped altogether)
             assertIsShareToAppChip(latest!!.primary)
             assertIsCallChip(latest!!.secondary)
 
@@ -405,15 +501,15 @@
             screenRecordState.value = ScreenRecordModel.DoingNothing
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            // THEN media projection and demo RON remain
+            // THEN media projection and demo notif remain
             assertIsShareToAppChip(latest!!.primary)
-            assertIsDemoRonChip(latest!!.secondary)
+            assertIsDemoNotifChip(latest!!.secondary)
 
             // WHEN media projection is dropped
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            // THEN demo RON is promoted to primary
-            assertIsDemoRonChip(latest!!.primary)
+            // THEN demo notif is promoted to primary
+            assertIsDemoNotifChip(latest!!.primary)
             assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
@@ -526,9 +622,17 @@
             assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false))
         }
 
-    private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) {
+    private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) {
         assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
         assertThat((latest as OngoingActivityChipModel.Shown).icon)
             .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
     }
+
+    private fun setNotifs(notifs: List<ActiveNotificationModel>) {
+        activeNotificationListRepository.activeNotifications.value =
+            ActiveNotificationsStore.Builder()
+                .apply { notifs.forEach { addIndividualNotif(it) } }
+                .build()
+        testScope.runCurrent()
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
new file mode 100644
index 0000000..8dcc444
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.statusbar.core
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Expect
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
+    @get:Rule val expect: Expect = Expect.create()
+
+    private val kosmos =
+        testKosmos().also {
+            it.statusBarOrchestratorFactory = it.fakeStatusBarOrchestratorFactory
+            it.statusBarInitializerStore = it.fakeStatusBarInitializerStore
+        }
+    private val testScope = kosmos.testScope
+    private val fakeDisplayRepository = kosmos.displayRepository
+    private val fakeOrchestratorFactory = kosmos.fakeStatusBarOrchestratorFactory
+    private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore
+
+    // Lazy, so that @EnableFlags is set before initializer is instantiated.
+    private val underTest by lazy { kosmos.multiDisplayStatusBarStarter }
+
+    @Test
+    fun start_startsInitializersForCurrentDisplays() =
+        testScope.runTest {
+            fakeDisplayRepository.addDisplay(displayId = 1)
+            fakeDisplayRepository.addDisplay(displayId = 2)
+
+            underTest.start()
+            runCurrent()
+
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 1).startedByCoreStartable)
+                .isTrue()
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 2).startedByCoreStartable)
+                .isTrue()
+        }
+
+    @Test
+    fun start_startsOrchestratorForCurrentDisplays() =
+        testScope.runTest {
+            fakeDisplayRepository.addDisplay(displayId = 1)
+            fakeDisplayRepository.addDisplay(displayId = 2)
+
+            underTest.start()
+            runCurrent()
+
+            verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 1)!!).start()
+            verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 2)!!).start()
+        }
+
+    @Test
+    fun displayAdded_orchestratorForNewDisplayIsStarted() =
+        testScope.runTest {
+            underTest.start()
+            runCurrent()
+
+            fakeDisplayRepository.addDisplay(displayId = 3)
+            runCurrent()
+
+            verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start()
+        }
+
+    @Test
+    fun displayAdded_initializerForNewDisplayIsStarted() =
+        testScope.runTest {
+            underTest.start()
+            runCurrent()
+
+            fakeDisplayRepository.addDisplay(displayId = 3)
+            runCurrent()
+
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+                .isTrue()
+        }
+
+    @Test
+    fun displayAddedDuringStart_initializerForNewDisplayIsStarted() =
+        testScope.runTest {
+            underTest.start()
+
+            fakeDisplayRepository.addDisplay(displayId = 3)
+            runCurrent()
+
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+                .isTrue()
+        }
+
+    @Test
+    fun displayAddedDuringStart_orchestratorForNewDisplayIsStarted() =
+        testScope.runTest {
+            underTest.start()
+
+            fakeDisplayRepository.addDisplay(displayId = 3)
+            runCurrent()
+
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+                .isTrue()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
index f64387c..c737bf7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
@@ -60,10 +60,9 @@
 
     val underTest =
         StatusBarInitializerImpl(
-            displayId = context.displayId,
-            statusBarWindowControllerStore = windowControllerStore,
             collapsedStatusBarFragmentProvider = { mock(CollapsedStatusBarFragment::class.java) },
             creationListeners = setOf(),
+            statusBarWindowController = windowController,
         )
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
index bb3fb1e..ab8e878 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -38,13 +38,10 @@
 import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT
 import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE
 import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT
-import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
-import com.android.systemui.statusbar.phone.mockPhoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
 import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
-import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
 import com.android.systemui.testKosmos
 import com.android.wm.shell.bubbles.bubbles
 import com.google.common.truth.Truth.assertThat
@@ -60,25 +57,20 @@
 @RunWith(AndroidJUnit4::class)
 class StatusBarOrchestratorTest : SysuiTestCase() {
 
-    private val kosmos =
-        testKosmos().also {
-            it.testDispatcher = it.unconfinedTestDispatcher
-            it.statusBarWindowStateRepositoryStore = it.fakeStatusBarWindowStateRepositoryStore
-        }
+    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
     private val testScope = kosmos.testScope
-    private val statusBarViewController = kosmos.mockPhoneStatusBarViewController
-    private val statusBarWindowControllerStore = kosmos.fakeStatusBarWindowControllerStore
-    private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
-    private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider
-    private val notificationShadeWindowViewController =
+    private val fakeStatusBarModePerDisplayRepository = kosmos.fakeStatusBarModePerDisplayRepository
+    private val mockPluginDependencyProvider = kosmos.mockPluginDependencyProvider
+    private val mockNotificationShadeWindowViewController =
         kosmos.mockNotificationShadeWindowViewController
-    private val shadeSurface = kosmos.mockShadeSurface
-    private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
-    private val fakeStatusBarWindowStateRepositoryStore =
-        kosmos.fakeStatusBarWindowStateRepositoryStore
+    private val mockShadeSurface = kosmos.mockShadeSurface
+    private val fakeBouncerRepository = kosmos.fakeKeyguardBouncerRepository
+    private val fakeStatusBarWindowStatePerDisplayRepository =
+        kosmos.fakeStatusBarWindowStatePerDisplayRepository
     private val fakePowerRepository = kosmos.fakePowerRepository
-    private val mockPhoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions
     private val mockBubbles = kosmos.bubbles
+    private val fakeStatusBarWindowController = kosmos.fakeStatusBarWindowController
+    private val fakeStatusBarInitializer = kosmos.fakeStatusBarInitializer
 
     private val orchestrator = kosmos.statusBarOrchestrator
 
@@ -86,30 +78,31 @@
     fun start_setsUpPluginDependencies() {
         orchestrator.start()
 
-        verify(pluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
-        verify(pluginDependencyProvider).allowPluginDependency(StatusBarStateController::class.java)
+        verify(mockPluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
+        verify(mockPluginDependencyProvider)
+            .allowPluginDependency(StatusBarStateController::class.java)
     }
 
     @Test
     fun start_attachesWindow() {
         orchestrator.start()
 
-        assertThat(statusBarWindowControllerStore.defaultDisplay.isAttached).isTrue()
+        assertThat(fakeStatusBarWindowController.isAttached).isTrue()
     }
 
     @Test
     fun start_setsStatusBarControllerOnShade() {
         orchestrator.start()
 
-        verify(notificationShadeWindowViewController)
-            .setStatusBarViewController(statusBarViewController)
+        verify(mockNotificationShadeWindowViewController)
+            .setStatusBarViewController(fakeStatusBarInitializer.statusBarViewController)
     }
 
     @Test
     fun start_updatesShadeExpansion() {
         orchestrator.start()
 
-        verify(shadeSurface).updateExpansionAndVisibility()
+        verify(mockShadeSurface).updateExpansionAndVisibility()
     }
 
     @Test
@@ -117,9 +110,9 @@
         testScope.runTest {
             orchestrator.start()
 
-            bouncerRepository.setPrimaryShow(isShowing = true)
+            fakeBouncerRepository.setPrimaryShow(isShowing = true)
 
-            verify(statusBarViewController)
+            verify(fakeStatusBarInitializer.statusBarViewController)
                 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
         }
 
@@ -128,9 +121,9 @@
         testScope.runTest {
             orchestrator.start()
 
-            bouncerRepository.setPrimaryShow(isShowing = false)
+            fakeBouncerRepository.setPrimaryShow(isShowing = false)
 
-            verify(statusBarViewController)
+            verify(fakeStatusBarInitializer.statusBarViewController)
                 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO)
         }
 
@@ -141,7 +134,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions).finishAnimations()
+            verify(fakeStatusBarInitializer.statusBarTransitions).finishAnimations()
         }
 
     @Test
@@ -151,7 +144,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions, never()).finishAnimations()
+            verify(fakeStatusBarInitializer.statusBarTransitions, never()).finishAnimations()
         }
 
     @Test
@@ -208,7 +201,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
         }
 
@@ -222,19 +215,19 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
 
             setStatusBarMode(OPAQUE)
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(OPAQUE.toTransitionModeInt(), /* animate= */ true)
 
             setStatusBarMode(LIGHTS_OUT)
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(LIGHTS_OUT.toTransitionModeInt(), /* animate= */ true)
 
             setStatusBarMode(LIGHTS_OUT_TRANSPARENT)
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(LIGHTS_OUT_TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
         }
 
@@ -248,7 +241,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
         }
 
@@ -262,7 +255,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
         }
 
@@ -276,7 +269,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
         }
 
@@ -295,7 +288,7 @@
             setTransientStatusBar()
             clearTransientStatusBar()
 
-            verify(mockPhoneStatusBarTransitions, times(1))
+            verify(fakeStatusBarInitializer.statusBarTransitions, times(1))
                 .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
         }
 
@@ -318,18 +311,18 @@
     }
 
     private fun setTransientStatusBar() {
-        statusBarModeRepository.defaultDisplay.showTransient()
+        fakeStatusBarModePerDisplayRepository.showTransient()
     }
 
     private fun clearTransientStatusBar() {
-        statusBarModeRepository.defaultDisplay.clearTransient()
+        fakeStatusBarModePerDisplayRepository.clearTransient()
     }
 
     private fun setStatusBarWindowState(state: StatusBarWindowState) {
-        fakeStatusBarWindowStateRepositoryStore.defaultDisplay.setWindowState(state)
+        fakeStatusBarWindowStatePerDisplayRepository.setWindowState(state)
     }
 
     private fun setStatusBarMode(statusBarMode: StatusBarMode) {
-        statusBarModeRepository.defaultDisplay.statusBarMode.value = statusBarMode
+        fakeStatusBarModePerDisplayRepository.statusBarMode.value = statusBarMode
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index d04d6fc..0947cd5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -45,12 +45,12 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.testing.UiThreadTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index 22f1e46..6435e82 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -24,9 +24,9 @@
 
 import android.provider.Settings;
 import android.testing.TestableResources;
-import android.testing.UiThreadTest;
 import android.util.KeyValueListParser;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1d74331..94753f7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -34,7 +34,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -43,9 +42,7 @@
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.service.trust.TrustAgentService;
 import android.testing.TestableLooper;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
@@ -67,7 +64,6 @@
 import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.TrustGrantFlags;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
@@ -580,22 +576,6 @@
     }
 
     @Test
-    @DisableSceneContainer
-    @DisableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void testShowAlternateBouncer_unlockingWithBiometricAllowed() {
-        // GIVEN will show alternate bouncer
-        when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false);
-        when(mAlternateBouncerInteractor.show()).thenReturn(true);
-
-        // WHEN showGenericBouncer is called
-        mStatusBarKeyguardViewManager.showBouncer(true);
-
-        // THEN alt auth bouncer is shown
-        verify(mAlternateBouncerInteractor).show();
-        verify(mPrimaryBouncerInteractor, never()).show(anyBoolean());
-    }
-
-    @Test
     public void testUpdateResources_delegatesToBouncer() {
         mStatusBarKeyguardViewManager.updateResources();
 
@@ -841,145 +821,6 @@
     }
 
     @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() {
-        mStatusBarKeyguardViewManager.addCallback(mCallback);
-
-        // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // THEN the touch is not acted upon
-        verify(mCallback, never()).onTouch(any());
-    }
-
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void onInterceptTouch_alternateBouncerViewFlagEnabled() {
-        // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // THEN the touch is not intercepted
-        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-    }
-
-    @Test
-    public void handleDispatchTouchEvent_alternateBouncerNotVisible() {
-        mStatusBarKeyguardViewManager.addCallback(mCallback);
-
-        // GIVEN the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
-        // THEN handleDispatchTouchEvent doesn't use the touches
-        assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-
-        // THEN the touch is not acted upon
-        verify(mCallback, never()).onTouch(any());
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void handleDispatchTouchEvent_shouldInterceptTouchAndHandleTouch() {
-        mStatusBarKeyguardViewManager.addCallback(mCallback);
-
-        // GIVEN the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // GIVEN all touches are NOT the udfps overlay
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
-        // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent
-        // to its child views (handleDispatchTouchEvent returns true)
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-
-        // THEN the touch is acted upon once for each dispatchTOuchEvent call
-        verify(mCallback, times(3)).onTouch(any());
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void handleDispatchTouchEvent_shouldInterceptTouchButNotHandleTouch() {
-        mStatusBarKeyguardViewManager.addCallback(mCallback);
-
-        // GIVEN the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // GIVEN all touches are within the udfps overlay
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(true);
-
-        // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent
-        // to its child views (handleDispatchTouchEvent returns true)
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-
-        // THEN the touch is NOT acted upon at the moment
-        verify(mCallback, never()).onTouch(any());
-    }
-
-    @Test
-    @DisableSceneContainer
-    public void shouldInterceptTouch_alternateBouncerNotVisible() {
-        // GIVEN the alternate bouncer is not visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
-        // THEN no motion events are intercepted
-        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void shouldInterceptTouch_alternateBouncerVisible() {
-        // GIVEN the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // THEN all motion events are intercepted
-        assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-    }
-
-    @Test
     public void alternateBouncerToShowPrimaryBouncer_updatesScrimControllerOnce() {
         // GIVEN the alternate bouncer has shown and calls to hide()  will result in successfully
         // hiding it
@@ -997,106 +838,6 @@
 
     @Test
     @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void alternateBouncerOnTouch_actionDownThenUp_noMinTimeShown_noHideAltBouncer() {
-        reset(mAlternateBouncerInteractor);
-
-        // GIVEN the alternate bouncer has shown for a minimum amount of time
-        when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(false);
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
-        // WHEN ACTION_DOWN and ACTION_UP touch event comes
-        boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
-        when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true);
-        boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
-        // THEN the touches are handled (doesn't let touches through to underlying views)
-        assertTrue(touchHandledDown);
-        assertTrue(touchHandledUp);
-
-        // THEN alternate bouncer does NOT attempt to hide since min showing time wasn't met
-        verify(mAlternateBouncerInteractor, never()).hide();
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void alternateBouncerOnTouch_actionDownThenUp_handlesTouch_hidesAltBouncer() {
-        reset(mAlternateBouncerInteractor);
-
-        // GIVEN the alternate bouncer has shown for a minimum amount of time
-        when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
-        // WHEN ACTION_DOWN and ACTION_UP touch event comes
-        boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
-        when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true);
-        boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
-        // THEN the touches are handled
-        assertTrue(touchHandledDown);
-        assertTrue(touchHandledUp);
-
-        // THEN alternate bouncer attempts to hide
-        verify(mAlternateBouncerInteractor).hide();
-    }
-
-    @Test
-    @DisableSceneContainer
-    public void alternateBouncerOnTouch_actionUp_doesNotHideAlternateBouncer() {
-        reset(mAlternateBouncerInteractor);
-
-        // GIVEN the alternate bouncer has shown for a minimum amount of time
-        when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
-        // WHEN only ACTION_UP touch event comes
-        mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
-        // THEN the alternateBouncer doesn't hide
-        verify(mAlternateBouncerInteractor, never()).hide();
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() {
-        // GIVEN keyguard update monitor callback is registered
-        verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture());
-
-        reset(mKeyguardUpdateMonitor);
-        reset(mKeyguardMessageAreaController);
-
-        // GIVEN alternate bouncer state = not visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
-        // WHEN the device is trusted by active unlock
-        mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser(
-                true,
-                true,
-                new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
-                        | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE),
-                null
-        );
-
-        // THEN the false visibility state is propagated to the keyguardUpdateMonitor
-        verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false));
-
-        // THEN message area visibility updated to FALSE with empty message
-        verify(mKeyguardMessageAreaController).setIsVisible(eq(false));
-        verify(mKeyguardMessageAreaController).setMessage(eq(""));
-    }
-
-    @Test
-    @DisableSceneContainer
     @DisableFlags(Flags.FLAG_SIM_PIN_RACE_CONDITION_ON_RESTART)
     public void testShowBouncerOrKeyguard_needsFullScreen() {
         when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
new file mode 100644
index 0000000..7361de7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.volume.panel.component.volume.domain.interactor
+
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.panel.component.volume.domain.model.SliderType
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AudioSlidersInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    private lateinit var underTest: AudioSlidersInteractor
+
+    @Before
+    fun setUp() =
+        with(kosmos) {
+            audioRepository.setMode(AudioManager.MODE_NORMAL)
+            underTest = audioSlidersInteractor
+        }
+
+    @Test
+    fun shouldAddAllStreams_notInCall() =
+        with(kosmos) {
+            testScope.runTest {
+                val sliders by collectLastValue(underTest.volumePanelSliders)
+                runCurrent()
+
+                assertThat(sliders).isEqualTo(
+                    mutableListOf(
+                        AudioManager.STREAM_MUSIC,
+                        AudioManager.STREAM_VOICE_CALL,
+                        AudioManager.STREAM_RING,
+                        AudioManager.STREAM_NOTIFICATION,
+                        AudioManager.STREAM_ALARM
+                    ).map { SliderType.Stream(AudioStream(it)) })
+            }
+        }
+
+    @Test
+    fun shouldAddAllStreams_inCall() =
+        with(kosmos) {
+            testScope.runTest {
+                audioRepository.setMode(AudioManager.MODE_IN_CALL)
+
+                val sliders by collectLastValue(underTest.volumePanelSliders)
+                runCurrent()
+
+                // Call stream is before music stream while in call.
+                assertThat(sliders).isEqualTo(
+                    mutableListOf(
+                        AudioManager.STREAM_VOICE_CALL,
+                        AudioManager.STREAM_MUSIC,
+                        AudioManager.STREAM_RING,
+                        AudioManager.STREAM_NOTIFICATION,
+                        AudioManager.STREAM_ALARM
+                    ).map { SliderType.Stream(AudioStream(it)) })
+            }
+        }
+}
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 690a89a..d0a1ce8 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -45,31 +45,26 @@
             android:tint="?android:attr/colorPrimary"
         />
 
-        <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text] will ever
-             be shown at one time. -->
+        <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text,
+             ongoing_activity_chip_short_time_delta] will ever be shown at one time. -->
+
+        <!-- Shows a timer, like 00:01. -->
         <com.android.systemui.statusbar.chips.ui.view.ChipChronometer
             android:id="@+id/ongoing_activity_chip_time"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:gravity="center|start"
-            android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
-            android:textAppearance="@android:style/TextAppearance.Material.Small"
-            android:fontFamily="@*android:string/config_headlineFontFamily"
-            android:textColor="?android:attr/colorPrimary"
+            style="@style/StatusBar.Chip.Text"
         />
 
-        <!-- Used to show generic text in the chip instead of a timer. -->
+        <!-- Shows generic text. -->
         <TextView
             android:id="@+id/ongoing_activity_chip_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:gravity="center|start"
-            android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
-            android:textAppearance="@android:style/TextAppearance.Material.Small"
-            android:fontFamily="@*android:string/config_headlineFontFamily"
-            android:textColor="?android:attr/colorPrimary"
+            style="@style/StatusBar.Chip.Text"
+            android:visibility="gone"
+            />
+
+        <!-- Shows a time delta in short form, like "15min" or "1hr". -->
+        <android.widget.DateTimeView
+            android:id="@+id/ongoing_activity_chip_short_time_delta"
+            style="@style/StatusBar.Chip.Text"
             android:visibility="gone"
             />
 
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index e214666..77fbb64 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -128,12 +128,6 @@
 
     <include layout="@layout/dock_info_bottom_area_overlay" />
 
-    <com.android.keyguard.LockIconView
-        android:id="@+id/lock_icon_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content">
-    </com.android.keyguard.LockIconView>
-
     <include
         layout="@layout/keyguard_bottom_area"
         android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml b/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml
deleted file mode 100644
index 4799f8c..0000000
--- a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<com.android.systemui.biometrics.UdfpsFpmEmptyView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <!-- The layout height/width are placeholders, which will be overwritten by
-     FingerprintSensorPropertiesInternal. -->
-    <View
-        android:id="@+id/udfps_enroll_accessibility_view"
-        android:layout_gravity="center"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:contentDescription="@string/accessibility_fingerprint_label"/>
-</com.android.systemui.biometrics.UdfpsFpmEmptyView>
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
deleted file mode 100644
index 530d752..0000000
--- a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view_legacy"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. -->
-
-</com.android.systemui.biometrics.UdfpsKeyguardViewLegacy>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
deleted file mode 100644
index 257d238..0000000
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 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.
-  -->
-<com.android.systemui.biometrics.UdfpsView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/udfps_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    systemui:sensorTouchAreaCoefficient="1.0"
-    android:contentDescription="@string/accessibility_fingerprint_label">
-
-    <ViewStub
-        android:id="@+id/animation_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-
-</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e1f25f9..7cebac2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -70,6 +70,20 @@
         <item name="android:fontWeight">700</item>
     </style>
 
+    <style name="StatusBar" />
+    <style name="StatusBar.Chip" />
+
+    <style name="StatusBar.Chip.Text">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:gravity">center|start</item>
+        <item name="android:paddingStart">@dimen/ongoing_activity_chip_icon_text_padding</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.Material.Small</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">?android:attr/colorPrimary</item>
+    </style>
+
     <style name="Chipbar" />
 
     <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title">
@@ -677,10 +691,12 @@
 
     <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="android:windowActionBar">false</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
         <item name="preferenceTheme">@style/TunerPreferenceTheme</item>
     </style>
 
     <style name="TunerPreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase">
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
     <style name="TextAppearance.NotificationInfo.Confirmation">
diff --git a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
index b792db3..306d682 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
@@ -17,6 +17,7 @@
 package com.android.keyguard
 
 import android.view.MotionEvent
+import android.view.View
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.res.R
@@ -34,11 +35,10 @@
 @SysUISingleton
 class EmptyLockIconViewController
 @Inject
-constructor(
-    private val keyguardRootView: Lazy<KeyguardRootView>,
-) : LockIconViewController {
+constructor(private val keyguardRootView: Lazy<KeyguardRootView>) : LockIconViewController {
     private val deviceEntryIconViewId = R.id.device_entry_icon_view
-    override fun setLockIconView(lockIconView: LockIconView) {
+
+    override fun setLockIconView(lockIconView: View) {
         // no-op
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
deleted file mode 100644
index 03b13fe..0000000
--- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
-
-import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
-import static com.android.keyguard.LockIconView.ICON_LOCK;
-import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.VibrationAttributes;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.biometrics.UdfpsController;
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import dagger.Lazy;
-
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
-
-import java.io.PrintWriter;
-import java.util.Objects;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen.
- *
- * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock
- * icon will show a set distance from the bottom of the device.
- */
-@SysUISingleton
-public class LegacyLockIconViewController implements Dumpable, LockIconViewController {
-    private static final String TAG = "LockIconViewController";
-    private static final float sDefaultDensity =
-            (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
-    private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36);
-    private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
-            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
-
-    private static final long FADE_OUT_DURATION_MS = 250L;
-
-    private final long mLongPressTimeout;
-    @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @NonNull private final KeyguardViewController mKeyguardViewController;
-    @NonNull private final StatusBarStateController mStatusBarStateController;
-    @NonNull private final KeyguardStateController mKeyguardStateController;
-    @NonNull private final FalsingManager mFalsingManager;
-    @NonNull private final AuthController mAuthController;
-    @NonNull private final AccessibilityManager mAccessibilityManager;
-    @NonNull private final ConfigurationController mConfigurationController;
-    @NonNull private final DelayableExecutor mExecutor;
-    private boolean mUdfpsEnrolled;
-    private Resources mResources;
-    private Context mContext;
-    @NonNull private CharSequence mUnlockedLabel;
-    @NonNull private CharSequence mLockedLabel;
-    @NonNull private final VibratorHelper mVibrator;
-    @Nullable private final AuthRippleController mAuthRippleController;
-    @NonNull private final FeatureFlags mFeatureFlags;
-    @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-    @NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
-    @NonNull private final KeyguardInteractor mKeyguardInteractor;
-    @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
-    @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
-
-    // Tracks the velocity of a touch to help filter out the touches that move too fast.
-    private VelocityTracker mVelocityTracker;
-    // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
-    private int mActivePointerId = -1;
-
-    private boolean mIsDozing;
-    private boolean mIsActiveDreamLockscreenHosted;
-    private boolean mIsBouncerShowing;
-    private boolean mRunningFPS;
-    private boolean mCanDismissLockScreen;
-    private int mStatusBarState;
-    private boolean mIsKeyguardShowing;
-    private Runnable mLongPressCancelRunnable;
-
-    private boolean mUdfpsSupported;
-    private float mHeightPixels;
-    private float mWidthPixels;
-    private int mBottomPaddingPx;
-    private int mDefaultPaddingPx;
-
-    private boolean mShowUnlockIcon;
-    private boolean mShowLockIcon;
-
-    // for udfps when strong auth is required or unlocked on AOD
-    private boolean mShowAodLockIcon;
-    private boolean mShowAodUnlockedIcon;
-    private final int mMaxBurnInOffsetX;
-    private final int mMaxBurnInOffsetY;
-    private float mInterpolatedDarkAmount;
-
-    private boolean mDownDetected;
-    private final Rect mSensorTouchLocation = new Rect();
-    private LockIconView mView;
-
-    @VisibleForTesting
-    final Consumer<Float> mDozeTransitionCallback = (Float value) -> {
-        mInterpolatedDarkAmount = value;
-        mView.setDozeAmount(value);
-        updateBurnInOffsets();
-    };
-
-    @VisibleForTesting
-    final Consumer<Boolean> mIsDozingCallback = (Boolean isDozing) -> {
-        mIsDozing = isDozing;
-        updateBurnInOffsets();
-        updateVisibility();
-    };
-
-    @VisibleForTesting
-    final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback =
-            (Boolean isLockscreenHosted) -> {
-                mIsActiveDreamLockscreenHosted = isLockscreenHosted;
-                updateVisibility();
-            };
-
-    @Inject
-    public LegacyLockIconViewController(
-            @NonNull StatusBarStateController statusBarStateController,
-            @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
-            @NonNull KeyguardViewController keyguardViewController,
-            @NonNull KeyguardStateController keyguardStateController,
-            @NonNull FalsingManager falsingManager,
-            @NonNull AuthController authController,
-            @NonNull DumpManager dumpManager,
-            @NonNull AccessibilityManager accessibilityManager,
-            @NonNull ConfigurationController configurationController,
-            @NonNull @Main DelayableExecutor executor,
-            @NonNull VibratorHelper vibrator,
-            @Nullable AuthRippleController authRippleController,
-            @NonNull @Main Resources resources,
-            @NonNull KeyguardTransitionInteractor transitionInteractor,
-            @NonNull KeyguardInteractor keyguardInteractor,
-            @NonNull FeatureFlags featureFlags,
-            PrimaryBouncerInteractor primaryBouncerInteractor,
-            Context context,
-            Lazy<DeviceEntryInteractor> deviceEntryInteractor
-    ) {
-        mStatusBarStateController = statusBarStateController;
-        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mAuthController = authController;
-        mKeyguardViewController = keyguardViewController;
-        mKeyguardStateController = keyguardStateController;
-        mFalsingManager = falsingManager;
-        mAccessibilityManager = accessibilityManager;
-        mConfigurationController = configurationController;
-        mExecutor = executor;
-        mVibrator = vibrator;
-        mAuthRippleController = authRippleController;
-        mTransitionInteractor = transitionInteractor;
-        mKeyguardInteractor = keyguardInteractor;
-        mFeatureFlags = featureFlags;
-        mPrimaryBouncerInteractor = primaryBouncerInteractor;
-
-        mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
-        mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
-        mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
-        mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
-        mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress);
-        dumpManager.registerDumpable(TAG, this);
-        mResources = resources;
-        mContext = context;
-        mDeviceEntryInteractor = deviceEntryInteractor;
-
-        mAccessibilityDelegate = new View.AccessibilityDelegate() {
-            private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
-                    new AccessibilityNodeInfo.AccessibilityAction(
-                            AccessibilityNodeInfoCompat.ACTION_CLICK,
-                            mResources.getString(R.string.accessibility_authenticate_hint));
-            private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
-                    new AccessibilityNodeInfo.AccessibilityAction(
-                            AccessibilityNodeInfoCompat.ACTION_CLICK,
-                            mResources.getString(R.string.accessibility_enter_hint));
-            public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(v, info);
-                if (isActionable()) {
-                    if (mShowLockIcon) {
-                        info.addAction(mAccessibilityAuthenticateHint);
-                    } else if (mShowUnlockIcon) {
-                        info.addAction(mAccessibilityEnterHint);
-                    }
-                }
-            }
-        };
-    }
-
-    /** Sets the LockIconView to the controller and rebinds any that depend on it. */
-    @SuppressLint("ClickableViewAccessibility")
-    @Override
-    public void setLockIconView(LockIconView lockIconView) {
-        mView = lockIconView;
-        mView.setAccessibilityDelegate(mAccessibilityDelegate);
-
-        if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
-            collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD),
-                    mDozeTransitionCallback);
-            collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback);
-        }
-
-        if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
-            collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
-                    mIsActiveDreamLockscreenHostedCallback);
-        }
-
-        updateIsUdfpsEnrolled();
-        updateConfiguration();
-        updateKeyguardShowing();
-
-        mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-        mIsDozing = mStatusBarStateController.isDozing();
-        mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount();
-        mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
-        mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-        mStatusBarState = mStatusBarStateController.getState();
-
-        updateColors();
-        mDownDetected = false;
-        updateBurnInOffsets();
-        updateVisibility();
-
-        updateAccessibility();
-
-        lockIconView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
-            @Override
-            public void onViewAttachedToWindow(View view) {
-                registerCallbacks();
-            }
-
-            @Override
-            public void onViewDetachedFromWindow(View view) {
-                unregisterCallbacks();
-            }
-        });
-
-        if (lockIconView.isAttachedToWindow()) {
-            registerCallbacks();
-        }
-
-        lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent));
-    }
-
-    private void registerCallbacks() {
-        mConfigurationController.addCallback(mConfigurationListener);
-        mAuthController.addCallback(mAuthControllerCallback);
-        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
-        mStatusBarStateController.addCallback(mStatusBarStateListener);
-        mKeyguardStateController.addCallback(mKeyguardStateCallback);
-        mAccessibilityManager.addAccessibilityStateChangeListener(
-                mAccessibilityStateChangeListener);
-
-    }
-
-    private void unregisterCallbacks() {
-        mAuthController.removeCallback(mAuthControllerCallback);
-        mConfigurationController.removeCallback(mConfigurationListener);
-        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
-        mStatusBarStateController.removeCallback(mStatusBarStateListener);
-        mKeyguardStateController.removeCallback(mKeyguardStateCallback);
-        mAccessibilityManager.removeAccessibilityStateChangeListener(
-                mAccessibilityStateChangeListener);
-
-    }
-
-    private void updateAccessibility() {
-        if (mAccessibilityManager.isEnabled()) {
-            mView.setOnClickListener(mA11yClickListener);
-        } else {
-            mView.setOnClickListener(null);
-        }
-    }
-
-    @Override
-    public float getTop() {
-        return mView.getLocationTop();
-    }
-
-    @Override
-    public float getBottom() {
-        return mView.getLocationBottom();
-    }
-
-    private void updateVisibility() {
-        if (!mIsKeyguardShowing && !mIsDozing) {
-            mView.setVisibility(View.INVISIBLE);
-            return;
-        }
-
-        if (mIsKeyguardShowing && mIsActiveDreamLockscreenHosted) {
-            mView.setVisibility(View.INVISIBLE);
-            return;
-        }
-
-        boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
-                && !mShowAodUnlockedIcon && !mShowAodLockIcon;
-        mShowLockIcon = !mCanDismissLockScreen && isLockScreen()
-                && (!mUdfpsEnrolled || !mRunningFPS);
-        mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
-        mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
-        mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
-
-        final CharSequence prevContentDescription = mView.getContentDescription();
-        if (mShowLockIcon) {
-            if (wasShowingFpIcon) {
-                // fp icon was shown by UdfpsView, and now we still want to animate the transition
-                // in this drawable
-                mView.updateIcon(ICON_FINGERPRINT, false);
-            }
-            mView.updateIcon(ICON_LOCK, false);
-            mView.setContentDescription(mLockedLabel);
-            mView.setVisibility(View.VISIBLE);
-        } else if (mShowUnlockIcon) {
-            if (wasShowingFpIcon) {
-                // fp icon was shown by UdfpsView, and now we still want to animate the transition
-                // in this drawable
-                mView.updateIcon(ICON_FINGERPRINT, false);
-            }
-            mView.updateIcon(ICON_UNLOCK, false);
-            mView.setContentDescription(mUnlockedLabel);
-            mView.setVisibility(View.VISIBLE);
-        } else if (mShowAodUnlockedIcon) {
-            mView.updateIcon(ICON_UNLOCK, true);
-            mView.setContentDescription(mUnlockedLabel);
-            mView.setVisibility(View.VISIBLE);
-        } else if (mShowAodLockIcon) {
-            mView.updateIcon(ICON_LOCK, true);
-            mView.setContentDescription(mLockedLabel);
-            mView.setVisibility(View.VISIBLE);
-        } else {
-            mView.clearIcon();
-            mView.setVisibility(View.INVISIBLE);
-            mView.setContentDescription(null);
-        }
-
-        boolean accessibilityEnabled =
-                !mPrimaryBouncerInteractor.isAnimatingAway() && mView.isVisibleToUser();
-        mView.setImportantForAccessibility(
-                accessibilityEnabled ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
-                        : View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-
-        if (!Objects.equals(prevContentDescription, mView.getContentDescription())
-                && mView.getContentDescription() != null && accessibilityEnabled) {
-            mView.announceForAccessibility(mView.getContentDescription());
-        }
-    }
-
-    private boolean isLockScreen() {
-        return !mIsDozing
-                && !mIsBouncerShowing
-                && mStatusBarState == StatusBarState.KEYGUARD;
-    }
-
-    private void updateKeyguardShowing() {
-        mIsKeyguardShowing = mKeyguardStateController.isShowing()
-                && !mKeyguardStateController.isKeyguardGoingAway();
-    }
-
-    private void updateColors() {
-        mView.updateColorAndBackgroundVisibility();
-    }
-
-    private void updateConfiguration() {
-        WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
-        mWidthPixels = bounds.right;
-        if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
-            // Assumed to be initially neglected as there are no left or right insets in portrait
-            // However, on landscape, these insets need to included when calculating the midpoint
-            WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
-            mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight();
-        }
-        mHeightPixels = bounds.bottom;
-        mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
-        mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding);
-        mUnlockedLabel = mResources.getString(
-                R.string.accessibility_unlock_button);
-        mLockedLabel = mResources.getString(R.string.accessibility_lock_icon);
-        updateLockIconLocation();
-    }
-
-    private void updateLockIconLocation() {
-        final float scaleFactor = mAuthController.getScaleFactor();
-        final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
-        if (KeyguardBottomAreaRefactor.isEnabled() || MigrateClocksToBlueprint.isEnabled()) {
-            // positioning in this case is handled by [DefaultDeviceEntrySection]
-            mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
-                    scaledPadding);
-        } else {
-            if (mUdfpsSupported) {
-                mView.setCenterLocation(mAuthController.getUdfpsLocation(),
-                        mAuthController.getUdfpsRadius(), scaledPadding);
-            } else {
-                mView.setCenterLocation(
-                        new Point((int) mWidthPixels / 2,
-                                (int) (mHeightPixels
-                                        - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
-                        sLockIconRadiusPx * scaleFactor, scaledPadding);
-            }
-        }
-    }
-
-    @Override
-    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("mUdfpsSupported: " + mUdfpsSupported);
-        pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
-        pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
-        pw.println();
-        pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
-        pw.println(" mShowLockIcon: " + mShowLockIcon);
-        pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
-        pw.println();
-        pw.println(" mIsDozing: " + mIsDozing);
-        pw.println(" isFlagEnabled(DOZING_MIGRATION_1): "
-                + mFeatureFlags.isEnabled(DOZING_MIGRATION_1));
-        pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
-        pw.println(" mRunningFPS: " + mRunningFPS);
-        pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
-        pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
-        pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
-        pw.println(" mSensorTouchLocation: " + mSensorTouchLocation);
-        pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx);
-        pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted);
-
-        if (mView != null) {
-            mView.dump(pw, args);
-        }
-    }
-
-    /** Every minute, update the aod icon's burn in offset */
-    @Override
-    public void dozeTimeTick() {
-        updateBurnInOffsets();
-    }
-
-    private void updateBurnInOffsets() {
-        float offsetX = MathUtils.lerp(0f,
-                getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
-                        - mMaxBurnInOffsetX, mInterpolatedDarkAmount);
-        float offsetY = MathUtils.lerp(0f,
-                getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
-                        - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
-
-        mView.setTranslationX(offsetX);
-        mView.setTranslationY(offsetY);
-    }
-
-    private void updateIsUdfpsEnrolled() {
-        boolean wasUdfpsSupported = mUdfpsSupported;
-        boolean wasUdfpsEnrolled = mUdfpsEnrolled;
-
-        mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
-        mView.setUseBackground(mUdfpsSupported);
-
-        mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
-        if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
-            updateVisibility();
-        }
-    }
-
-    private StatusBarStateController.StateListener mStatusBarStateListener =
-            new StatusBarStateController.StateListener() {
-                @Override
-                public void onDozeAmountChanged(float linear, float eased) {
-                    if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
-                        mInterpolatedDarkAmount = eased;
-                        mView.setDozeAmount(eased);
-                        updateBurnInOffsets();
-                    }
-                }
-
-                @Override
-                public void onDozingChanged(boolean isDozing) {
-                    if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
-                        mIsDozing = isDozing;
-                        updateBurnInOffsets();
-                        updateVisibility();
-                    }
-                }
-
-                @Override
-                public void onStateChanged(int statusBarState) {
-                    mStatusBarState = statusBarState;
-                    updateVisibility();
-                }
-            };
-
-    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
-            new KeyguardUpdateMonitorCallback() {
-                @Override
-                public void onKeyguardBouncerStateChanged(boolean bouncer) {
-                    mIsBouncerShowing = bouncer;
-                    updateVisibility();
-                }
-
-                @Override
-                public void onBiometricRunningStateChanged(boolean running,
-                        BiometricSourceType biometricSourceType) {
-                    final boolean wasRunningFps = mRunningFPS;
-
-                    if (biometricSourceType == FINGERPRINT) {
-                        mRunningFPS = running;
-                    }
-
-                    if (wasRunningFps != mRunningFPS) {
-                        updateVisibility();
-                    }
-                }
-            };
-
-    private final KeyguardStateController.Callback mKeyguardStateCallback =
-            new KeyguardStateController.Callback() {
-        @Override
-        public void onUnlockedChanged() {
-            mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-            updateKeyguardShowing();
-            updateVisibility();
-        }
-
-        @Override
-        public void onKeyguardShowingChanged() {
-            // Reset values in case biometrics were removed (ie: pin/pattern/password => swipe).
-            // If biometrics were removed, local vars mCanDismissLockScreen and
-            // mUserUnlockedWithBiometric may not be updated.
-            mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-
-            // reset mIsBouncerShowing state in case it was preemptively set
-            // onLongPress
-            mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-
-            updateKeyguardShowing();
-            updateVisibility();
-        }
-
-        @Override
-        public void onKeyguardFadingAwayChanged() {
-            updateKeyguardShowing();
-            updateVisibility();
-        }
-    };
-
-    private final ConfigurationController.ConfigurationListener mConfigurationListener =
-            new ConfigurationController.ConfigurationListener() {
-        @Override
-        public void onUiModeChanged() {
-            updateColors();
-        }
-
-        @Override
-        public void onThemeChanged() {
-            updateColors();
-        }
-
-        @Override
-        public void onConfigChanged(Configuration newConfig) {
-            updateConfiguration();
-            updateColors();
-        }
-    };
-
-    /**
-     * Handles the touch if {@link #isActionable()} is true.
-     * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
-     * area for {@link #mLongPressTimeout} ms.
-     *
-     * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
-     */
-    private boolean onTouchEvent(MotionEvent event) {
-        if (!actionableDownEventStartedOnView(event)) {
-            cancelTouches();
-            return false;
-        }
-
-        switch(event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_HOVER_ENTER:
-                if (!mDownDetected && mAccessibilityManager.isTouchExplorationEnabled()) {
-                    vibrateOnTouchExploration();
-                }
-
-                // The pointer that causes ACTION_DOWN is always at index 0.
-                // We need to persist its ID to track it during ACTION_MOVE that could include
-                // data for many other pointers because of multi-touch support.
-                mActivePointerId = event.getPointerId(0);
-                if (mVelocityTracker == null) {
-                    // To simplify the lifecycle of the velocity tracker, make sure it's never null
-                    // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
-                    mVelocityTracker = VelocityTracker.obtain();
-                } else {
-                    // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
-                    // ACTION_DOWN, in that case we should just reuse the old instance.
-                    mVelocityTracker.clear();
-                }
-                mVelocityTracker.addMovement(event);
-
-                mDownDetected = true;
-                mLongPressCancelRunnable = mExecutor.executeDelayed(
-                        this::onLongPress, mLongPressTimeout);
-                break;
-            case MotionEvent.ACTION_MOVE:
-            case MotionEvent.ACTION_HOVER_MOVE:
-                mVelocityTracker.addMovement(event);
-                // Compute pointer velocity in pixels per second.
-                mVelocityTracker.computeCurrentVelocity(1000);
-                float velocity = computePointerSpeed(mVelocityTracker,
-                        mActivePointerId);
-                if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS
-                        && exceedsVelocityThreshold(velocity)) {
-                    Log.v(TAG, "lock icon long-press rescheduled due to "
-                            + "high pointer velocity=" + velocity);
-                    mLongPressCancelRunnable.run();
-                    mLongPressCancelRunnable = mExecutor.executeDelayed(
-                            this::onLongPress, mLongPressTimeout);
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_HOVER_EXIT:
-                cancelTouches();
-                break;
-        }
-
-        return true;
-    }
-
-    /**
-     * Calculate the pointer speed given a velocity tracker and the pointer id.
-     * This assumes that the velocity tracker has already been passed all relevant motion events.
-     */
-    private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
-        final float vx = tracker.getXVelocity(pointerId);
-        final float vy = tracker.getYVelocity(pointerId);
-        return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
-    }
-
-    /**
-     * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
-     */
-    private static boolean exceedsVelocityThreshold(float velocity) {
-        return velocity > 750f;
-    }
-
-    private boolean actionableDownEventStartedOnView(MotionEvent event) {
-        if (!isActionable()) {
-            return false;
-        }
-
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            return true;
-        }
-
-        return mDownDetected;
-    }
-
-    @ExperimentalCoroutinesApi
-    @VisibleForTesting
-    protected void onLongPress() {
-        cancelTouches();
-        if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
-            Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
-            return;
-        }
-
-        // pre-emptively set to true to hide view
-        mIsBouncerShowing = true;
-        if (!DeviceEntryUdfpsRefactor.isEnabled()
-                && mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
-            mAuthRippleController.showUnlockRipple(FINGERPRINT);
-        }
-        updateVisibility();
-
-        // play device entry haptic (consistent with UDFPS controller longpress)
-        vibrateOnLongPress();
-
-        if (SceneContainerFlag.isEnabled()) {
-            mDeviceEntryInteractor.get().attemptDeviceEntry();
-        } else {
-            mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
-        }
-    }
-
-
-    private void cancelTouches() {
-        mDownDetected = false;
-        if (mLongPressCancelRunnable != null) {
-            mLongPressCancelRunnable.run();
-        }
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    private boolean isActionable() {
-        if (mIsBouncerShowing) {
-            Log.v(TAG, "lock icon long-press ignored, bouncer already showing.");
-            // a long press gestures from AOD may have already triggered the bouncer to show,
-            // so this touch is no longer actionable
-            return false;
-        }
-        return mUdfpsSupported || mShowUnlockIcon;
-    }
-
-    /**
-     * Set the alpha of this view.
-     */
-    @Override
-    public void setAlpha(float alpha) {
-        mView.setAlpha(alpha);
-    }
-
-    private void updateUdfpsConfig() {
-        // must be called from the main thread since it may update the views
-        mExecutor.execute(() -> {
-            updateIsUdfpsEnrolled();
-            updateConfiguration();
-        });
-    }
-
-    @VisibleForTesting
-    void vibrateOnTouchExploration() {
-        mVibrator.performHapticFeedback(
-                mView,
-                HapticFeedbackConstants.CONTEXT_CLICK
-        );
-    }
-
-    @VisibleForTesting
-    void vibrateOnLongPress() {
-        mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS);
-    }
-
-    private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
-        @Override
-        public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
-            if (modality == TYPE_FINGERPRINT) {
-                updateUdfpsConfig();
-            }
-        }
-
-        @Override
-        public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
-            if (modality == TYPE_FINGERPRINT) {
-                updateUdfpsConfig();
-            }
-        }
-
-        @Override
-        public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {
-            updateUdfpsConfig();
-        }
-    };
-
-    /**
-     * Whether the lock icon will handle a touch while dozing.
-     */
-    @Override
-    public boolean willHandleTouchWhileDozing(MotionEvent event) {
-        // is in lock icon area
-        mView.getHitRect(mSensorTouchLocation);
-        final boolean inLockIconArea =
-                mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
-                        && mView.getVisibility() == View.VISIBLE;
-
-        return inLockIconArea && actionableDownEventStartedOnView(event);
-    }
-
-    private final View.OnClickListener mA11yClickListener = v -> onLongPress();
-
-    private final AccessibilityManager.AccessibilityStateChangeListener
-            mAccessibilityStateChangeListener = enabled -> updateAccessibility();
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
deleted file mode 100644
index ff6a3d0..0000000
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-
-import com.android.internal.graphics.ColorUtils;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
-
-import java.io.PrintWriter;
-
-/**
- * A view positioned under the notification shade.
- */
-public class LockIconView extends FrameLayout implements Dumpable {
-    @IntDef({ICON_NONE, ICON_LOCK, ICON_FINGERPRINT, ICON_UNLOCK})
-    public @interface IconType {}
-
-    public static final int ICON_NONE = -1;
-    public static final int ICON_LOCK = 0;
-    public static final int ICON_FINGERPRINT = 1;
-    public static final int ICON_UNLOCK = 2;
-
-    private @IconType int mIconType;
-    private boolean mAod;
-
-    @NonNull private final RectF mSensorRect;
-    @NonNull private Point mLockIconCenter = new Point(0, 0);
-    private float mRadius;
-    private int mLockIconPadding;
-
-    private ImageView mLockIcon;
-    private ImageView mBgView;
-
-    private int mLockIconColor;
-    private boolean mUseBackground = false;
-    private float mDozeAmount = 0f;
-
-    @SuppressLint("ClickableViewAccessibility")
-    public LockIconView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mSensorRect = new RectF();
-
-        addBgImageView(context, attrs);
-        addLockIconImageView(context, attrs);
-    }
-
-    void setDozeAmount(float dozeAmount) {
-        mDozeAmount = dozeAmount;
-        updateColorAndBackgroundVisibility();
-    }
-
-    void updateColorAndBackgroundVisibility() {
-        if (mUseBackground && mLockIcon.getDrawable() != null) {
-            mLockIconColor = ColorUtils.blendARGB(
-                    Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
-                    Color.WHITE,
-                    mDozeAmount);
-            int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
-                    com.android.internal.R.attr.colorSurface);
-            mBgView.setImageTintList(ColorStateList.valueOf(backgroundColor));
-            mBgView.setAlpha(1f - mDozeAmount);
-            mBgView.setVisibility(View.VISIBLE);
-        } else {
-            mLockIconColor = ColorUtils.blendARGB(
-                    Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
-                    Color.WHITE,
-                    mDozeAmount);
-            mBgView.setVisibility(View.GONE);
-        }
-
-        mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
-    }
-
-    /**
-     * Whether or not to render the lock icon background. Mainly used for UDPFS.
-     */
-    public void setUseBackground(boolean useBackground) {
-        mUseBackground = useBackground;
-        updateColorAndBackgroundVisibility();
-    }
-
-    /**
-     * Set the location of the lock icon.
-     */
-    public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) {
-        mLockIconCenter = center;
-        mRadius = radius;
-        mLockIconPadding = drawablePadding;
-
-        mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding,
-                mLockIconPadding);
-
-        mSensorRect.set(mLockIconCenter.x - mRadius,
-                mLockIconCenter.y - mRadius,
-                mLockIconCenter.x + mRadius,
-                mLockIconCenter.y + mRadius);
-
-        final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-        if (lp != null) {
-            lp.width = (int) (mSensorRect.right - mSensorRect.left);
-            lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
-            lp.topMargin = (int) mSensorRect.top;
-            lp.setMarginStart((int) mSensorRect.left);
-            setLayoutParams(lp);
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    float getLocationTop() {
-        Rect r = new Rect();
-        mLockIcon.getGlobalVisibleRect(r);
-        return r.top;
-    }
-
-    float getLocationBottom() {
-        Rect r = new Rect();
-        mLockIcon.getGlobalVisibleRect(r);
-        return r.bottom;
-
-    }
-
-    /**
-     * Updates the icon its default state where no visual is shown.
-     */
-    public void clearIcon() {
-        updateIcon(ICON_NONE, false);
-    }
-
-    /**
-     * Transition the current icon to a new state
-     * @param icon type (ie: lock icon, unlock icon, fingerprint icon)
-     * @param aod whether to use the aod icon variant (some icons don't have aod variants and will
-     *            therefore show no icon)
-     */
-    public void updateIcon(@IconType int icon, boolean aod) {
-        mIconType = icon;
-        mAod = aod;
-
-        mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
-    }
-
-    public ImageView getLockIcon() {
-        return mLockIcon;
-    }
-
-    private void addLockIconImageView(Context context, AttributeSet attrs) {
-        mLockIcon = new ImageView(context, attrs);
-        mLockIcon.setId(R.id.lock_icon);
-        mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
-        mLockIcon.setImageDrawable(context.getDrawable(R.drawable.super_lock_icon));
-        addView(mLockIcon);
-        LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams();
-        lp.height = MATCH_PARENT;
-        lp.width = MATCH_PARENT;
-        lp.gravity = Gravity.CENTER;
-        mLockIcon.setLayoutParams(lp);
-    }
-
-    private void addBgImageView(Context context, AttributeSet attrs) {
-        mBgView = new ImageView(context, attrs);
-        mBgView.setId(R.id.lock_icon_bg);
-        mBgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg));
-        mBgView.setVisibility(View.INVISIBLE);
-        addView(mBgView);
-        LayoutParams lp = (LayoutParams) mBgView.getLayoutParams();
-        lp.height = MATCH_PARENT;
-        lp.width = MATCH_PARENT;
-        mBgView.setLayoutParams(lp);
-    }
-
-    private static int[] getLockIconState(@IconType int icon, boolean aod) {
-        if (icon == ICON_NONE) {
-            return new int[0];
-        }
-
-        int[] lockIconState = new int[2];
-        switch (icon) {
-            case ICON_LOCK:
-                lockIconState[0] = android.R.attr.state_first;
-                break;
-            case ICON_FINGERPRINT:
-                lockIconState[0] = android.R.attr.state_middle;
-                break;
-            case ICON_UNLOCK:
-                lockIconState[0] = android.R.attr.state_last;
-                break;
-        }
-
-        if (aod) {
-            lockIconState[1] = android.R.attr.state_single;
-        } else {
-            lockIconState[1] = -android.R.attr.state_single;
-        }
-
-        return lockIconState;
-    }
-
-    private String typeToString(@IconType int type) {
-        switch (type) {
-            case ICON_NONE:
-                return "none";
-            case ICON_LOCK:
-                return "lock";
-            case ICON_FINGERPRINT:
-                return "fingerprint";
-            case ICON_UNLOCK:
-                return "unlock";
-        }
-
-        return "invalid";
-    }
-
-    @Override
-    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("Lock Icon View Parameters:");
-        pw.println("    Center in px (x, y)= ("
-                + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
-        pw.println("    Radius in pixels: " + mRadius);
-        pw.println("    Drawable padding: " + mLockIconPadding);
-        pw.println("    mIconType=" + typeToString(mIconType));
-        pw.println("    mAod=" + mAod);
-        pw.println("Lock Icon View actual measurements:");
-        pw.println("    topLeft= (" + getX() + ", " + getY() + ")");
-        pw.println("    width=" + getWidth() + " height=" + getHeight());
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
index 10d5a0c..c5012b0 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
@@ -17,13 +17,19 @@
 package com.android.keyguard
 
 import android.view.MotionEvent
+import android.view.View
 
 /** Controls the [LockIconView]. */
 interface LockIconViewController {
-    fun setLockIconView(lockIconView: LockIconView)
+    fun setLockIconView(lockIconView: View)
+
     fun getTop(): Float
+
     fun getBottom(): Float
+
     fun dozeTimeTick()
+
     fun setAlpha(alpha: Float)
+
     fun willHandleTouchWhileDozing(event: MotionEvent): Boolean
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index cd9efaf..610e3f8a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -39,6 +39,10 @@
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
 import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper
 import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
 import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
@@ -159,6 +163,13 @@
         impl: NightDisplayTileDataInteractor
     ): QSTileAvailabilityInteractor
 
+    @Binds
+    @IntoMap
+    @StringKey(HEARING_DEVICES_TILE_SPEC)
+    fun provideHearingDevicesAvailabilityInteractor(
+        impl: HearingDevicesTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
     companion object {
         const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
         const val COLOR_INVERSION_TILE_SPEC = "inversion"
@@ -191,7 +202,7 @@
             factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>,
             mapper: ColorCorrectionTileMapper,
             stateInteractor: ColorCorrectionTileDataInteractor,
-            userActionInteractor: ColorCorrectionUserActionInteractor
+            userActionInteractor: ColorCorrectionUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(COLOR_CORRECTION_TILE_SPEC),
@@ -223,7 +234,7 @@
             factory: QSTileViewModelFactory.Static<ColorInversionTileModel>,
             mapper: ColorInversionTileMapper,
             stateInteractor: ColorInversionTileDataInteractor,
-            userActionInteractor: ColorInversionUserActionInteractor
+            userActionInteractor: ColorInversionUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(COLOR_INVERSION_TILE_SPEC),
@@ -255,7 +266,7 @@
             factory: QSTileViewModelFactory.Static<FontScalingTileModel>,
             mapper: FontScalingTileMapper,
             stateInteractor: FontScalingTileDataInteractor,
-            userActionInteractor: FontScalingTileUserActionInteractor
+            userActionInteractor: FontScalingTileUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(FONT_SCALING_TILE_SPEC),
@@ -279,21 +290,6 @@
                 category = TileCategory.DISPLAY,
             )
 
-        @Provides
-        @IntoMap
-        @StringKey(HEARING_DEVICES_TILE_SPEC)
-        fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
-            QSTileConfig(
-                tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
-                uiConfig =
-                    QSTileUIConfig.Resource(
-                        iconRes = R.drawable.qs_hearing_devices_icon,
-                        labelRes = R.string.quick_settings_hearing_devices_label,
-                    ),
-                instanceId = uiEventLogger.getNewInstanceId(),
-                category = TileCategory.ACCESSIBILITY,
-            )
-
         /**
          * Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden
          * behind a flag.
@@ -305,7 +301,7 @@
             factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>,
             mapper: ReduceBrightColorsTileMapper,
             stateInteractor: ReduceBrightColorsTileDataInteractor,
-            userActionInteractor: ReduceBrightColorsTileUserActionInteractor
+            userActionInteractor: ReduceBrightColorsTileUserActionInteractor,
         ): QSTileViewModel =
             if (Flags.qsNewTilesFuture())
                 factory.create(
@@ -339,7 +335,7 @@
             factory: QSTileViewModelFactory.Static<OneHandedModeTileModel>,
             mapper: OneHandedModeTileMapper,
             stateInteractor: OneHandedModeTileDataInteractor,
-            userActionInteractor: OneHandedModeTileUserActionInteractor
+            userActionInteractor: OneHandedModeTileUserActionInteractor,
         ): QSTileViewModel =
             if (Flags.qsNewTilesFuture())
                 factory.create(
@@ -376,7 +372,7 @@
             factory: QSTileViewModelFactory.Static<NightDisplayTileModel>,
             mapper: NightDisplayTileMapper,
             stateInteractor: NightDisplayTileDataInteractor,
-            userActionInteractor: NightDisplayTileUserActionInteractor
+            userActionInteractor: NightDisplayTileUserActionInteractor,
         ): QSTileViewModel =
             if (Flags.qsNewTilesFuture())
                 factory.create(
@@ -386,5 +382,43 @@
                     mapper,
                 )
             else StubQSTileViewModel
+
+        @Provides
+        @IntoMap
+        @StringKey(HEARING_DEVICES_TILE_SPEC)
+        fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+            QSTileConfig(
+                tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+                uiConfig =
+                    QSTileUIConfig.Resource(
+                        iconRes = R.drawable.qs_hearing_devices_icon,
+                        labelRes = R.string.quick_settings_hearing_devices_label,
+                    ),
+                instanceId = uiEventLogger.getNewInstanceId(),
+                category = TileCategory.ACCESSIBILITY,
+            )
+
+        /**
+         * Inject HearingDevices Tile into tileViewModelMap in QSModule. The tile is hidden behind a
+         * flag.
+         */
+        @Provides
+        @IntoMap
+        @StringKey(HEARING_DEVICES_TILE_SPEC)
+        fun provideHearingDevicesTileViewModel(
+            factory: QSTileViewModelFactory.Static<HearingDevicesTileModel>,
+            mapper: HearingDevicesTileMapper,
+            stateInteractor: HearingDevicesTileDataInteractor,
+            userActionInteractor: HearingDevicesTileUserActionInteractor,
+        ): QSTileViewModel {
+            return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) {
+                factory.create(
+                    TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+                    userActionInteractor,
+                    stateInteractor,
+                    mapper,
+                )
+            } else StubQSTileViewModel
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index c95a94e..f6cc724 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -37,11 +37,9 @@
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.core.LogLevel
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.CircleReveal
@@ -87,7 +85,7 @@
     private val lightRevealScrim: LightRevealScrim,
     private val authRippleInteractor: AuthRippleInteractor,
     private val facePropertyRepository: FacePropertyRepository,
-    rippleView: AuthRippleView?
+    rippleView: AuthRippleView?,
 ) :
     ViewController<AuthRippleView>(rippleView),
     CoreStartable,
@@ -108,15 +106,13 @@
     }
 
     init {
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            rippleView?.repeatWhenAttached {
-                repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
-                    authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
-                        if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
-                            showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
-                        } else {
-                            showUnlockRippleInternal(BiometricSourceType.FACE)
-                        }
+        rippleView?.repeatWhenAttached {
+            repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
+                authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
+                    if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
+                        showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
+                    } else {
+                        showUnlockRippleInternal(BiometricSourceType.FACE)
                     }
                 }
             }
@@ -134,29 +130,8 @@
         keyguardStateController.addCallback(this)
         wakefulnessLifecycle.addObserver(this)
         commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
-        if (!DeviceEntryUdfpsRefactor.isEnabled) {
-            biometricUnlockController.addListener(biometricModeListener)
-        }
     }
 
-    private val biometricModeListener =
-        object : BiometricUnlockController.BiometricUnlockEventsListener {
-            override fun onBiometricUnlockedWithKeyguardDismissal(
-                biometricSourceType: BiometricSourceType?
-            ) {
-                DeviceEntryUdfpsRefactor.assertInLegacyMode()
-                if (biometricSourceType != null) {
-                    showUnlockRippleInternal(biometricSourceType)
-                } else {
-                    logger.log(
-                        TAG,
-                        LogLevel.ERROR,
-                        "Unexpected scenario where biometricSourceType is null"
-                    )
-                }
-            }
-        }
-
     @VisibleForTesting
     public override fun onViewDetached() {
         udfpsController?.removeCallback(udfpsControllerCallback)
@@ -166,17 +141,10 @@
         keyguardStateController.removeCallback(this)
         wakefulnessLifecycle.removeObserver(this)
         commandRegistry.unregisterCommand("auth-ripple")
-        biometricUnlockController.removeListener(biometricModeListener)
 
         notificationShadeWindowController.setForcePluginOpen(false, this)
     }
 
-    @Deprecated("Update authRippleInteractor.showUnlockRipple instead of calling this.")
-    fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        showUnlockRippleInternal(biometricSourceType)
-    }
-
     private fun showUnlockRippleInternal(biometricSourceType: BiometricSourceType) {
         val keyguardNotShowing = !keyguardStateController.isShowing
         val unlockNotAllowed =
@@ -197,8 +165,8 @@
                         0,
                         Math.max(
                             Math.max(it.x, displayMetrics.widthPixels - it.x),
-                            Math.max(it.y, displayMetrics.heightPixels - it.y)
-                        )
+                            Math.max(it.y, displayMetrics.heightPixels - it.y),
+                        ),
                     )
                 logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius")
                 showUnlockedRipple()
@@ -213,8 +181,8 @@
                         0,
                         Math.max(
                             Math.max(it.x, displayMetrics.widthPixels - it.x),
-                            Math.max(it.y, displayMetrics.heightPixels - it.y)
-                        )
+                            Math.max(it.y, displayMetrics.heightPixels - it.y),
+                        ),
                     )
                 logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple")
                 showUnlockedRipple()
@@ -322,7 +290,7 @@
             override fun onBiometricAuthenticated(
                 userId: Int,
                 biometricSourceType: BiometricSourceType,
-                isStrongBiometric: Boolean
+                isStrongBiometric: Boolean,
             ) {
                 if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
                     mView.fadeDwellRipple()
@@ -337,7 +305,7 @@
 
             override fun onBiometricAcquired(
                 biometricSourceType: BiometricSourceType,
-                acquireInfo: Int
+                acquireInfo: Int,
             ) {
                 if (
                     biometricSourceType == BiometricSourceType.FINGERPRINT &&
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
deleted file mode 100644
index 242601d..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.util.AttributeSet
-
-/**
- * Class that coordinates non-HBM animations during BiometricPrompt.
- *
- * Currently doesn't draw anything.
- *
- * Note that [AuthBiometricFingerprintViewController] also shows UDFPS animations. At some point we should
- * de-dupe this if necessary.
- */
-class UdfpsBpView(context: Context, attrs: AttributeSet?) : UdfpsAnimationView(context, attrs) {
-
-    // Drawable isn't ever added to the view, so we don't currently show anything
-    private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context)
-
-    override fun getDrawable(): UdfpsDrawable = fingerprintDrawable
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
deleted file mode 100644
index e0455b5..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-
-/**
- * Class that coordinates non-HBM animations for biometric prompt.
- */
-class UdfpsBpViewController(
-    view: UdfpsBpView,
-    statusBarStateController: StatusBarStateController,
-    shadeInteractor: ShadeInteractor,
-    systemUIDialogManager: SystemUIDialogManager,
-    dumpManager: DumpManager,
-    udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) : UdfpsAnimationViewController<UdfpsBpView>(
-    view,
-    statusBarStateController,
-    shadeInteractor,
-    systemUIDialogManager,
-    dumpManager,
-    udfpsOverlayInteractor,
-) {
-    override val tag = "UdfpsBpViewController"
-
-    override fun shouldPauseAuth(): Boolean {
-        return false
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index a3904ca..2863e29 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -87,7 +87,6 @@
 import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.ScreenLifecycle;
@@ -98,7 +97,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -162,7 +160,6 @@
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
-    @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final SystemClock mSystemClock;
     @NonNull private final UnlockedScreenOffAnimationController
@@ -283,7 +280,6 @@
                         mKeyguardUpdateMonitor,
                         mDialogManager,
                         mDumpManager,
-                        mLockscreenShadeTransitionController,
                         mConfigurationController,
                         mKeyguardStateController,
                         mUnlockedScreenOffAnimationController,
@@ -291,10 +287,9 @@
                         requestId,
                         reason,
                         callback,
-                        (view, event, fromUdfpsView) -> onTouch(
+                        (view, event) -> onTouch(
                             requestId,
-                            event,
-                            fromUdfpsView
+                            event
                         ),
                             mActivityTransitionAnimator,
                         mPrimaryBouncerInteractor,
@@ -374,9 +369,6 @@
                 if (mOverlay == null || mOverlay.isHiding()) {
                     return;
                 }
-                if (!DeviceEntryUdfpsRefactor.isEnabled()) {
-                    ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message);
-                }
             });
         }
 
@@ -391,7 +383,7 @@
          */
         public void debugOnTouch(MotionEvent event) {
             final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
-            UdfpsController.this.onTouch(requestId, event, true);
+            UdfpsController.this.onTouch(requestId, event);
         }
 
         /**
@@ -449,22 +441,10 @@
         if (!mOverlayParams.equals(overlayParams)) {
             mOverlayParams = overlayParams;
 
-            if (DeviceEntryUdfpsRefactor.isEnabled()) {
-                if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
-                    mOverlay.updateOverlayParams(mOverlayParams);
-                } else {
-                    redrawOverlay();
-                }
+            if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
+                mOverlay.updateOverlayParams(mOverlayParams);
             } else {
-                final boolean wasShowingAlternateBouncer =
-                        mAlternateBouncerInteractor.isVisibleState();
-                // When the bounds change it's always to re-create the overlay's window with new
-                // LayoutParams. If the overlay needs to be shown, this will re-create and show the
-                // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
                 redrawOverlay();
-                if (wasShowingAlternateBouncer) {
-                    mKeyguardViewManager.showBouncer(true);
-                }
             }
         }
     }
@@ -563,11 +543,7 @@
         }
     }
 
-    private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
-        if (!fromUdfpsView) {
-            Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
-            return false;
-        }
+    private boolean onTouch(long requestId, @NonNull MotionEvent event) {
         if (mOverlay == null) {
             Log.w(TAG, "ignoring onTouch with null overlay");
             return false;
@@ -591,13 +567,6 @@
             if (!mIsAodInterruptActive) {
                 mOnFingerDown = false;
             }
-        } else if (!DeviceEntryUdfpsRefactor.isEnabled()) {
-            if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
-                    && !mAlternateBouncerInteractor.isVisibleState())
-                    || mPrimaryBouncerInteractor.isInTransit()) {
-                Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor");
-                return false;
-            }
         }
 
         final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId,
@@ -661,22 +630,13 @@
                         mStatusBarStateController.isDozing());
                 break;
 
-            case UNCHANGED:
-                if (mActivePointerId == MotionEvent.INVALID_POINTER_ID
-                        && mAlternateBouncerInteractor.isVisibleState()) {
-                    // No pointer on sensor, forward to keyguard if alternateBouncer is visible
-                    mKeyguardViewManager.onTouch(event);
-                }
-
             default:
                 break;
         }
         logBiometricTouch(processedTouch.getEvent(), data);
 
         // Always pilfer pointers that are within sensor area or when alternate bouncer is showing
-        if (mActivePointerId != MotionEvent.INVALID_POINTER_ID
-                || (mAlternateBouncerInteractor.isVisibleState()
-                && !DeviceEntryUdfpsRefactor.isEnabled())) {
+        if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) {
             shouldPilfer = true;
         }
 
@@ -692,14 +652,7 @@
     }
 
     private boolean shouldTryToDismissKeyguard() {
-        boolean onKeyguard = false;
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            onKeyguard = mKeyguardStateController.isShowing();
-        } else {
-            onKeyguard = mOverlay != null
-                    && mOverlay.getAnimationViewController()
-                        instanceof UdfpsKeyguardViewControllerLegacy;
-        }
+        boolean onKeyguard = mKeyguardStateController.isShowing();
         return onKeyguard
                 && mKeyguardStateController.canDismissLockScreen()
                 && !mAttemptedToDismissKeyguard;
@@ -719,7 +672,6 @@
             @NonNull FalsingManager falsingManager,
             @NonNull PowerManager powerManager,
             @NonNull AccessibilityManager accessibilityManager,
-            @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
             @NonNull ScreenLifecycle screenLifecycle,
             @NonNull VibratorHelper vibrator,
             @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
@@ -769,7 +721,6 @@
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
         mAccessibilityManager = accessibilityManager;
-        mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         screenLifecycle.addObserver(mScreenObserver);
         mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
         mConfigurationController = configurationController;
@@ -849,13 +800,7 @@
 
     @Override
     public void dozeTimeTick() {
-        if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) {
-            DeviceEntryUdfpsRefactor.assertInLegacyMode();
-            final View view = mOverlay.getTouchOverlay();
-            if (view != null) {
-                ((UdfpsView) view).dozeTimeTick();
-            }
-        }
+
     }
 
     private void redrawOverlay() {
@@ -915,17 +860,8 @@
         if (!isOptical()) {
             return;
         }
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            if (mUdfpsDisplayMode != null) {
-                mUdfpsDisplayMode.disable(null);
-            }
-        } else {
-            if (view != null) {
-                UdfpsView udfpsView = (UdfpsView) view;
-                if (udfpsView.isDisplayConfigured()) {
-                    udfpsView.unconfigureDisplay();
-                }
-            }
+        if (mUdfpsDisplayMode != null) {
+            mUdfpsDisplayMode.disable(null);
         }
     }
 
@@ -1118,11 +1054,7 @@
             if (mIgnoreRefreshRate) {
                 dispatchOnUiReady(requestId);
             } else {
-                if (DeviceEntryUdfpsRefactor.isEnabled()) {
                     mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId));
-                } else {
-                    ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId));
-                }
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 1bac0bc..a1efc19 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -23,8 +23,6 @@
 import android.graphics.Rect
 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
 import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
 import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
 import android.hardware.biometrics.BiometricRequestConstants.RequestReason
@@ -42,7 +40,6 @@
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
-import androidx.annotation.LayoutRes
 import androidx.annotation.VisibleForTesting
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -56,7 +53,6 @@
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -64,7 +60,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -102,7 +97,6 @@
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val dialogManager: SystemUIDialogManager,
     private val dumpManager: DumpManager,
-    private val transitionController: LockscreenShadeTransitionController,
     private val configurationController: ConfigurationController,
     private val keyguardStateController: KeyguardStateController,
     private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
@@ -110,7 +104,7 @@
     val requestId: Long,
     @RequestReason val requestReason: Int,
     private val controllerCallback: IUdfpsOverlayControllerCallback,
-    private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+    private val onTouch: (View, MotionEvent) -> Boolean,
     private val activityTransitionAnimator: ActivityTransitionAnimator,
     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -133,23 +127,15 @@
             .map {} // map to Unit
     private var listenForCurrentKeyguardState: Job? = null
     private var addViewRunnable: Runnable? = null
-    private var overlayViewLegacy: UdfpsView? = null
-        private set
-
     private var overlayTouchView: UdfpsTouchOverlay? = null
 
     /**
-     * Get the current UDFPS overlay touch view which is a different View depending on whether the
-     * DeviceEntryUdfpsRefactor flag is enabled or not.
+     * Get the current UDFPS overlay touch view
      *
      * @return The view, when [isShowing], else null
      */
     fun getTouchOverlay(): View? {
-        return if (DeviceEntryUdfpsRefactor.isEnabled) {
-            overlayTouchView
-        } else {
-            overlayViewLegacy
-        }
+        return overlayTouchView
     }
 
     private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
@@ -161,7 +147,7 @@
         WindowManager.LayoutParams(
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                 0 /* flags set in computeLayoutParams() */,
-                PixelFormat.TRANSLUCENT
+                PixelFormat.TRANSLUCENT,
             )
             .apply {
                 title = TAG
@@ -188,10 +174,6 @@
     val isHiding: Boolean
         get() = getTouchOverlay() == null
 
-    /** The animation controller if the overlay [isShowing]. */
-    val animationViewController: UdfpsAnimationViewController<*>?
-        get() = overlayViewLegacy?.animationViewController
-
     private var touchExplorationEnabled = false
 
     private fun shouldRemoveEnrollmentUi(): Boolean {
@@ -199,7 +181,7 @@
             return Settings.Global.getInt(
                 context.contentResolver,
                 SETTING_REMOVE_ENROLLMENT_UI,
-                0 /* def */
+                0, /* def */
             ) != 0
         }
         return false
@@ -212,63 +194,43 @@
             overlayParams = params
             sensorBounds = Rect(params.sensorBounds)
             try {
-                if (DeviceEntryUdfpsRefactor.isEnabled) {
-                    overlayTouchView =
-                        (inflater.inflate(R.layout.udfps_touch_overlay, null, false)
-                                as UdfpsTouchOverlay)
-                            .apply {
-                                // This view overlaps the sensor area
-                                // prevent it from being selectable during a11y
-                                if (requestReason.isImportantForAccessibility()) {
-                                    importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
-                                }
-
-                                addViewNowOrLater(this, null)
-                                when (requestReason) {
-                                    REASON_AUTH_KEYGUARD ->
-                                        UdfpsTouchOverlayBinder.bind(
-                                            view = this,
-                                            viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
-                                            udfpsOverlayInteractor = udfpsOverlayInteractor,
-                                        )
-                                    else ->
-                                        UdfpsTouchOverlayBinder.bind(
-                                            view = this,
-                                            viewModel = defaultUdfpsTouchOverlayViewModel.get(),
-                                            udfpsOverlayInteractor = udfpsOverlayInteractor,
-                                        )
-                                }
-                            }
-                } else {
-                    overlayViewLegacy =
-                        (inflater.inflate(R.layout.udfps_view, null, false) as UdfpsView).apply {
-                            overlayParams = params
-                            setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
-                            val animation = inflateUdfpsAnimation(this, controller)
-                            if (animation != null) {
-                                animation.init()
-                                animationViewController = animation
-                            }
+                overlayTouchView =
+                    (inflater.inflate(R.layout.udfps_touch_overlay, null, false)
+                            as UdfpsTouchOverlay)
+                        .apply {
                             // This view overlaps the sensor area
                             // prevent it from being selectable during a11y
                             if (requestReason.isImportantForAccessibility()) {
                                 importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
                             }
 
-                            addViewNowOrLater(this, animation)
-                            sensorRect = sensorBounds
+                            addViewNowOrLater(this, null)
+                            when (requestReason) {
+                                REASON_AUTH_KEYGUARD ->
+                                    UdfpsTouchOverlayBinder.bind(
+                                        view = this,
+                                        viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
+                                        udfpsOverlayInteractor = udfpsOverlayInteractor,
+                                    )
+                                else ->
+                                    UdfpsTouchOverlayBinder.bind(
+                                        view = this,
+                                        viewModel = defaultUdfpsTouchOverlayViewModel.get(),
+                                        udfpsOverlayInteractor = udfpsOverlayInteractor,
+                                    )
+                            }
                         }
-                }
+
                 getTouchOverlay()?.apply {
                     touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled
                     overlayTouchListener = TouchExplorationStateChangeListener {
                         if (accessibilityManager.isTouchExplorationEnabled) {
-                            setOnHoverListener { v, event -> onTouch(v, event, true) }
+                            setOnHoverListener { v, event -> onTouch(v, event) }
                             setOnTouchListener(null)
                             touchExplorationEnabled = true
                         } else {
                             setOnHoverListener(null)
-                            setOnTouchListener { v, event -> onTouch(v, event, true) }
+                            setOnTouchListener { v, event -> onTouch(v, event) }
                             touchExplorationEnabled = false
                         }
                     }
@@ -312,7 +274,6 @@
     }
 
     fun updateOverlayParams(updatedOverlayParams: UdfpsOverlayParams) {
-        DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
         overlayParams = updatedOverlayParams
         sensorBounds = updatedOverlayParams.sensorBounds
         getTouchOverlay()?.let {
@@ -326,108 +287,11 @@
         }
     }
 
-    fun inflateUdfpsAnimation(
-        view: UdfpsView,
-        controller: UdfpsController
-    ): UdfpsAnimationViewController<*>? {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-
-        val isEnrollment =
-            when (requestReason) {
-                REASON_ENROLL_FIND_SENSOR,
-                REASON_ENROLL_ENROLLING -> true
-                else -> false
-            }
-
-        val filteredRequestReason =
-            if (isEnrollment && shouldRemoveEnrollmentUi()) {
-                REASON_AUTH_OTHER
-            } else {
-                requestReason
-            }
-
-        return when (filteredRequestReason) {
-            REASON_ENROLL_FIND_SENSOR,
-            REASON_ENROLL_ENROLLING -> {
-                // Enroll udfps UI is handled by settings, so use empty view here
-                UdfpsFpmEmptyViewController(
-                    view.addUdfpsView(R.layout.udfps_fpm_empty_view) {
-                        updateAccessibilityViewLocation(sensorBounds)
-                    },
-                    statusBarStateController,
-                    shadeInteractor,
-                    dialogManager,
-                    dumpManager,
-                    udfpsOverlayInteractor,
-                )
-            }
-            REASON_AUTH_KEYGUARD -> {
-                UdfpsKeyguardViewControllerLegacy(
-                    view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
-                        updateSensorLocation(sensorBounds)
-                    },
-                    statusBarStateController,
-                    statusBarKeyguardViewManager,
-                    keyguardUpdateMonitor,
-                    dumpManager,
-                    transitionController,
-                    configurationController,
-                    keyguardStateController,
-                    unlockedScreenOffAnimationController,
-                    dialogManager,
-                    controller,
-                    activityTransitionAnimator,
-                    primaryBouncerInteractor,
-                    alternateBouncerInteractor,
-                    udfpsKeyguardAccessibilityDelegate,
-                    selectedUserInteractor,
-                    transitionInteractor,
-                    shadeInteractor,
-                    udfpsOverlayInteractor,
-                )
-            }
-            REASON_AUTH_BP -> {
-                // note: empty controller, currently shows no visual affordance
-                UdfpsBpViewController(
-                    view.addUdfpsView(R.layout.udfps_bp_view),
-                    statusBarStateController,
-                    shadeInteractor,
-                    dialogManager,
-                    dumpManager,
-                    udfpsOverlayInteractor,
-                )
-            }
-            REASON_AUTH_OTHER,
-            REASON_AUTH_SETTINGS -> {
-                UdfpsFpmEmptyViewController(
-                    view.addUdfpsView(R.layout.udfps_fpm_empty_view),
-                    statusBarStateController,
-                    shadeInteractor,
-                    dialogManager,
-                    dumpManager,
-                    udfpsOverlayInteractor,
-                )
-            }
-            else -> {
-                Log.e(TAG, "Animation for reason $requestReason not supported yet")
-                null
-            }
-        }
-    }
-
     /** Hide the overlay or return false and do nothing if it is already hidden. */
     fun hide(): Boolean {
         val wasShowing = isShowing
 
-        overlayViewLegacy?.apply {
-            if (isDisplayConfigured) {
-                unconfigureDisplay()
-            }
-            animationViewController = null
-        }
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            udfpsDisplayModeProvider.disable(null)
-        }
+        udfpsDisplayModeProvider.disable(null)
         getTouchOverlay()?.apply {
             if (this.parent != null) {
                 windowManager.removeView(this)
@@ -440,7 +304,6 @@
             }
         }
 
-        overlayViewLegacy = null
         overlayTouchView = null
         overlayTouchListener = null
         listenForCurrentKeyguardState?.cancel()
@@ -490,7 +353,7 @@
                         Surface.rotationToString(rot) +
                         " animation=$animation" +
                         " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
-                        " isOccluded=${keyguardStateController.isOccluded}"
+                        " isOccluded=${keyguardStateController.isOccluded}",
                 )
             } else {
                 Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot))
@@ -498,14 +361,14 @@
                     rotatedBounds,
                     overlayParams.naturalDisplayWidth,
                     overlayParams.naturalDisplayHeight,
-                    rot
+                    rot,
                 )
 
                 RotationUtils.rotateBounds(
                     sensorBounds,
                     overlayParams.naturalDisplayWidth,
                     overlayParams.naturalDisplayHeight,
-                    rot
+                    rot,
                 )
             }
         }
@@ -519,14 +382,7 @@
     }
 
     private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
-        val keyguardNotShowing =
-            if (DeviceEntryUdfpsRefactor.isEnabled) {
-                !keyguardStateController.isShowing
-            } else {
-                animation !is UdfpsKeyguardViewControllerLegacy
-            }
-
-        if (keyguardNotShowing) {
+        if (!keyguardStateController.isShowing) {
             // always rotate view if we're not on the keyguard
             return true
         }
@@ -534,16 +390,6 @@
         // on the keyguard, make sure we don't rotate if we're going to sleep or not occluded
         return !(keyguardUpdateMonitor.isGoingToSleep || !keyguardStateController.isOccluded)
     }
-
-    private inline fun <reified T : View> UdfpsView.addUdfpsView(
-        @LayoutRes id: Int,
-        init: T.() -> Unit = {}
-    ): T {
-        val subView = inflater.inflate(id, null) as T
-        addView(subView)
-        subView.init()
-        return subView
-    }
 }
 
 @RequestReason
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
deleted file mode 100644
index 0838855..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.graphics.Rect
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewGroup
-import com.android.systemui.res.R
-
-/**
- * View corresponding with udfps_fpm_empty_view.xml
- *
- * Currently doesn't draw anything.
- */
-class UdfpsFpmEmptyView(
-    context: Context,
-    attrs: AttributeSet?
-) : UdfpsAnimationView(context, attrs) {
-
-    // Drawable isn't ever added to the view, so we don't currently show anything
-    private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context)
-
-    override fun getDrawable(): UdfpsDrawable = fingerprintDrawable
-
-    fun updateAccessibilityViewLocation(sensorBounds: Rect) {
-        val fingerprintAccessibilityView: View =
-            requireViewById(R.id.udfps_enroll_accessibility_view)
-        val params: ViewGroup.LayoutParams = fingerprintAccessibilityView.layoutParams
-        params.width = sensorBounds.width()
-        params.height = sensorBounds.height()
-        fingerprintAccessibilityView.layoutParams = params
-        fingerprintAccessibilityView.requestLayout()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
deleted file mode 100644
index cfbbc26..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-
-/**
- * Class that coordinates non-HBM animations for non keyguard, or biometric prompt states.
- *
- * Currently doesn't draw anything.
- */
-class UdfpsFpmEmptyViewController(
-    view: UdfpsFpmEmptyView,
-    statusBarStateController: StatusBarStateController,
-    shadeInteractor: ShadeInteractor,
-    systemUIDialogManager: SystemUIDialogManager,
-    dumpManager: DumpManager,
-    udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) : UdfpsAnimationViewController<UdfpsFpmEmptyView>(
-    view,
-    statusBarStateController,
-    shadeInteractor,
-    systemUIDialogManager,
-    dumpManager,
-    udfpsOverlayInteractor,
-) {
-    override val tag = "UdfpsFpmOtherViewController"
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
deleted file mode 100644
index c3d9240..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.content.res.Configuration
-import android.util.MathUtils
-import android.view.View
-import androidx.annotation.VisibleForTesting
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
-import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.Edge
-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.DREAMING
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
-import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.res.R
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.OccludingAppBiometricUI
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import java.io.PrintWriter
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-
-/** Class that coordinates non-HBM animations during keyguard authentication. */
-@ExperimentalCoroutinesApi
-open class UdfpsKeyguardViewControllerLegacy(
-    private val view: UdfpsKeyguardViewLegacy,
-    statusBarStateController: StatusBarStateController,
-    private val keyguardViewManager: StatusBarKeyguardViewManager,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    dumpManager: DumpManager,
-    private val lockScreenShadeTransitionController: LockscreenShadeTransitionController,
-    private val configurationController: ConfigurationController,
-    private val keyguardStateController: KeyguardStateController,
-    private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
-    systemUIDialogManager: SystemUIDialogManager,
-    private val udfpsController: UdfpsController,
-    private val activityTransitionAnimator: ActivityTransitionAnimator,
-    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
-    private val alternateBouncerInteractor: AlternateBouncerInteractor,
-    private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
-    private val selectedUserInteractor: SelectedUserInteractor,
-    private val transitionInteractor: KeyguardTransitionInteractor,
-    shadeInteractor: ShadeInteractor,
-    udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) :
-    UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
-        view,
-        statusBarStateController,
-        shadeInteractor,
-        systemUIDialogManager,
-        dumpManager,
-        udfpsOverlayInteractor,
-    ) {
-    private val uniqueIdentifier = this.toString()
-    private var showingUdfpsBouncer = false
-    private var udfpsRequested = false
-    private var qsExpansion = 0f
-    private var faceDetectRunning = false
-    private var statusBarState = 0
-    private var transitionToFullShadeProgress = 0f
-    private var lastDozeAmount = 0f
-    private var panelExpansionFraction = 0f
-    private var launchTransitionFadingAway = false
-    private var isLaunchingActivity = false
-    private var activityLaunchProgress = 0f
-    private var inputBouncerExpansion = 0f
-
-    private val stateListener: StatusBarStateController.StateListener =
-        object : StatusBarStateController.StateListener {
-            override fun onStateChanged(statusBarState: Int) {
-                this@UdfpsKeyguardViewControllerLegacy.statusBarState = statusBarState
-                updateAlpha()
-                updatePauseAuth()
-            }
-        }
-
-    private val configurationListener: ConfigurationController.ConfigurationListener =
-        object : ConfigurationController.ConfigurationListener {
-            override fun onUiModeChanged() {
-                view.updateColor()
-            }
-
-            override fun onThemeChanged() {
-                view.updateColor()
-            }
-
-            override fun onConfigChanged(newConfig: Configuration) {
-                updateScaleFactor()
-                view.updatePadding()
-                view.updateColor()
-            }
-        }
-
-    private val keyguardStateControllerCallback: KeyguardStateController.Callback =
-        object : KeyguardStateController.Callback {
-            override fun onUnlockedChanged() {
-                updatePauseAuth()
-            }
-
-            override fun onLaunchTransitionFadingAwayChanged() {
-                launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
-                updatePauseAuth()
-            }
-        }
-
-    private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener =
-        object : ActivityTransitionAnimator.Listener {
-            override fun onTransitionAnimationStart() {
-                isLaunchingActivity = true
-                activityLaunchProgress = 0f
-                updateAlpha()
-            }
-
-            override fun onTransitionAnimationEnd() {
-                isLaunchingActivity = false
-                updateAlpha()
-            }
-
-            override fun onTransitionAnimationProgress(linearProgress: Float) {
-                activityLaunchProgress = linearProgress
-                updateAlpha()
-            }
-        }
-
-    private val statusBarKeyguardViewManagerCallback: KeyguardViewManagerCallback =
-        object : KeyguardViewManagerCallback {
-            override fun onQSExpansionChanged(qsExpansion: Float) {
-                this@UdfpsKeyguardViewControllerLegacy.qsExpansion = qsExpansion
-                updateAlpha()
-                updatePauseAuth()
-            }
-        }
-
-    private val occludingAppBiometricUI: OccludingAppBiometricUI =
-        object : OccludingAppBiometricUI {
-            override fun requestUdfps(request: Boolean, color: Int) {
-                udfpsRequested = request
-                view.requestUdfps(request, color)
-                updateAlpha()
-                updatePauseAuth()
-            }
-
-            override fun dump(pw: PrintWriter) {
-                pw.println(tag)
-            }
-        }
-
-    override val tag: String
-        get() = TAG
-
-    override fun onInit() {
-        super.onInit()
-        keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
-    }
-
-    init {
-        com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        view.repeatWhenAttached {
-            // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
-            // can make the view not visible; and we still want to listen for events
-            // that may make the view visible again.
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                listenForBouncerExpansion(this)
-                listenForAlternateBouncerVisibility(this)
-                listenForOccludedToAodTransition(this)
-                listenForGoneToAodTransition(this)
-                listenForLockscreenAodTransitions(this)
-                listenForAodToOccludedTransitions(this)
-                listenForAlternateBouncerToAodTransitions(this)
-                listenForDreamingToAodTransitions(this)
-                listenForPrimaryBouncerToAodTransitions(this)
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor
-                .transition(
-                    edge = Edge.create(Scenes.Bouncer, AOD),
-                    edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD)
-                )
-                .collect { transitionStep ->
-                    view.onDozeAmountChanged(
-                        transitionStep.value,
-                        transitionStep.value,
-                        ANIMATE_APPEAR_ON_SCREEN_OFF,
-                    )
-                }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep ->
-                view.onDozeAmountChanged(
-                    transitionStep.value,
-                    transitionStep.value,
-                    ANIMATE_APPEAR_ON_SCREEN_OFF,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect {
-                transitionStep ->
-                view.onDozeAmountChanged(
-                    transitionStep.value,
-                    transitionStep.value,
-                    UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep ->
-                view.onDozeAmountChanged(
-                    1f - transitionStep.value,
-                    1f - transitionStep.value,
-                    UdfpsKeyguardViewLegacy.ANIMATION_NONE,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep ->
-                view.onDozeAmountChanged(
-                    transitionStep.value,
-                    transitionStep.value,
-                    ANIMATE_APPEAR_ON_SCREEN_OFF,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor
-                .transition(
-                    edge = Edge.create(Scenes.Gone, AOD),
-                    edgeWithoutSceneContainer = Edge.create(GONE, AOD)
-                )
-                .collect { transitionStep ->
-                    view.onDozeAmountChanged(
-                        transitionStep.value,
-                        transitionStep.value,
-                        ANIMATE_APPEAR_ON_SCREEN_OFF,
-                    )
-                }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transitionValue(AOD).collect {
-                view.onDozeAmountChanged(
-                    it,
-                    it,
-                    UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
-        return scope.launch {
-            primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
-                inputBouncerExpansion = bouncerExpansion
-
-                panelExpansionFraction =
-                    if (keyguardViewManager.isPrimaryBouncerInTransit) {
-                        aboutToShowBouncerProgress(1f - bouncerExpansion)
-                    } else {
-                        1f - bouncerExpansion
-                    }
-                updateAlpha()
-                updatePauseAuth()
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job {
-        return scope.launch {
-            alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
-                showUdfpsBouncer(isVisible)
-            }
-        }
-    }
-
-    public override fun onViewAttached() {
-        super.onViewAttached()
-        alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier)
-        val dozeAmount = statusBarStateController.dozeAmount
-        lastDozeAmount = dozeAmount
-        stateListener.onDozeAmountChanged(dozeAmount, dozeAmount)
-        statusBarStateController.addCallback(stateListener)
-        udfpsRequested = false
-        launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
-        keyguardStateController.addCallback(keyguardStateControllerCallback)
-        statusBarState = statusBarStateController.state
-        qsExpansion = keyguardViewManager.qsExpansion
-        keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
-        configurationController.addCallback(configurationListener)
-        updateScaleFactor()
-        view.updatePadding()
-        updateAlpha()
-        updatePauseAuth()
-        keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
-        lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this
-        activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener)
-        view.startIconAsyncInflate {
-            val animationViewInternal: View =
-                view.requireViewById(R.id.udfps_animation_view_internal)
-            animationViewInternal.accessibilityDelegate = udfpsKeyguardAccessibilityDelegate
-        }
-    }
-
-    public override fun onViewDetached() {
-        super.onViewDetached()
-        alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier)
-        faceDetectRunning = false
-        keyguardStateController.removeCallback(keyguardStateControllerCallback)
-        statusBarStateController.removeCallback(stateListener)
-        keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI)
-        configurationController.removeCallback(configurationListener)
-        if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) {
-            lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null
-        }
-        activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener)
-        keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<String>) {
-        super.dump(pw, args)
-        pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer")
-        pw.println(
-            "altBouncerInteractor#isAlternateBouncerVisible=" +
-                "${alternateBouncerInteractor.isVisibleState()}"
-        )
-        pw.println(
-            "altBouncerInteractor#canShowAlternateBouncerForFingerprint=" +
-                "${alternateBouncerInteractor.canShowAlternateBouncerForFingerprint()}"
-        )
-        pw.println("faceDetectRunning=$faceDetectRunning")
-        pw.println("statusBarState=" + StatusBarState.toString(statusBarState))
-        pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress")
-        pw.println("qsExpansion=$qsExpansion")
-        pw.println("panelExpansionFraction=$panelExpansionFraction")
-        pw.println("unpausedAlpha=" + view.unpausedAlpha)
-        pw.println("udfpsRequestedByApp=$udfpsRequested")
-        pw.println("launchTransitionFadingAway=$launchTransitionFadingAway")
-        pw.println("lastDozeAmount=$lastDozeAmount")
-        pw.println("inputBouncerExpansion=$inputBouncerExpansion")
-        view.dump(pw)
-    }
-
-    /**
-     * Overrides non-bouncer show logic in shouldPauseAuth to still show icon.
-     *
-     * @return whether the udfpsBouncer has been newly shown or hidden
-     */
-    private fun showUdfpsBouncer(show: Boolean): Boolean {
-        if (showingUdfpsBouncer == show) {
-            return false
-        }
-        val udfpsAffordanceWasNotShowing = shouldPauseAuth()
-        showingUdfpsBouncer = show
-        if (showingUdfpsBouncer) {
-            if (udfpsAffordanceWasNotShowing) {
-                view.animateInUdfpsBouncer(null)
-            }
-            view.announceForAccessibility(
-                view.context.getString(R.string.accessibility_fingerprint_bouncer)
-            )
-        }
-        updateAlpha()
-        updatePauseAuth()
-        return true
-    }
-
-    /**
-     * Returns true if the fingerprint manager is running but we want to temporarily pause
-     * authentication. On the keyguard, we may want to show udfps when the shade is expanded, so
-     * this can be overridden with the showBouncer method.
-     */
-    override fun shouldPauseAuth(): Boolean {
-        if (showingUdfpsBouncer) {
-            return false
-        }
-        if (
-            udfpsRequested &&
-                !notificationShadeVisible &&
-                !isInputBouncerFullyVisible() &&
-                keyguardStateController.isShowing
-        ) {
-            return false
-        }
-        if (launchTransitionFadingAway) {
-            return true
-        }
-
-        // Only pause auth if we're not on the keyguard AND we're not transitioning to doze.
-        // For the UnlockedScreenOffAnimation, the statusBarState is
-        // delayed. However, we still animate in the UDFPS affordance with the
-        // unlockedScreenOffDozeAnimator.
-        if (
-            statusBarState != StatusBarState.KEYGUARD &&
-                !unlockedScreenOffAnimationController.isAnimationPlaying()
-        ) {
-            return true
-        }
-        if (isBouncerExpansionGreaterThan(.5f)) {
-            return true
-        }
-        if (
-            keyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                selectedUserInteractor.getSelectedUserId()
-            )
-        ) {
-            // If the device was unlocked by a biometric, immediately hide the UDFPS icon to avoid
-            // overlap with the LockIconView. Shortly afterwards, UDFPS will stop running.
-            return true
-        }
-        return view.unpausedAlpha < 255 * .1
-    }
-
-    fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean {
-        return inputBouncerExpansion >= bouncerExpansionThreshold
-    }
-
-    fun isInputBouncerFullyVisible(): Boolean {
-        return inputBouncerExpansion == 1f
-    }
-
-    override fun listenForTouchesOutsideView(): Boolean {
-        return true
-    }
-
-    /**
-     * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
-     * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down
-     * to expand the notification shade from the empty space in the middle of the lock screen.
-     */
-    fun setTransitionToFullShadeProgress(progress: Float) {
-        transitionToFullShadeProgress = progress
-        updateAlpha()
-    }
-
-    /**
-     * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's alpha is
-     * based on the doze amount.
-     */
-    override fun updateAlpha() {
-        // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested,
-        // then the keyguard is occluded by some application - so instead use the input bouncer
-        // hidden amount to determine the fade.
-        val expansion = if (udfpsRequested) getInputBouncerHiddenAmt() else panelExpansionFraction
-        var alpha: Int =
-            if (showingUdfpsBouncer) 255
-            else MathUtils.constrain(MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f).toInt()
-        if (!showingUdfpsBouncer) {
-            // swipe from top of the lockscreen to expand full QS:
-            alpha =
-                (alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion)))
-                    .toInt()
-
-            // swipe from the middle (empty space) of lockscreen to expand the notification shade:
-            alpha = (alpha * (1.0f - transitionToFullShadeProgress)).toInt()
-
-            // Fade out the icon if we are animating an activity launch over the lockscreen and the
-            // activity didn't request the UDFPS.
-            if (isLaunchingActivity && !udfpsRequested) {
-                val udfpsActivityLaunchAlphaMultiplier =
-                    1f -
-                        (activityLaunchProgress *
-                                (ActivityTransitionAnimator.TIMINGS.totalDuration / 83))
-                            .coerceIn(0f, 1f)
-                alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt()
-            }
-
-            // Fade out alpha when a dialog is shown
-            // Fade in alpha when a dialog is hidden
-            alpha = (alpha * view.dialogSuggestedAlpha).toInt()
-        }
-        view.unpausedAlpha = alpha
-    }
-
-    private fun getInputBouncerHiddenAmt(): Float {
-        return 1f - inputBouncerExpansion
-    }
-
-    /** Update the scale factor based on the device's resolution. */
-    private fun updateScaleFactor() {
-        udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) }
-    }
-
-    companion object {
-        const val TAG = "UdfpsKeyguardViewController"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
deleted file mode 100644
index 6d4eea8..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.util.MathUtils;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
-
-import com.android.app.animation.Interpolators;
-import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
-
-import com.airbnb.lottie.LottieAnimationView;
-import com.airbnb.lottie.LottieProperty;
-import com.airbnb.lottie.model.KeyPath;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * View corresponding with udfps_keyguard_view_legacy.xml
- */
-public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView {
-    private UdfpsDrawable mFingerprintDrawable; // placeholder
-    private LottieAnimationView mAodFp;
-    private LottieAnimationView mLockScreenFp;
-
-    // used when highlighting fp icon:
-    private int mTextColorPrimary;
-    private ImageView mBgProtection;
-    boolean mUdfpsRequested;
-
-    private AnimatorSet mBackgroundInAnimator = new AnimatorSet();
-    private int mAlpha; // 0-255
-    private float mScaleFactor = 1;
-    private Rect mSensorBounds = new Rect();
-
-    // AOD anti-burn-in offsets
-    private final int mMaxBurnInOffsetX;
-    private final int mMaxBurnInOffsetY;
-    private float mInterpolatedDarkAmount;
-    private int mAnimationType = ANIMATION_NONE;
-    private boolean mFullyInflated;
-    private Runnable mOnFinishInflateRunnable;
-
-    public UdfpsKeyguardViewLegacy(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        mFingerprintDrawable = new UdfpsFpDrawable(context);
-
-        mMaxBurnInOffsetX = context.getResources()
-            .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
-        mMaxBurnInOffsetY = context.getResources()
-            .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
-    }
-
-    /**
-     * Inflate internal udfps view on a background thread and call the onFinishRunnable
-     * when inflation is finished.
-     */
-    public void startIconAsyncInflate(Runnable onFinishInflate) {
-        mOnFinishInflateRunnable = onFinishInflate;
-        // inflate Lottie views on a background thread in case it takes a while to inflate
-        AsyncLayoutInflater inflater = new AsyncLayoutInflater(mContext);
-        inflater.inflate(R.layout.udfps_keyguard_view_internal, this,
-                mLayoutInflaterFinishListener);
-    }
-
-    @Override
-    public UdfpsDrawable getDrawable() {
-        return mFingerprintDrawable;
-    }
-
-    @Override
-    void onSensorRectUpdated(RectF bounds) {
-        super.onSensorRectUpdated(bounds);
-        bounds.round(this.mSensorBounds);
-        postInvalidate();
-    }
-
-    @Override
-    void onDisplayConfiguring() {
-    }
-
-    @Override
-    void onDisplayUnconfigured() {
-    }
-
-    @Override
-    public boolean dozeTimeTick() {
-        updateBurnInOffsets();
-        return true;
-    }
-
-    private void updateBurnInOffsets() {
-        if (!mFullyInflated) {
-            return;
-        }
-
-        // if we're animating from screen off, we can immediately place the icon in the
-        // AoD-burn in location, else we need to translate the icon from LS => AoD.
-        final float darkAmountForAnimation = mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF
-                ? 1f : mInterpolatedDarkAmount;
-        final float burnInOffsetX = MathUtils.lerp(0f,
-            getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
-                - mMaxBurnInOffsetX, darkAmountForAnimation);
-        final float burnInOffsetY = MathUtils.lerp(0f,
-            getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
-                - mMaxBurnInOffsetY, darkAmountForAnimation);
-        final float burnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(),
-                darkAmountForAnimation);
-
-        if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) {
-            mLockScreenFp.setTranslationX(burnInOffsetX);
-            mLockScreenFp.setTranslationY(burnInOffsetY);
-            mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
-            mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount);
-        } else if (darkAmountForAnimation == 0f) {
-            // we're on the lockscreen and should use mAlpha (changes based on shade expansion)
-            mLockScreenFp.setTranslationX(0);
-            mLockScreenFp.setTranslationY(0);
-            mBgProtection.setAlpha(mAlpha / 255f);
-            mLockScreenFp.setAlpha(mAlpha / 255f);
-        } else {
-            mBgProtection.setAlpha(0f);
-            mLockScreenFp.setAlpha(0f);
-        }
-        mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount);
-
-        mAodFp.setTranslationX(burnInOffsetX);
-        mAodFp.setTranslationY(burnInOffsetY);
-        mAodFp.setProgress(burnInProgress);
-        mAodFp.setAlpha(mInterpolatedDarkAmount);
-
-        // done animating
-        final boolean doneAnimatingBetweenAodAndLS =
-                mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
-                        && (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f);
-        final boolean doneAnimatingUnlockedScreenOff =
-                mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF
-                        && (mInterpolatedDarkAmount == 1f);
-        if (doneAnimatingBetweenAodAndLS || doneAnimatingUnlockedScreenOff) {
-            mAnimationType = ANIMATION_NONE;
-        }
-    }
-
-    void requestUdfps(boolean request, int color) {
-        mUdfpsRequested = request;
-    }
-
-    void updateColor() {
-        if (!mFullyInflated) {
-            return;
-        }
-
-        mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
-                com.android.internal.R.attr.materialColorOnSurface);
-        final int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
-                com.android.internal.R.attr.materialColorSurfaceContainerHigh);
-        mBgProtection.setImageTintList(ColorStateList.valueOf(backgroundColor));
-        mLockScreenFp.invalidate(); // updated with a valueCallback
-    }
-
-    void setScaleFactor(float scale) {
-        mScaleFactor = scale;
-    }
-
-    void updatePadding() {
-        if (mLockScreenFp == null || mAodFp == null) {
-            return;
-        }
-
-        final int defaultPaddingPx =
-                getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
-        final int padding = (int) (defaultPaddingPx * mScaleFactor);
-        mLockScreenFp.setPadding(padding, padding, padding, padding);
-        mAodFp.setPadding(padding, padding, padding, padding);
-    }
-
-    /**
-     * @param alpha between 0 and 255
-     */
-    void setUnpausedAlpha(int alpha) {
-        mAlpha = alpha;
-        updateAlpha();
-    }
-
-    /**
-     * @return alpha between 0 and 255
-     */
-    int getUnpausedAlpha() {
-        return mAlpha;
-    }
-
-    @Override
-    protected int updateAlpha() {
-        int alpha = super.updateAlpha();
-        updateBurnInOffsets();
-        return alpha;
-    }
-
-    @Override
-    int calculateAlpha() {
-        if (mPauseAuth) {
-            return 0;
-        }
-        return mAlpha;
-    }
-
-    static final int ANIMATION_NONE = 0;
-    static final int ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN = 1;
-    static final int ANIMATE_APPEAR_ON_SCREEN_OFF = 2;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ANIMATION_NONE, ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, ANIMATE_APPEAR_ON_SCREEN_OFF})
-    private @interface AnimationType {}
-
-    void onDozeAmountChanged(float linear, float eased, @AnimationType int animationType) {
-        mAnimationType = animationType;
-        mInterpolatedDarkAmount = eased;
-        updateAlpha();
-    }
-
-    void updateSensorLocation(@NonNull Rect sensorBounds) {
-        mSensorBounds.set(sensorBounds);
-    }
-
-    /**
-     * Animates in the bg protection circle behind the fp icon to highlight the icon.
-     */
-    void animateInUdfpsBouncer(Runnable onEndAnimation) {
-        if (mBackgroundInAnimator.isRunning() || !mFullyInflated) {
-            // already animating in or not yet inflated
-            return;
-        }
-
-        // fade in and scale up
-        mBackgroundInAnimator = new AnimatorSet();
-        mBackgroundInAnimator.playTogether(
-                ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f));
-        mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mBackgroundInAnimator.setDuration(500);
-        mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (onEndAnimation != null) {
-                    onEndAnimation.run();
-                }
-            }
-        });
-        mBackgroundInAnimator.start();
-    }
-
-    /**
-     * Print debugging information for this class.
-     */
-    public void dump(PrintWriter pw) {
-        pw.println("UdfpsKeyguardView (" + this + ")");
-        pw.println("    mPauseAuth=" + mPauseAuth);
-        pw.println("    mUnpausedAlpha=" + getUnpausedAlpha());
-        pw.println("    mUdfpsRequested=" + mUdfpsRequested);
-        pw.println("    mInterpolatedDarkAmount=" + mInterpolatedDarkAmount);
-        pw.println("    mAnimationType=" + mAnimationType);
-    }
-
-    private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener =
-            new AsyncLayoutInflater.OnInflateFinishedListener() {
-                @Override
-                public void onInflateFinished(View view, int resid, ViewGroup parent) {
-                    mFullyInflated = true;
-                    mAodFp = view.findViewById(R.id.udfps_aod_fp);
-                    mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp);
-                    mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg);
-
-                    updatePadding();
-                    updateColor();
-                    updateAlpha();
-
-                    final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                    lp.width = mSensorBounds.width();
-                    lp.height = mSensorBounds.height();
-                    RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds));
-                    lp.setMarginsRelative((int) relativeToView.left, (int) relativeToView.top,
-                            (int) relativeToView.right, (int) relativeToView.bottom);
-                    parent.addView(view, lp);
-
-                    // requires call to invalidate to update the color
-                    mLockScreenFp.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER,
-                            frameInfo -> new PorterDuffColorFilter(mTextColorPrimary,
-                                    PorterDuff.Mode.SRC_ATOP));
-                    mOnFinishInflateRunnable.run();
-                }
-            };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
deleted file mode 100644
index 76bcd6e..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2020 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.biometrics
-
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.Rect
-import android.graphics.RectF
-import android.util.AttributeSet
-import android.util.Log
-import android.view.MotionEvent
-import android.widget.FrameLayout
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.doze.DozeReceiver
-
-private const val TAG = "UdfpsView"
-
-/**
- * The main view group containing all UDFPS animations.
- */
-class UdfpsView(
-    context: Context,
-    attrs: AttributeSet?
-) : FrameLayout(context, attrs), DozeReceiver {
-    // sensorRect may be bigger than the sensor. True sensor dimensions are defined in
-    // overlayParams.sensorBounds
-    var sensorRect = Rect()
-    private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null
-    private val debugTextPaint = Paint().apply {
-        isAntiAlias = true
-        color = Color.BLUE
-        textSize = 32f
-    }
-
-    /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
-    var animationViewController: UdfpsAnimationViewController<*>? = null
-
-    /** Parameters that affect the position and size of the overlay. */
-    var overlayParams = UdfpsOverlayParams()
-
-    /** Debug message. */
-    var debugMessage: String? = null
-        set(value) {
-            field = value
-            postInvalidate()
-        }
-
-    /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
-    var isDisplayConfigured: Boolean = false
-        private set
-
-    fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) {
-        mUdfpsDisplayMode = udfpsDisplayModeProvider
-    }
-
-    // Don't propagate any touch events to the child views.
-    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
-        return (animationViewController == null || !animationViewController!!.shouldPauseAuth())
-    }
-
-    override fun dozeTimeTick() {
-        animationViewController?.dozeTimeTick()
-    }
-
-    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
-        super.onLayout(changed, left, top, right, bottom)
-
-        // Updates sensor rect in relation to the overlay view
-        animationViewController?.onSensorRectUpdated(RectF(sensorRect))
-    }
-
-    override fun onAttachedToWindow() {
-        super.onAttachedToWindow()
-        Log.v(TAG, "onAttachedToWindow")
-    }
-
-    override fun onDetachedFromWindow() {
-        super.onDetachedFromWindow()
-        Log.v(TAG, "onDetachedFromWindow")
-    }
-
-    override fun onDraw(canvas: Canvas) {
-        super.onDraw(canvas)
-        if (!isDisplayConfigured) {
-            if (!debugMessage.isNullOrEmpty()) {
-                canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
-            }
-        }
-    }
-
-    fun configureDisplay(onDisplayConfigured: Runnable) {
-        isDisplayConfigured = true
-        animationViewController?.onDisplayConfiguring()
-        mUdfpsDisplayMode?.enable(onDisplayConfigured)
-    }
-
-    fun unconfigureDisplay() {
-        isDisplayConfigured = false
-        animationViewController?.onDisplayUnconfigured()
-        mUdfpsDisplayMode?.disable(null /* onDisabled */)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
index 7503a8b..a105d66 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
 import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.lifecycle.repeatWhenAttached
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.launch
@@ -42,14 +41,13 @@
         viewModel: UdfpsTouchOverlayViewModel,
         udfpsOverlayInteractor: UdfpsOverlayInteractor,
     ) {
-        if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) return
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch {
                         viewModel.shouldHandleTouches.collect { shouldHandleTouches ->
                             Log.d(
                                 "UdfpsTouchOverlayBinder",
-                                "[$view]: update shouldHandleTouches=$shouldHandleTouches"
+                                "[$view]: update shouldHandleTouches=$shouldHandleTouches",
                             )
                             view.isInvisible = !shouldHandleTouches
                             udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches)
@@ -58,7 +56,7 @@
                     .invokeOnCompletion {
                         Log.d(
                             "UdfpsTouchOverlayBinder",
-                            "[$view-detached]: update shouldHandleTouches=false"
+                            "[$view-detached]: update shouldHandleTouches=false",
                         )
                         udfpsOverlayInteractor.setHandleTouches(false)
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index f1c3f94..22b2888 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.log.dagger.BouncerTableLog
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
@@ -88,7 +87,6 @@
     val showMessage: StateFlow<BouncerShowMessageModel?>
     val resourceUpdateRequests: StateFlow<Boolean>
     val alternateBouncerVisible: StateFlow<Boolean>
-    val alternateBouncerUIAvailable: StateFlow<Boolean>
 
     /** Last shown security mode of the primary bouncer (ie: pin/pattern/password/SIM) */
     val lastShownSecurityMode: StateFlow<KeyguardSecurityModel.SecurityMode>
@@ -126,8 +124,6 @@
 
     fun setAlternateVisible(isVisible: Boolean)
 
-    fun setAlternateBouncerUIAvailable(isAvailable: Boolean)
-
     fun setLastShownSecurityMode(securityMode: KeyguardSecurityModel.SecurityMode)
 }
 
@@ -199,9 +195,6 @@
     private val _alternateBouncerVisible = MutableStateFlow(false)
     override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
     override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
-    private val _alternateBouncerUIAvailable = MutableStateFlow(false)
-    override val alternateBouncerUIAvailable: StateFlow<Boolean> =
-        _alternateBouncerUIAvailable.asStateFlow()
 
     init {
         setUpLogging()
@@ -220,11 +213,6 @@
         _alternateBouncerVisible.value = isVisible
     }
 
-    override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        _alternateBouncerUIAvailable.value = isAvailable
-    }
-
     override fun setPrimaryShow(isShowing: Boolean) {
         _primaryBouncerShow.value = isShowing
     }
@@ -320,9 +308,6 @@
         resourceUpdateRequests
             .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false)
             .launchIn(applicationScope)
-        alternateBouncerUIAvailable
-            .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false)
-            .launchIn(applicationScope)
         alternateBouncerVisible
             .logDiffsForTable(buffer, "", "AlternateBouncerVisible", false)
             .launchIn(applicationScope)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 0949ea4..9c2a10a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -17,22 +17,17 @@
 package com.android.systemui.bouncer.domain.interactor
 
 import android.util.Log
-import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import com.android.systemui.util.time.SystemClock
 import dagger.Lazy
@@ -55,13 +50,9 @@
 class AlternateBouncerInteractor
 @Inject
 constructor(
-    private val statusBarStateController: StatusBarStateController,
-    private val keyguardStateController: KeyguardStateController,
     private val bouncerRepository: KeyguardBouncerRepository,
     fingerprintPropertyRepository: FingerprintPropertyRepository,
-    private val biometricSettingsRepository: BiometricSettingsRepository,
     private val systemClock: SystemClock,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val deviceEntryBiometricsAllowedInteractor:
         Lazy<DeviceEntryBiometricsAllowedInteractor>,
     private val keyguardInteractor: Lazy<KeyguardInteractor>,
@@ -73,17 +64,10 @@
     val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
     private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet()
     val alternateBouncerSupported: StateFlow<Boolean> =
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            fingerprintPropertyRepository.sensorType
-                .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
-                .stateIn(
-                    scope = scope,
-                    started = SharingStarted.Eagerly,
-                    initialValue = false,
-                )
-        } else {
-            bouncerRepository.alternateBouncerUIAvailable
-        }
+        fingerprintPropertyRepository.sensorType
+            .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
+            .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = false)
+
     private val isDozingOrAod: Flow<Boolean> =
         anyOf(
                 keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map {
@@ -106,7 +90,7 @@
                     combine(
                             keyguardTransitionInteractor.get().currentKeyguardState,
                             sceneInteractor.get().currentScene,
-                            ::Pair
+                            ::Pair,
                         )
                         .flatMapLatest { (currentKeyguardState, transitionState) ->
                             if (currentKeyguardState == KeyguardState.GONE) {
@@ -122,7 +106,7 @@
                                         .isFingerprintAuthCurrentlyAllowed,
                                     keyguardInteractor.get().isKeyguardDismissible,
                                     bouncerRepository.primaryBouncerShow,
-                                    isDozingOrAod
+                                    isDozingOrAod,
                                 ) {
                                     fingerprintAllowed,
                                     keyguardDismissible,
@@ -141,37 +125,17 @@
             }
             .distinctUntilChanged()
             .onEach { Log.d(TAG, "canShowAlternateBouncer changed to $it") }
-            .stateIn(
-                scope = scope,
-                started = WhileSubscribed(),
-                initialValue = false,
-            )
+            .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false)
 
     /**
      * Always shows the alternate bouncer. Requesters must check [canShowAlternateBouncer]` before
      * calling this.
      */
     fun forceShow() {
-        if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
-            show()
-            return
-        }
         bouncerRepository.setAlternateVisible(true)
     }
 
     /**
-     * Sets the correct bouncer states to show the alternate bouncer if it can show.
-     *
-     * @return whether alternateBouncer is visible
-     * @deprecated use [forceShow] and manually check [canShowAlternateBouncer] beforehand
-     */
-    fun show(): Boolean {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        bouncerRepository.setAlternateVisible(canShowAlternateBouncerForFingerprint())
-        return isVisibleState()
-    }
-
-    /**
      * Sets the correct bouncer states to hide the bouncer. Should only be called through
      * StatusBarKeyguardViewManager until ScrimController is refactored to use
      * alternateBouncerInteractor.
@@ -189,28 +153,8 @@
         return bouncerRepository.alternateBouncerVisible.value
     }
 
-    fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        if (isAvailable) {
-            alternateBouncerUiAvailableFromSource.add(token)
-        } else {
-            alternateBouncerUiAvailableFromSource.remove(token)
-        }
-        bouncerRepository.setAlternateBouncerUIAvailable(
-            alternateBouncerUiAvailableFromSource.isNotEmpty()
-        )
-    }
-
     fun canShowAlternateBouncerForFingerprint(): Boolean {
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            return canShowAlternateBouncer.value
-        }
-        return alternateBouncerSupported.value &&
-            biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value &&
-            !keyguardUpdateMonitor.isFingerprintLockedOut &&
-            !keyguardStateController.isUnlocked &&
-            !statusBarStateController.isDozing &&
-            !bouncerRepository.primaryBouncerShow.value
+        return canShowAlternateBouncer.value
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 08a7c39..8510915 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal
 
+import android.os.UserHandle
 import android.provider.Settings
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
@@ -147,9 +148,10 @@
             .emitOnStart()
             .onEach {
                 screenTimeout =
-                    systemSettings.getInt(
+                    systemSettings.getIntForUser(
                         Settings.System.SCREEN_OFF_TIMEOUT,
                         DEFAULT_SCREEN_TIMEOUT,
+                        UserHandle.USER_CURRENT,
                     )
             }
             .launchIn(bgScope)
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index b8c03c0..c464a66 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -17,25 +17,17 @@
 package com.android.systemui.deviceentry
 
 import com.android.keyguard.EmptyLockIconViewController
-import com.android.keyguard.LegacyLockIconViewController
 import com.android.keyguard.LockIconViewController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
 import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import dagger.Lazy
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.Multibinds
 
-@Module(
-    includes =
-        [
-            DeviceEntryRepositoryModule::class,
-            FaceWakeUpTriggersConfigModule::class,
-        ],
-)
+@Module(includes = [DeviceEntryRepositoryModule::class, FaceWakeUpTriggersConfigModule::class])
 abstract class DeviceEntryModule {
     /**
      * A set of DeviceEntryIconTransitions. Ensures that this can be injected even if it's empty.
@@ -46,14 +38,9 @@
         @Provides
         @SysUISingleton
         fun provideLockIconViewController(
-            legacyLockIconViewController: Lazy<LegacyLockIconViewController>,
-            emptyLockIconViewController: Lazy<EmptyLockIconViewController>,
+            emptyLockIconViewController: Lazy<EmptyLockIconViewController>
         ): LockIconViewController {
-            return if (DeviceEntryUdfpsRefactor.isEnabled) {
-                emptyLockIconViewController.get()
-            } else {
-                legacyLockIconViewController.get()
-            }
+            return emptyLockIconViewController.get()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
index ffe392a..88daa5d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
 import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
 import javax.inject.Inject
@@ -55,9 +54,7 @@
         faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
 
     init {
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            startUpdatingFaceHelpMessageDeferral()
-        }
+        startUpdatingFaceHelpMessageDeferral()
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
deleted file mode 100644
index b5d5803..0000000
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.deviceentry.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the device entry udfps refactor flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object DeviceEntryUdfpsRefactor {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
-
-    /** A token used for dependency declaration */
-    val token: FlagToken
-        get() = FlagToken(FLAG_NAME, isEnabled)
-
-    /** Is the refactor enabled */
-    @JvmStatic
-    inline val isEnabled
-        get() = Flags.deviceEntryUdfpsRefactor()
-
-    /**
-     * Called to ensure code is only run when the flag is enabled. This protects users from the
-     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
-     * build to ensure that the refactor author catches issues in testing.
-     */
-    @JvmStatic
-    inline fun isUnexpectedlyInLegacyMode() =
-        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
-    /**
-     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
-     * the flag is enabled to ensure that the refactor author catches issues in testing.
-     */
-    @JvmStatic
-    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index 9626077..03cf1a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -23,15 +23,12 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.BiometricType
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -41,7 +38,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
 
 /**
  * Encapsulates business logic for device entry events that impact the side fingerprint sensor
@@ -51,7 +47,6 @@
 class DeviceEntrySideFpsOverlayInteractor
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     @Application private val context: Context,
     deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
     private val sceneInteractor: SceneInteractor,
@@ -60,18 +55,6 @@
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
 ) {
 
-    init {
-        if (!DeviceEntryUdfpsRefactor.isEnabled) {
-            applicationScope.launch {
-                deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType ->
-                    if (sensorType == BiometricType.SIDE_FINGERPRINT) {
-                        alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
-                    }
-                }
-            }
-        }
-    }
-
     private val isSideFpsIndicatorOnPrimaryBouncerEnabled: Boolean
         get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)
 
@@ -90,7 +73,7 @@
                 primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(),
                 // Bouncer scene visibility changes.
                 isBouncerSceneActive,
-                deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }
+                deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it },
             )
             .map {
                 isBouncerActive() &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 210f4cd..21d1db4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -129,15 +129,19 @@
             constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
             val largeClockTopMargin =
                 SystemBarUtils.getStatusBarHeight(context) +
-                    context.resources.getDimensionPixelSize(
-                        customR.dimen.small_clock_padding_top
-                    ) +
+                    context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
                     context.resources.getDimensionPixelSize(
                         R.dimen.keyguard_smartspace_top_offset
                     ) +
                     getDimen(context, DATE_WEATHER_VIEW_HEIGHT) +
                     getDimen(context, ENHANCED_SMARTSPACE_HEIGHT)
-            connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
+            connect(
+                customR.id.lockscreen_clock_view_large,
+                TOP,
+                PARENT_ID,
+                TOP,
+                largeClockTopMargin,
+            )
             connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
             connect(
                 customR.id.lockscreen_clock_view_large,
@@ -146,12 +150,11 @@
                 ConstraintSet.END,
             )
 
-
             // In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS
             // devices, but we need position of device entry icon to constrain clock
             if (getConstraint(lockId) != null) {
                 connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP)
-           } else {
+            } else {
                 // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
                 val bottomPaddingPx =
                     context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index c3729c0..212da9f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -122,8 +122,11 @@
         final Intent launchingIntent = getIntent();
         mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
                 EXTRA_USER_REVIEW_GRANTED_CONSENT, false);
-
-        mPackageName = getCallingPackage();
+        if (com.android.systemui.Flags.mediaProjectionRequestAttributionFix()) {
+            mPackageName = getLaunchedFromPackage();
+        } else {
+            mPackageName = getCallingPackage();
+        }
 
         // This activity is launched directly by an app, or system server. System server provides
         // the package name through the intent if so.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
new file mode 100644
index 0000000..8dd611f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [HearingDevicesTileModel] to [QSTileState]. */
+class HearingDevicesTileMapper
+@Inject
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+    QSTileDataToStateMapper<HearingDevicesTileModel> {
+
+    override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState =
+        QSTileState.build(resources, theme, config.uiConfig) {
+            label = resources.getString(R.string.quick_settings_hearing_devices_label)
+            iconRes = R.drawable.qs_hearing_devices_icon
+            val loadedIcon =
+                Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+            icon = { loadedIcon }
+            sideViewIcon = QSTileState.SideViewIcon.Chevron
+            contentDescription = label
+            if (data.isAnyActiveHearingDevice) {
+                activationState = QSTileState.ActivationState.ACTIVE
+                secondaryLabel =
+                    resources.getString(R.string.quick_settings_hearing_devices_connected)
+            } else if (data.isAnyPairedHearingDevice) {
+                activationState = QSTileState.ActivationState.INACTIVE
+                secondaryLabel =
+                    resources.getString(R.string.quick_settings_hearing_devices_disconnected)
+            } else {
+                activationState = QSTileState.ActivationState.INACTIVE
+                secondaryLabel = ""
+            }
+            supportedActions =
+                setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
new file mode 100644
index 0000000..ec0a4e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.Flags
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.statusbar.policy.BluetoothController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+
+/** Observes hearing devices state changes providing the [HearingDevicesTileModel]. */
+class HearingDevicesTileDataInteractor
+@Inject
+constructor(
+    @Background private val backgroundContext: CoroutineContext,
+    private val bluetoothController: BluetoothController,
+    private val hearingDevicesChecker: HearingDevicesChecker,
+) : QSTileDataInteractor<HearingDevicesTileModel> {
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>,
+    ): Flow<HearingDevicesTileModel> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : BluetoothController.Callback {
+                        override fun onBluetoothStateChange(enabled: Boolean) {
+                            trySend(getModel())
+                        }
+
+                        override fun onBluetoothDevicesChanged() {
+                            trySend(getModel())
+                        }
+                    }
+                bluetoothController.addCallback(callback)
+                awaitClose { bluetoothController.removeCallback(callback) }
+            }
+            .flowOn(backgroundContext)
+            .distinctUntilChanged()
+
+    override fun availability(user: UserHandle): Flow<Boolean> =
+        flowOf(Flags.hearingAidsQsTileDialog())
+
+    private fun getModel() =
+        HearingDevicesTileModel(
+            hearingDevicesChecker.isAnyActiveHearingDevice,
+            hearingDevicesChecker.isAnyPairedHearingDevice,
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
new file mode 100644
index 0000000..5e7172e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
+import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+/** Handles hearing devices tile clicks. */
+class HearingDevicesTileUserActionInteractor
+@Inject
+constructor(
+    @Main private val mainContext: CoroutineContext,
+    private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+    private val hearingDevicesDialogManager: HearingDevicesDialogManager,
+) : QSTileUserActionInteractor<HearingDevicesTileModel> {
+
+    override suspend fun handleInput(input: QSTileInput<HearingDevicesTileModel>) =
+        with(input) {
+            when (action) {
+                is QSTileUserAction.Click -> {
+                    withContext(mainContext) {
+                        hearingDevicesDialogManager.showDialog(
+                            action.expandable,
+                            LAUNCH_SOURCE_QS_TILE,
+                        )
+                    }
+                }
+                is QSTileUserAction.LongClick -> {
+                    qsTileIntentUserActionHandler.handle(
+                        action.expandable,
+                        Intent(Settings.ACTION_HEARING_DEVICES_SETTINGS),
+                    )
+                }
+                is QSTileUserAction.ToggleClick -> {}
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
new file mode 100644
index 0000000..4e37b77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.model
+
+/** Hearing devices tile model */
+data class HearingDevicesTileModel(
+    val isAnyActiveHearingDevice: Boolean,
+    val isAnyPairedHearingDevice: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index 7b6b0f6..6097ef5 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -20,7 +20,6 @@
 
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 import com.android.systemui.Flags.sceneContainer
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -42,8 +41,7 @@
                 KeyguardWmStateRefactor.isEnabled &&
                 MigrateClocksToBlueprint.isEnabled &&
                 NotificationThrottleHun.isEnabled &&
-                PredictiveBackSysUiFlag.isEnabled &&
-                DeviceEntryUdfpsRefactor.isEnabled
+                PredictiveBackSysUiFlag.isEnabled
 
     // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
 
@@ -58,7 +56,6 @@
             MigrateClocksToBlueprint.token,
             NotificationThrottleHun.token,
             PredictiveBackSysUiFlag.token,
-            DeviceEntryUdfpsRefactor.token,
             // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
index acfcd13..2259b55 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
@@ -313,7 +313,9 @@
         setWindowFocusable(true);
         mViewProxy.requestFocus();
 
-        enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+        if (screenshot.getType() != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+        }
 
         attachWindow();
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index f5c6052..08214c4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -35,6 +35,7 @@
 import android.view.Display
 import android.view.ScrollCaptureResponse
 import android.view.ViewRootImpl.ActivityConfigCallback
+import android.view.WindowManager
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import android.widget.Toast
 import android.window.WindowContext
@@ -217,7 +218,9 @@
         window.setFocusable(true)
         viewProxy.requestFocus()
 
-        enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
+        if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
+        }
 
         window.attachWindow()
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index cae141d..5699557 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -35,7 +35,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.AuthKeyguardMessageArea;
 import com.android.keyguard.KeyguardUnfoldTransition;
-import com.android.keyguard.LockIconViewController;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -44,7 +43,6 @@
 import com.android.systemui.bouncer.ui.binder.BouncerViewBinder;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlagsClassic;
@@ -75,7 +73,6 @@
 import com.android.systemui.statusbar.phone.DozeScrimController;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -101,9 +98,7 @@
     private final NotificationShadeDepthController mDepthController;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    private final LockIconViewController mLockIconViewController;
     private final ShadeLogger mShadeLogger;
-    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final StatusBarWindowStateController mStatusBarWindowStateController;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private final AmbientState mAmbientState;
@@ -174,9 +169,7 @@
             PanelExpansionInteractor panelExpansionInteractor,
             ShadeExpansionStateManager shadeExpansionStateManager,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             StatusBarWindowStateController statusBarWindowStateController,
-            LockIconViewController lockIconViewController,
             CentralSurfaces centralSurfaces,
             DozeServiceHost dozeServiceHost,
             DozeScrimController dozeScrimController,
@@ -210,9 +203,7 @@
         mShadeExpansionStateManager = shadeExpansionStateManager;
         mDepthController = depthController;
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
-        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mStatusBarWindowStateController = statusBarWindowStateController;
-        mLockIconViewController = lockIconViewController;
         mShadeLogger = shadeLogger;
         mService = centralSurfaces;
         mDozeServiceHost = dozeServiceHost;
@@ -259,7 +250,6 @@
                             mDisableSubpixelTextTransitionListener));
         }
 
-        lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view));
         dumpManager.registerDumpable(this);
     }
 
@@ -392,9 +382,6 @@
                 }
 
                 if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
-                    // If the user was sliding their finger across the lock screen,
-                    // we may have been intercepting the touch and forwarding it to the
-                    // UDFPS affordance via mStatusBarKeyguardViewManager.onTouch (see below).
                     // If this touch ended up unlocking the device, we want to cancel the touch
                     // immediately, so we don't cause swipe or expand animations afterwards.
                     cancelCurrentTouch();
@@ -419,9 +406,6 @@
                         && mDreamingWakeupGestureHandler.onTouchEvent(ev)) {
                     return logDownDispatch(ev, "dream wakeup gesture handled", true);
                 }
-                if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) {
-                    return logDownDispatch(ev, "dispatched to Keyguard", true);
-                }
                 if (mBrightnessMirror != null
                         && mBrightnessMirror.getVisibility() == View.VISIBLE) {
                     // Disallow new pointers while the brightness mirror is visible. This is so that
@@ -512,7 +496,6 @@
                 if (mStatusBarStateController.isDozing()
                         && !mDozeServiceHost.isPulsing()
                         && !mDockManager.isDocked()
-                        && !mLockIconViewController.willHandleTouchWhileDozing(ev)
                 ) {
                     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                         mShadeLogger.d("NSWVC: capture all touch events in always-on");
@@ -520,22 +503,8 @@
                     return true;
                 }
 
-                if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) {
-                    // Don't allow touches to proceed to underlying views if alternate
-                    // bouncer is showing
-                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-                        mShadeLogger.d("NSWVC: alt bouncer showing");
-                    }
-                    return true;
-                }
-
-                boolean bouncerShowing;
-                if (DeviceEntryUdfpsRefactor.isEnabled()) {
-                    bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
+                boolean bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
                             || mAlternateBouncerInteractor.isVisibleState();
-                } else {
-                    bouncerShowing = mService.isBouncerShowing();
-                }
                 if (mPanelExpansionInteractor.isFullyExpanded()
                         && !bouncerShowing
                         && !mStatusBarStateController.isDozing()) {
@@ -603,9 +572,6 @@
                 if (mStatusBarStateController.isDozing()) {
                     handled = !mDozeServiceHost.isPulsing();
                 }
-                if (mStatusBarKeyguardViewManager.onTouch(ev)) {
-                    return true;
-                }
                 if (MigrateClocksToBlueprint.isEnabled()) {
                     if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
                         // we still want to finish our drag down gesture when locking the screen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 1481b73..1ec5357 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -15,7 +15,6 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.ExpandHelper
 import com.android.systemui.Gefingerpoken
-import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy
 import com.android.systemui.classifier.Classifier
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
@@ -90,6 +89,7 @@
     @get:VisibleForTesting
     var fractionToShade: Float = 0f
         private set
+
     private var useSplitShade: Boolean = false
     private lateinit var nsslController: NotificationStackScrollLayoutController
     lateinit var centralSurfaces: CentralSurfaces
@@ -161,9 +161,6 @@
     val distanceUntilShowingPulsingNotifications
         get() = fullTransitionDistance
 
-    /** The udfpsKeyguardViewController if it exists. */
-    var mUdfpsKeyguardViewControllerLegacy: UdfpsKeyguardViewControllerLegacy? = null
-
     /** The touch helper responsible for the drag down animation. */
     val touchHelper =
         DragDownHelper(
@@ -171,7 +168,7 @@
             this,
             naturalScrollingSettingObserver,
             shadeRepository,
-            context
+            context,
         )
 
     private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy {
@@ -448,7 +445,6 @@
 
         val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance)
         shadeRepository.setUdfpsTransitionToFullShadeProgress(udfpsProgress)
-        mUdfpsKeyguardViewControllerLegacy?.setTransitionToFullShadeProgress(udfpsProgress)
 
         val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance)
         centralSurfaces.setTransitionToFullShadeProgress(statusBarProgress)
@@ -457,7 +453,7 @@
     private fun setDragDownAmountAnimated(
         target: Float,
         delay: Long = 0,
-        endlistener: (() -> Unit)? = null
+        endlistener: (() -> Unit)? = null,
     ) {
         logger.logDragDownAnimation(target)
         val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target)
@@ -553,7 +549,7 @@
     private fun goToLockedShadeInternal(
         expandView: View?,
         animationHandler: ((Long) -> Unit)? = null,
-        cancelAction: Runnable? = null
+        cancelAction: Runnable? = null,
     ) {
         if (!shadeInteractor.isShadeEnabled.value) {
             cancelAction?.run()
@@ -564,10 +560,7 @@
         var entry: NotificationEntry? = null
         if (expandView is ExpandableNotificationRow) {
             entry = expandView.entry
-            entry.setUserExpanded(
-                /* userExpanded= */ true,
-                /* allowChildExpansion= */ true,
-            )
+            entry.setUserExpanded(/* userExpanded= */ true, /* allowChildExpansion= */ true)
             // Indicate that the group expansion is changing at this time -- this way the group
             // and children backgrounds / divider animations will look correct.
             entry.setGroupExpansionChanging(true)
@@ -594,9 +587,7 @@
                 statusBarStateController.setLeaveOpenOnKeyguardHide(false)
                 draggedDownEntry?.apply {
                     setUserLocked(false)
-                    notifyHeightChanged(
-                        /* needsAnimation= */ false,
-                    )
+                    notifyHeightChanged(/* needsAnimation= */ false)
                     draggedDownEntry = null
                 }
                 cancelAction?.run()
@@ -614,9 +605,7 @@
             // This call needs to be after updating the shade state since otherwise
             // the scrimstate resets too early
             if (animationHandler != null) {
-                animationHandler.invoke(
-                    /* delay= */ 0,
-                )
+                animationHandler.invoke(/* delay= */ 0)
             } else {
                 performDefaultGoToFullShadeAnimation(0)
             }
@@ -757,7 +746,7 @@
     private val dragDownCallback: LockscreenShadeTransitionController,
     private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
     private val shadeRepository: ShadeRepository,
-    context: Context
+    context: Context,
 ) : Gefingerpoken {
 
     private var dragDownAmountOnStart = 0.0f
@@ -932,7 +921,7 @@
     @VisibleForTesting
     fun cancelChildExpansion(
         child: ExpandableView,
-        animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
+        animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS,
     ) {
         if (child.actualHeight == child.collapsedHeight) {
             expandCallback.setUserLockedChild(child, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 32d37ae..c019f30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -22,3 +22,7 @@
 per-file *RemoteInput* = file:notification/OWNERS
 per-file *EmptyShadeView* = set noparent
 per-file *EmptyShadeView* = file:notification/OWNERS
+per-file *Lockscreen* = set noparent
+per-file *Lockscreen* = file:../keyguard/OWNERS
+per-file *Scrim* = set noparent
+per-file *Scrim* = file:../keyguard/OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
index be733d4..8ce0dbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -31,8 +31,8 @@
 abstract class StatusBarChipsModule {
     @Binds
     @IntoMap
-    @ClassKey(DemoRonChipViewModel::class)
-    abstract fun binds(impl: DemoRonChipViewModel): CoreStartable
+    @ClassKey(DemoNotifChipViewModel::class)
+    abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable
 
     companion object {
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
index cce9a16..5fa19dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
 
 import android.content.pm.PackageManager
 import android.content.pm.PackageManager.NameNotFoundException
@@ -22,7 +22,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
@@ -37,25 +37,25 @@
 import kotlinx.coroutines.flow.asStateFlow
 
 /**
- * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on
- * adb commands sent by the user.
+ * A view model that will emit demo promoted ongoing notification chips from [chip] based on adb
+ * commands sent by the user.
  *
  * Example adb commands:
  *
  * To show a chip with the SysUI icon and custom text and color:
  * ```
- * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343"
+ * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343"
  * ```
  *
  * To hide the chip:
  * ```
- * adb shell cmd statusbar demo-ron --hide
+ * adb shell cmd statusbar demo-notif --hide
  * ```
  *
- * See [DemoRonCommand] for more information on the adb command spec.
+ * See [DemoNotifCommand] for more information on the adb command spec.
  */
 @SysUISingleton
-class DemoRonChipViewModel
+class DemoNotifChipViewModel
 @Inject
 constructor(
     private val commandRegistry: CommandRegistry,
@@ -63,19 +63,19 @@
     private val systemClock: SystemClock,
 ) : OngoingActivityChipViewModel, CoreStartable {
     override fun start() {
-        commandRegistry.registerCommand("demo-ron") { DemoRonCommand() }
+        commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() }
     }
 
     private val _chip =
         MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden())
     override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow()
 
-    private inner class DemoRonCommand : ParseableCommand("demo-ron") {
+    private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) {
         private val packageName: String? by
             param(
                 longName = "packageName",
                 shortName = "p",
-                description = "The package name for the demo RON app",
+                description = "The package name for app \"posting\" the demo notification",
                 valueParser = Type.String,
             )
 
@@ -99,15 +99,12 @@
             )
 
         private val hide by
-            flag(
-                longName = "hide",
-                description = "Hides any existing demo RON chip",
-            )
+            flag(longName = "hide", description = "Hides any existing demo notification chip")
 
         override fun execute(pw: PrintWriter) {
-            if (!StatusBarRonChips.isEnabled) {
+            if (!StatusBarNotifChips.isEnabled) {
                 pw.println(
-                    "Error: com.android.systemui.status_bar_ron_chips must be enabled " +
+                    "Error: com.android.systemui.status_bar_notification_chips must be enabled " +
                         "before using this demo feature"
                 )
                 return
@@ -167,8 +164,12 @@
                 return null
             }
             return OngoingActivityChipModel.ChipIcon.FullColorAppIcon(
-                Icon.Loaded(drawable = iconDrawable, contentDescription = null),
+                Icon.Loaded(drawable = iconDrawable, contentDescription = null)
             )
         }
     }
+
+    companion object {
+        private const val DEMO_COMMAND_NAME = "demo-notif"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
index 4ef1909..47ffbaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.shared
+package com.android.systemui.statusbar.chips.notification.shared
 
 import com.android.systemui.Flags
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 
-/** Helper for reading or using the status bar RON chips flag state. */
+/** Helper for reading or using the status bar promoted notification chips flag state. */
 @Suppress("NOTHING_TO_INLINE")
-object StatusBarRonChips {
+object StatusBarNotifChips {
     /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_NOTIFICATION_CHIPS
 
     /** A token used for dependency declaration */
     val token: FlagToken
@@ -33,7 +33,7 @@
     /** Is the refactor enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.statusBarRonChips()
+        get() = Flags.statusBarNotificationChips()
 
     /**
      * Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
new file mode 100644
index 0000000..6ae9263
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.statusbar.chips.notification.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** A view model for status bar chips for promoted ongoing notifications. */
+@SysUISingleton
+class NotifChipsViewModel
+@Inject
+constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) {
+    /**
+     * A flow modeling the notification chips that should be shown. Emits an empty list if there are
+     * no notifications that should show a status bar chip.
+     */
+    val chips: Flow<List<OngoingActivityChipModel.Shown>> =
+        activeNotificationsInteractor.promotedOngoingNotifications.map { notifications ->
+            notifications.mapNotNull { it.toChipModel() }
+        }
+
+    /**
+     * Converts the notification to the [OngoingActivityChipModel] object. Returns null if the
+     * notification has invalid data such that it can't be displayed as a chip.
+     */
+    private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? {
+        // TODO(b/364653005): Log error if there's no icon view.
+        val rawIcon = this.statusBarChipIconView ?: return null
+        val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon)
+        // TODO(b/364653005): Use the notification color if applicable.
+        val colors = ColorsModel.Themed
+        // TODO(b/364653005): When the chip is clicked, show the HUN.
+        val onClickListener = null
+        return OngoingActivityChipModel.Shown.ShortTimeDelta(
+            icon,
+            colors,
+            time = this.whenTime,
+            onClickListener,
+        )
+        // TODO(b/364653005): If Notification.showWhen = false, don't show the time delta.
+        // TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text.
+        // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`.
+        // TODO(b/364653005): If the app that posted the notification is in the foreground, don't
+        // show that app's chip.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index 3b1e565..f4462a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -21,13 +21,14 @@
 import android.graphics.drawable.GradientDrawable
 import android.view.View
 import android.view.ViewGroup
+import android.widget.DateTimeView
 import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.TextView
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
 import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
@@ -42,6 +43,8 @@
         val chipTimeView: ChipChronometer =
             chipView.requireViewById(R.id.ongoing_activity_chip_time)
         val chipTextView: TextView = chipView.requireViewById(R.id.ongoing_activity_chip_text)
+        val chipShortTimeDeltaView: DateTimeView =
+            chipView.requireViewById(R.id.ongoing_activity_chip_short_time_delta)
         val chipBackgroundView: ChipBackgroundContainer =
             chipView.requireViewById(R.id.ongoing_activity_chip_background)
 
@@ -49,13 +52,14 @@
             is OngoingActivityChipModel.Shown -> {
                 // Data
                 setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
-                setChipMainContent(chipModel, chipTextView, chipTimeView)
+                setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView)
                 chipView.setOnClickListener(chipModel.onClickListener)
                 updateChipPadding(
                     chipModel,
                     chipBackgroundView,
                     chipTextView,
                     chipTimeView,
+                    chipShortTimeDeltaView,
                 )
 
                 // Accessibility
@@ -65,6 +69,7 @@
                 val textColor = chipModel.colors.text(chipContext)
                 chipTimeView.setTextColor(textColor)
                 chipTextView.setTextColor(textColor)
+                chipShortTimeDeltaView.setTextColor(textColor)
                 (chipBackgroundView.background as GradientDrawable).color =
                     chipModel.colors.background(chipContext)
             }
@@ -97,7 +102,7 @@
                 defaultIconView.tintView(iconTint)
             }
             is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
-                StatusBarRonChips.assertInNewMode()
+                StatusBarNotifChips.assertInNewMode()
                 IconViewBinder.bind(icon.impl, defaultIconView)
                 defaultIconView.visibility = View.VISIBLE
                 defaultIconView.untintView()
@@ -161,6 +166,7 @@
         chipModel: OngoingActivityChipModel.Shown,
         chipTextView: TextView,
         chipTimeView: ChipChronometer,
+        chipShortTimeDeltaView: DateTimeView,
     ) {
         when (chipModel) {
             is OngoingActivityChipModel.Shown.Countdown -> {
@@ -168,21 +174,35 @@
                 chipTextView.visibility = View.VISIBLE
 
                 chipTimeView.hide()
+                chipShortTimeDeltaView.visibility = View.GONE
             }
             is OngoingActivityChipModel.Shown.Text -> {
                 chipTextView.text = chipModel.text
                 chipTextView.visibility = View.VISIBLE
 
                 chipTimeView.hide()
+                chipShortTimeDeltaView.visibility = View.GONE
             }
             is OngoingActivityChipModel.Shown.Timer -> {
                 ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
                 chipTimeView.visibility = View.VISIBLE
 
                 chipTextView.visibility = View.GONE
+                chipShortTimeDeltaView.visibility = View.GONE
+            }
+            is OngoingActivityChipModel.Shown.ShortTimeDelta -> {
+                chipShortTimeDeltaView.setTime(chipModel.time)
+                // TODO(b/364653005): DateTimeView's relative time doesn't quite match the format we
+                //  want in the status bar chips.
+                chipShortTimeDeltaView.isShowRelativeTime = true
+                chipShortTimeDeltaView.visibility = View.VISIBLE
+
+                chipTextView.visibility = View.GONE
+                chipTimeView.hide()
             }
             is OngoingActivityChipModel.Shown.IconOnly -> {
                 chipTextView.visibility = View.GONE
+                chipShortTimeDeltaView.visibility = View.GONE
                 chipTimeView.hide()
             }
         }
@@ -200,6 +220,7 @@
         backgroundView: View,
         chipTextView: TextView,
         chipTimeView: ChipChronometer,
+        chipShortTimeDeltaView: DateTimeView,
     ) {
         if (chipModel.icon != null) {
             if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
@@ -209,15 +230,18 @@
                 backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon()
                 chipTextView.setTextPaddingForEmbeddedPaddingIcon()
                 chipTimeView.setTextPaddingForEmbeddedPaddingIcon()
+                chipShortTimeDeltaView.setTextPaddingForEmbeddedPaddingIcon()
             } else {
                 backgroundView.setBackgroundPaddingForNormalIcon()
                 chipTextView.setTextPaddingForNormalIcon()
                 chipTimeView.setTextPaddingForNormalIcon()
+                chipShortTimeDeltaView.setTextPaddingForNormalIcon()
             }
         } else {
             backgroundView.setBackgroundPaddingForNoIcon()
             chipTextView.setTextPaddingForNoIcon()
             chipTimeView.setTextPaddingForNoIcon()
+            chipShortTimeDeltaView.setTextPaddingForNoIcon()
         }
     }
 
@@ -258,23 +282,13 @@
             context.resources.getDimensionPixelSize(
                 R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
             )
-        setPaddingRelative(
-            sidePadding,
-            paddingTop,
-            sidePadding,
-            paddingBottom,
-        )
+        setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
     }
 
     private fun View.setBackgroundPaddingForNormalIcon() {
         val sidePadding =
             context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding)
-        setPaddingRelative(
-            sidePadding,
-            paddingTop,
-            sidePadding,
-            paddingBottom,
-        )
+        setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
     }
 
     private fun View.setBackgroundPaddingForNoIcon() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 62622a8..cf07af1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.Flags
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 
 /** Model representing the display of an ongoing activity as a chip in the status bar. */
 sealed class OngoingActivityChipModel {
@@ -79,6 +80,24 @@
         }
 
         /**
+         * The chip shows the time delta between now and [time] in a short format, e.g. "15min" or
+         * "1hr ago".
+         */
+        data class ShortTimeDelta(
+            override val icon: ChipIcon,
+            override val colors: ColorsModel,
+            /** The time of the event that this chip represents. */
+            val time: Long,
+            override val onClickListener: View.OnClickListener?,
+        ) : Shown(icon, colors, onClickListener) {
+            init {
+                StatusBarNotifChips.assertInNewMode()
+            }
+
+            override val logName = "Shown.ShortTimeDelta"
+        }
+
+        /**
          * This chip shows a countdown using [secondsUntilStarted]. Used to inform users that an
          * event is about to start. Typically, a [Countdown] chip will turn into a [Timer] chip.
          */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 24c1b87..ed32597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
-import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
@@ -24,19 +23,20 @@
 import com.android.systemui.statusbar.chips.StatusBarChipsLog
 import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
 import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel
 import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.util.kotlin.combine
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -55,7 +55,8 @@
     shareToAppChipViewModel: ShareToAppChipViewModel,
     castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
     callChipViewModel: CallChipViewModel,
-    demoRonChipViewModel: DemoRonChipViewModel,
+    notifChipsViewModel: NotifChipsViewModel,
+    demoNotifChipViewModel: DemoNotifChipViewModel,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) {
     private enum class ChipType {
@@ -63,8 +64,9 @@
         ShareToApp,
         CastToOtherDevice,
         Call,
-        /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */
-        DemoRon,
+        Notification,
+        /** A demo of a notification chip, used just for testing. */
+        DemoNotification,
     }
 
     /** Model that helps us internally track the various chip states from each of the types. */
@@ -85,7 +87,8 @@
             val shareToApp: OngoingActivityChipModel.Hidden,
             val castToOtherDevice: OngoingActivityChipModel.Hidden,
             val call: OngoingActivityChipModel.Hidden,
-            val demoRon: OngoingActivityChipModel.Hidden,
+            val notifs: OngoingActivityChipModel.Hidden,
+            val demoNotif: OngoingActivityChipModel.Hidden,
         ) : InternalChipModel
     }
 
@@ -94,7 +97,8 @@
         val shareToApp: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
         val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
         val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
-        val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+        val notifs: List<OngoingActivityChipModel.Shown> = emptyList(),
+        val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
     )
 
     /** Bundles all the incoming chips into one object to easily pass to various flows. */
@@ -104,8 +108,9 @@
                 shareToAppChipViewModel.chip,
                 castToOtherDeviceChipViewModel.chip,
                 callChipViewModel.chip,
-                demoRonChipViewModel.chip,
-            ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon ->
+                notifChipsViewModel.chips,
+                demoNotifChipViewModel.chip,
+            ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif ->
                 logger.log(
                     TAG,
                     LogLevel.INFO,
@@ -121,16 +126,19 @@
                     LogLevel.INFO,
                     {
                         str1 = call.logName
-                        str2 = demoRon.logName
+                        // TODO(b/364653005): Log other information for notification chips.
+                        str2 = notifs.map { it.logName }.toString()
+                        str3 = demoNotif.logName
                     },
-                    { "... > Call=$str1 > DemoRon=$str2" }
+                    { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" },
                 )
                 ChipBundle(
                     screenRecord = screenRecord,
                     shareToApp = shareToApp,
                     castToOtherDevice = castToOtherDevice,
                     call = call,
-                    demoRon = demoRon,
+                    notifs = notifs,
+                    demoNotif = demoNotif,
                 )
             }
             // Some of the chips could have timers in them and we don't want the start time
@@ -189,9 +197,9 @@
      * actually displaying the chip.
      */
     val chips: StateFlow<MultipleOngoingActivityChipsModel> =
-        if (!Flags.statusBarRonChips()) {
-            // Multiple chips are only allowed with RONs. If the flag isn't on, use just the
-            // primary chip.
+        if (!StatusBarNotifChips.isEnabled) {
+            // Multiple chips are only allowed with notification chips. If the flag isn't on, use
+            // just the primary chip.
             primaryChip
                 .map {
                     MultipleOngoingActivityChipsModel(
@@ -199,11 +207,7 @@
                         secondary = OngoingActivityChipModel.Hidden(),
                     )
                 }
-                .stateIn(
-                    scope,
-                    SharingStarted.Lazily,
-                    MultipleOngoingActivityChipsModel(),
-                )
+                .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel())
         } else {
             internalChips
                 .pairwise(initialValue = DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL)
@@ -212,11 +216,7 @@
                     val correctSecondary = createOutputModel(old.secondary, new.secondary)
                     MultipleOngoingActivityChipsModel(correctPrimary, correctSecondary)
                 }
-                .stateIn(
-                    scope,
-                    SharingStarted.Lazily,
-                    MultipleOngoingActivityChipsModel(),
-                )
+                .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel())
         }
 
     /** A data class representing the return result of [pickMostImportantChip]. */
@@ -251,7 +251,7 @@
                             // suppress the share-to-app chip to make sure they don't both show.
                             // See b/296461748.
                             shareToApp = OngoingActivityChipModel.Hidden(),
-                        )
+                        ),
                 )
             bundle.shareToApp is OngoingActivityChipModel.Shown ->
                 MostImportantChipResult(
@@ -274,11 +274,19 @@
                     mostImportantChip = InternalChipModel.Shown(ChipType.Call, bundle.call),
                     remainingChips = bundle.copy(call = OngoingActivityChipModel.Hidden()),
                 )
-            bundle.demoRon is OngoingActivityChipModel.Shown -> {
-                StatusBarRonChips.assertInNewMode()
+            bundle.notifs.isNotEmpty() ->
                 MostImportantChipResult(
-                    mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon),
-                    remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()),
+                    mostImportantChip =
+                        InternalChipModel.Shown(ChipType.Notification, bundle.notifs.first()),
+                    remainingChips =
+                        bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)),
+                )
+            bundle.demoNotif is OngoingActivityChipModel.Shown -> {
+                StatusBarNotifChips.assertInNewMode()
+                MostImportantChipResult(
+                    mostImportantChip =
+                        InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif),
+                    remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()),
                 )
             }
             else -> {
@@ -287,7 +295,8 @@
                 check(bundle.shareToApp is OngoingActivityChipModel.Hidden)
                 check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden)
                 check(bundle.call is OngoingActivityChipModel.Hidden)
-                check(bundle.demoRon is OngoingActivityChipModel.Hidden)
+                check(bundle.notifs.isEmpty())
+                check(bundle.demoNotif is OngoingActivityChipModel.Hidden)
                 MostImportantChipResult(
                     mostImportantChip =
                         InternalChipModel.Hidden(
@@ -295,7 +304,8 @@
                             shareToApp = bundle.shareToApp,
                             castToOtherDevice = bundle.castToOtherDevice,
                             call = bundle.call,
-                            demoRon = bundle.demoRon,
+                            notifs = OngoingActivityChipModel.Hidden(),
+                            demoNotif = bundle.demoNotif,
                         ),
                     // All the chips are already hidden, so no need to filter anything out of the
                     // bundle.
@@ -323,7 +333,8 @@
                 ChipType.ShareToApp -> new.shareToApp
                 ChipType.CastToOtherDevice -> new.castToOtherDevice
                 ChipType.Call -> new.call
-                ChipType.DemoRon -> new.demoRon
+                ChipType.Notification -> new.notifs
+                ChipType.DemoNotification -> new.demoNotif
             }
         } else if (new is InternalChipModel.Shown) {
             // If we have a chip to show, always show it.
@@ -344,7 +355,8 @@
                 shareToApp = OngoingActivityChipModel.Hidden(),
                 castToOtherDevice = OngoingActivityChipModel.Hidden(),
                 call = OngoingActivityChipModel.Hidden(),
-                demoRon = OngoingActivityChipModel.Hidden(),
+                notifs = OngoingActivityChipModel.Hidden(),
+                demoNotif = OngoingActivityChipModel.Hidden(),
             )
 
         private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
new file mode 100644
index 0000000..b64a577
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.statusbar.core
+
+import android.view.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayScopeRepository
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.util.kotlin.pairwiseBy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
+
+/**
+ * Responsible for creating and starting the status bar components for each display. Also does it
+ * for newly added displays.
+ */
+@SysUISingleton
+class MultiDisplayStatusBarStarter
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val displayScopeRepository: DisplayScopeRepository,
+    private val statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
+    private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+    private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+    private val displayRepository: DisplayRepository,
+    private val initializerStore: StatusBarInitializerStore,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+    private val statusBarInitializerStore: StatusBarInitializerStore,
+) : CoreStartable {
+
+    init {
+        StatusBarConnectedDisplays.assertInNewMode()
+    }
+
+    override fun start() {
+        applicationScope.launch {
+            displayRepository.displays
+                .pairwiseBy { previousDisplays, currentDisplays ->
+                    currentDisplays - previousDisplays
+                }
+                .onStart { emit(displayRepository.displays.value) }
+                .collect { newDisplays ->
+                    newDisplays.forEach { createAndStartComponentsForDisplay(it) }
+                }
+        }
+    }
+
+    private fun createAndStartComponentsForDisplay(display: Display) {
+        val displayId = display.displayId
+        createAndStartOrchestratorForDisplay(displayId)
+        createAndStartInitializerForDisplay(displayId)
+    }
+
+    private fun createAndStartOrchestratorForDisplay(displayId: Int) {
+        statusBarOrchestratorFactory
+            .create(
+                displayId,
+                displayScopeRepository.scopeForDisplay(displayId),
+                statusBarWindowStateRepositoryStore.forDisplay(displayId),
+                statusBarModeRepositoryStore.forDisplay(displayId),
+                initializerStore.forDisplay(displayId),
+                statusBarWindowControllerStore.forDisplay(displayId),
+            )
+            .start()
+    }
+
+    private fun createAndStartInitializerForDisplay(displayId: Int) {
+        statusBarInitializerStore.forDisplay(displayId).start()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 7eff812..2c94632 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
-import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.StatusBarWindowController
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -37,7 +37,7 @@
  * Responsible for creating the status bar window and initializing the root components of that
  * window (see [CollapsedStatusBarFragment])
  */
-interface StatusBarInitializer {
+interface StatusBarInitializer : CoreStartable {
 
     var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener?
 
@@ -68,18 +68,17 @@
     }
 
     interface Factory {
-        fun create(displayId: Int): StatusBarInitializer
+        fun create(statusBarWindowController: StatusBarWindowController): StatusBarInitializer
     }
 }
 
 class StatusBarInitializerImpl
 @AssistedInject
 constructor(
-    @Assisted private val displayId: Int,
-    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+    @Assisted private val statusBarWindowController: StatusBarWindowController,
     private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
     private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
-) : CoreStartable, StatusBarInitializer {
+) : StatusBarInitializer {
     private var component: StatusBarFragmentComponent? = null
 
     @get:VisibleForTesting
@@ -111,7 +110,7 @@
 
     private fun doStart() {
         initialized = true
-        statusBarWindowControllerStore.defaultDisplay.fragmentHostManager
+        statusBarWindowController.fragmentHostManager
             .addTagListener(
                 CollapsedStatusBarFragment.TAG,
                 object : FragmentHostManager.FragmentListener {
@@ -145,6 +144,8 @@
 
     @AssistedFactory
     interface Factory : StatusBarInitializer.Factory {
-        override fun create(displayId: Int): StatusBarInitializerImpl
+        override fun create(
+            statusBarWindowController: StatusBarWindowController
+        ): StatusBarInitializerImpl
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
index 8d044bb..6c38026 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import java.util.concurrent.ConcurrentHashMap
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineName
@@ -53,6 +54,7 @@
     @Background private val backgroundApplicationScope: CoroutineScope,
     private val factory: StatusBarInitializer.Factory,
     private val displayRepository: DisplayRepository,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
 ) : StatusBarInitializerStore, CoreStartable {
 
     init {
@@ -68,7 +70,11 @@
         if (displayRepository.getDisplay(displayId) == null) {
             throw IllegalArgumentException("Display with id $displayId doesn't exist.")
         }
-        return perDisplayInitializers.computeIfAbsent(displayId) { factory.create(displayId) }
+        return perDisplayInitializers.computeIfAbsent(displayId) {
+            factory.create(
+                statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId)
+            )
+        }
     }
 
     override fun start() {
@@ -85,15 +91,13 @@
 @SysUISingleton
 class SingleDisplayStatusBarInitializerStore
 @Inject
-constructor(factory: StatusBarInitializerImpl.Factory) : StatusBarInitializerStore {
+constructor(private val defaultInstance: StatusBarInitializer) : StatusBarInitializerStore {
 
     init {
         StatusBarConnectedDisplays.assertInLegacyMode()
     }
 
-    private val defaultInstance = factory.create(Display.DEFAULT_DISPLAY)
-
     override val defaultDisplay: StatusBarInitializer = defaultInstance
 
-    override fun forDisplay(displayId: Int): StatusBarInitializer = defaultDisplay
+    override fun forDisplay(displayId: Int): StatusBarInitializer = defaultInstance
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index d372eb2..47e6c57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -16,12 +16,12 @@
 
 package com.android.systemui.statusbar.core
 
+import android.view.Display
 import android.view.View
 import com.android.systemui.CoreStartable
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.plugins.PluginDependencyProvider
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -31,19 +31,21 @@
 import com.android.systemui.statusbar.AutoHideUiElement
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
 import com.android.systemui.statusbar.phone.AutoHideController
 import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
-import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
-import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
 import com.android.wm.shell.bubbles.Bubbles
 import dagger.Lazy
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import java.io.PrintWriter
 import java.util.Optional
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
@@ -57,27 +59,35 @@
  * It is a temporary class, created to pull status bar related logic out of CentralSurfacesImpl. The
  * plan is break it out into individual classes.
  */
-@SysUISingleton
 class StatusBarOrchestrator
-@Inject
+@AssistedInject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
-    private val statusBarInitializer: StatusBarInitializer,
-    private val statusBarModeRepository: StatusBarModeRepositoryStore,
-    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+    @Assisted private val displayId: Int,
+    @Assisted private val coroutineScope: CoroutineScope,
+    @Assisted private val statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+    @Assisted private val statusBarModeRepository: StatusBarModePerDisplayRepository,
+    @Assisted private val statusBarInitializer: StatusBarInitializer,
+    @Assisted private val statusBarWindowController: StatusBarWindowController,
     private val demoModeController: DemoModeController,
     private val pluginDependencyProvider: PluginDependencyProvider,
     private val autoHideController: AutoHideController,
     private val remoteInputManager: NotificationRemoteInputManager,
     private val notificationShadeWindowViewControllerLazy:
-    Lazy<NotificationShadeWindowViewController>,
+        Lazy<NotificationShadeWindowViewController>,
     private val shadeSurface: ShadeSurface,
     private val bubblesOptional: Optional<Bubbles>,
-    private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+    private val dumpManager: DumpManager,
     powerInteractor: PowerInteractor,
     primaryBouncerInteractor: PrimaryBouncerInteractor,
 ) : CoreStartable {
 
+    private val dumpableName: String =
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            javaClass.simpleName
+        } else {
+            "${javaClass.simpleName}$displayId"
+        }
+
     private val phoneStatusBarViewController =
         MutableStateFlow<PhoneStatusBarViewController?>(value = null)
 
@@ -86,9 +96,9 @@
 
     private val shouldAnimateNextBarModeChange =
         combine(
-            statusBarModeRepository.defaultDisplay.isTransientShown,
+            statusBarModeRepository.isTransientShown,
             powerInteractor.isAwake,
-            statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+            statusBarWindowStateRepository.windowState,
         ) { isTransientShown, isDeviceAwake, statusBarWindowState ->
             !isTransientShown &&
                 isDeviceAwake &&
@@ -107,8 +117,8 @@
 
     private val statusBarVisible =
         combine(
-            statusBarModeRepository.defaultDisplay.statusBarMode,
-            statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+            statusBarModeRepository.statusBarMode,
+            statusBarWindowStateRepository.windowState,
         ) { mode, statusBarWindowState ->
             mode != StatusBarMode.LIGHTS_OUT &&
                 mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT &&
@@ -119,7 +129,7 @@
         combine(
                 shouldAnimateNextBarModeChange,
                 phoneStatusBarTransitions.filterNotNull(),
-                statusBarModeRepository.defaultDisplay.statusBarMode,
+                statusBarModeRepository.statusBarMode,
                 ::Triple,
             )
             .distinctUntilChangedBy { (_, barTransitions, statusBarMode) ->
@@ -130,26 +140,29 @@
 
     override fun start() {
         StatusBarSimpleFragment.assertInNewMode()
-        applicationScope.launch {
-            launch {
-                controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
-                    setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+        coroutineScope
+            .launch {
+                dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator)
+                launch {
+                    controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
+                        setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+                    }
                 }
-            }
-            launch {
-                barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
-                    if (deviceAsleep) {
-                        barTransitions.finishAnimations()
+                launch {
+                    barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
+                        if (deviceAsleep) {
+                            barTransitions.finishAnimations()
+                        }
+                    }
+                }
+                launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
+                launch {
+                    barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
+                        updateBarMode(animate, barTransitions, statusBarMode)
                     }
                 }
             }
-            launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
-            launch {
-                barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
-                    updateBarMode(animate, barTransitions, statusBarMode)
-                }
-            }
-        }
+            .invokeOnCompletion { dumpManager.unregisterDumpable(dumpableName) }
         createAndAddWindow()
         setupPluginDependencies()
         setUpAutoHide()
@@ -157,7 +170,7 @@
 
     private fun createAndAddWindow() {
         initializeStatusBarFragment()
-        statusBarWindowControllerStore.defaultDisplay.attach()
+        statusBarWindowController.attach()
     }
 
     private fun initializeStatusBarFragment() {
@@ -170,6 +183,10 @@
                     phoneStatusBarViewController.value = statusBarViewController
                     phoneStatusBarTransitions.value = statusBarTransitions
 
+                    if (displayId != Display.DEFAULT_DISPLAY) {
+                        return
+                    }
+                    // TODO(b/373310629): shade should be display id aware
                     notificationShadeWindowViewControllerLazy
                         .get()
                         .setStatusBarViewController(statusBarViewController)
@@ -189,6 +206,10 @@
     }
 
     private fun setUpAutoHide() {
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            return
+        }
+        // TODO(b/373309973): per display implementation of auto hide controller
         autoHideController.setStatusBar(
             object : AutoHideUiElement {
                 override fun synchronizeState() {}
@@ -198,13 +219,14 @@
                 }
 
                 override fun isVisible(): Boolean {
-                    return statusBarModeRepository.defaultDisplay.isTransientShown.value
+                    return statusBarModeRepository.isTransientShown.value
                 }
 
                 override fun hide() {
-                    statusBarModeRepository.defaultDisplay.clearTransient()
+                    statusBarModeRepository.clearTransient()
                 }
-            })
+            }
+        )
     }
 
     private fun updateBarMode(
@@ -215,11 +237,18 @@
         if (!demoModeController.isInDemoMode) {
             barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
         }
-        autoHideController.touchAutoHide()
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            // TODO(b/373309973): per display implementation of auto hide controller
+            autoHideController.touchAutoHide()
+        }
     }
 
     private fun updateBubblesVisibility(statusBarVisible: Boolean) {
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            return
+        }
         bubblesOptional.ifPresent { bubbles: Bubbles ->
+            // TODO(b/373311537): per display implementation of Bubbles
             bubbles.onStatusBarVisibilityChanged(statusBarVisible)
         }
     }
@@ -238,11 +267,23 @@
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value)
+        pw.println(statusBarWindowStateRepository.windowState.value)
         CentralSurfaces.dumpBarTransitions(
             pw,
             "PhoneStatusBarTransitions",
             phoneStatusBarTransitions.value,
         )
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            displayId: Int,
+            displayScope: CoroutineScope,
+            statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+            statusBarModeRepository: StatusBarModePerDisplayRepository,
+            statusBarInitializer: StatusBarInitializer,
+            statusBarWindowController: StatusBarWindowController,
+        ): StatusBarOrchestrator
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
index 962cb095..154be1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
@@ -31,6 +31,7 @@
 
 interface StatusBarModeRepositoryStore {
     val defaultDisplay: StatusBarModePerDisplayRepository
+
     fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository
 }
 
@@ -39,7 +40,7 @@
 @Inject
 constructor(
     @DisplayId private val displayId: Int,
-    factory: StatusBarModePerDisplayRepositoryFactory
+    factory: StatusBarModePerDisplayRepositoryFactory,
 ) :
     StatusBarModeRepositoryStore,
     CoreStartable,
@@ -47,11 +48,9 @@
     override val defaultDisplay = factory.create(displayId)
 
     override fun forDisplay(displayId: Int) =
-        if (this.displayId == displayId) {
-            defaultDisplay
-        } else {
-            TODO("b/127878649 implement multi-display state management")
-        }
+        // TODO(b/369337087): implement per display status bar modes.
+        //  For now just use default display instance.
+        defaultDisplay
 
     override fun start() {
         defaultDisplay.start()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
index 0927a72..a614638 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
@@ -115,23 +115,23 @@
                 final float h = y - mInitialTouchY;
                 if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
                         && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
-                    setTrackingHeadsUp(true);
-                    mCollapseSnoozes = h < 0;
-                    mInitialTouchX = x;
-                    mInitialTouchY = y;
-                    int startHeight = (int) (mPickedChild.getActualHeight()
-                                                + mPickedChild.getTranslationY());
-                    mPanel.setHeadsUpDraggingStartingHeight(startHeight);
-                    mPanel.startExpand(x, y, true /* startTracking */, startHeight);
-
                     if (!SceneContainerFlag.isEnabled()) {
+                        setTrackingHeadsUp(true);
+                        mCollapseSnoozes = h < 0;
+                        mInitialTouchX = x;
+                        mInitialTouchY = y;
+                        int startHeight = (int) (mPickedChild.getActualHeight()
+                                + mPickedChild.getTranslationY());
+                        mPanel.setHeadsUpDraggingStartingHeight(startHeight);
+                        mPanel.startExpand(x, y, true /* startTracking */, startHeight);
+
                         // This call needs to be after the expansion start otherwise we will get a
                         // flicker of one frame as it's not expanded yet.
                         mHeadsUpManager.unpinAll(true);
-                    }
 
-                    clearNotificationEffects();
-                    endMotion();
+                        clearNotificationEffects();
+                        endMotion();
+                    }
                     return true;
                 }
                 break;
@@ -167,17 +167,70 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (!mTrackingHeadsUp) {
+        if (SceneContainerFlag.isEnabled()) {
+            int pointerIndex = event.findPointerIndex(mTrackingPointer);
+            if (pointerIndex < 0) {
+                pointerIndex = 0;
+                mTrackingPointer = event.getPointerId(pointerIndex);
+            }
+            final float x = event.getX(pointerIndex);
+            final float y = event.getY(pointerIndex);
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_POINTER_UP:
+                    final int upPointer = event.getPointerId(event.getActionIndex());
+                    if (mTrackingPointer == upPointer) {
+                        // gesture is ongoing, find a new pointer to track
+                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                        mTrackingPointer = event.getPointerId(newIndex);
+                        mInitialTouchX = event.getX(newIndex);
+                        mInitialTouchY = event.getY(newIndex);
+                    }
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    final float h = y - mInitialTouchY;
+                    if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
+                            && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+                        setTrackingHeadsUp(true);
+                        mCollapseSnoozes = h < 0;
+                        mInitialTouchX = x;
+                        mInitialTouchY = y;
+                        int startHeight = (int) (mPickedChild.getActualHeight()
+                                + mPickedChild.getTranslationY());
+                        mPanel.setHeadsUpDraggingStartingHeight(startHeight);
+                        mPanel.startExpand(x, y, true /* startTracking */, startHeight);
+
+                        clearNotificationEffects();
+                        endMotion();
+                        return true;
+                    }
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_UP:
+                    if (mPickedChild != null && mTouchingHeadsUpView) {
+                        // We may swallow this click if the heads up just came in.
+                        if (mHeadsUpManager.shouldSwallowClick(
+                                mPickedChild.getEntry().getSbn().getKey())) {
+                            endMotion();
+                            return true;
+                        }
+                    }
+                    endMotion();
+                    return false;
+            }
             return false;
+        } else {
+            if (!mTrackingHeadsUp) {
+                return false;
+            }
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    endMotion();
+                    setTrackingHeadsUp(false);
+                    break;
+            }
+            return true;
         }
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                endMotion();
-                setTrackingHeadsUp(false);
-                break;
-        }
-        return true;
     }
 
     private void endMotion() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 9b382e6..697a6ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -17,6 +17,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
@@ -26,6 +27,7 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
@@ -74,6 +76,17 @@
     val allNotificationsCountValue: Int
         get() = repository.activeNotifications.value.individuals.size
 
+    /** The notifications that are promoted and ongoing. Sorted by priority order. */
+    val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> =
+        if (StatusBarNotifChips.isEnabled) {
+            // TODO(b/364653005): Filter all the notifications down to just the promoted ones.
+            // TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow
+            // instead of being separate.
+            topLevelRepresentativeNotifications
+        } else {
+            flowOf(emptyList())
+        }
+
     /**
      * The priority ongoing call notification, or null if there is no ongoing call.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 4c7d90c..d538f52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -449,7 +449,7 @@
                 }
 
                 // We need to reset the View state, even if the animation was cancelled
-                onAppearAnimationFinished(isAppearing);
+                onAppearAnimationFinished(isAppearing, /* cancelled = */ !mRunWithoutInterruptions);
 
                 if (mRunWithoutInterruptions) {
                     InteractionJankMonitor.getInstance().end(getCujType(isAppearing));
@@ -463,6 +463,7 @@
                 if (onStartedRunnable != null) {
                     onStartedRunnable.run();
                 }
+                onAppearAnimationStarted(isAppearing);
                 mRunWithoutInterruptions = true;
                 Configuration.Builder builder = Configuration.Builder
                         .withView(getCujType(isAppearing), ActivatableNotificationView.this);
@@ -486,6 +487,8 @@
                 frameTimeNanos -> {
                     if (mAppearAnimator == cachedAnimator) {
                         mAppearAnimator.start();
+                    } else {
+                        onAppearAnimationSkipped(isAppearing);
                     }
                 }, delay);
     }
@@ -502,7 +505,13 @@
         }
     }
 
-    protected void onAppearAnimationFinished(boolean wasAppearing) {
+    protected void onAppearAnimationStarted(boolean isAppear) {
+    }
+
+    protected void onAppearAnimationSkipped(boolean isAppear) {
+    }
+
+    protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) {
     }
 
     private void cancelAppearAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 33dbbc2..38e6609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1835,6 +1835,25 @@
         void logSkipResetAllContentAlphas(
                 NotificationEntry entry
         );
+
+        /** Called when we start an appear animation. */
+        void logStartAppearAnimation(NotificationEntry entry, boolean isAppear);
+
+        /** Called when we cancel the running appear animation. */
+        void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing);
+
+        /** Called when the animator of the appear animation is started. */
+        void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear);
+
+        /** Called when we prepared an appear animation, but the animator was never started. */
+        void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear);
+
+        /** Called when the animator of the appear animation is finished. */
+        void logAppearAnimationFinished(
+                NotificationEntry entry,
+                boolean isAppear,
+                boolean cancelled
+        );
     }
 
     /**
@@ -3165,6 +3184,13 @@
     }
 
     @Override
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
+            Runnable onFinishRunnable) {
+        mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ true);
+        super.performAddAnimation(delay, duration, isHeadsUpAppear, onFinishRunnable);
+    }
+
+    @Override
     public long performRemoveAnimation(
             long duration,
             long delay,
@@ -3173,6 +3199,7 @@
             Runnable onStartedRunnable,
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener, ClipSide clipSide) {
+        mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ false);
         if (mMenuRow != null && mMenuRow.isMenuVisible()) {
             Animator anim = getTranslateViewAnimator(0f, null /* listener */);
             if (anim != null) {
@@ -3201,8 +3228,25 @@
     }
 
     @Override
-    protected void onAppearAnimationFinished(boolean wasAppearing) {
-        super.onAppearAnimationFinished(wasAppearing);
+    protected void onAppearAnimationStarted(boolean isAppear) {
+        mLogger.logAppearAnimationStarted(getEntry(), /* isAppear = */ isAppear);
+        super.onAppearAnimationStarted(isAppear);
+    }
+
+    @Override
+    protected void onAppearAnimationSkipped(boolean isAppear) {
+        mLogger.logAppearAnimationSkipped(getEntry(),  /* isAppear = */ isAppear);
+        super.onAppearAnimationSkipped(isAppear);
+    }
+
+    @Override
+    protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) {
+        mLogger.logAppearAnimationFinished(
+                /* entry = */ getEntry(),
+                /* isAppear = */ wasAppearing,
+                /* cancelled = */ cancelled
+        );
+        super.onAppearAnimationFinished(wasAppearing, cancelled);
         if (wasAppearing) {
             // During the animation the visible view might have changed, so let's make sure all
             // alphas are reset
@@ -3218,6 +3262,12 @@
     }
 
     @Override
+    public void cancelAppearDrawing() {
+        mLogger.logCancelAppearDrawing(getEntry(), isDrawingAppearAnimation());
+        super.cancelAppearDrawing();
+    }
+
+    @Override
     public void resetAllContentAlphas() {
         mLogger.logResetAllContentAlphas(getEntry());
         mPrivateLayout.setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index c31a2cb..23a2fac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -209,6 +209,32 @@
                 ) {
                     mLogBufferLogger.logSkipResetAllContentAlphas(entry);
                 }
+
+                @Override
+                public void logStartAppearAnimation(NotificationEntry entry, boolean isAppear) {
+                    mLogBufferLogger.logStartAppearAnimation(entry, isAppear);
+                }
+
+                @Override
+                public void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing) {
+                    mLogBufferLogger.logCancelAppearDrawing(entry, wasDrawing);
+                }
+
+                @Override
+                public void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear) {
+                    mLogBufferLogger.logAppearAnimationStarted(entry, isAppear);
+                }
+
+                @Override
+                public void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear) {
+                    mLogBufferLogger.logAppearAnimationSkipped(entry, isAppear);
+                }
+
+                @Override
+                public void logAppearAnimationFinished(NotificationEntry entry, boolean isAppear,
+                        boolean cancelled) {
+                    mLogBufferLogger.logAppearAnimationFinished(entry, isAppear, cancelled);
+                }
             };
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index b1e9032..6e78f56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -30,7 +30,7 @@
 @Inject
 constructor(
     @NotificationLog private val buffer: LogBuffer,
-    @NotificationRenderLog private val notificationRenderBuffer: LogBuffer
+    @NotificationRenderLog private val notificationRenderBuffer: LogBuffer,
 ) {
     fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) {
         buffer.log(
@@ -40,7 +40,7 @@
                 str1 = child.logKey
                 str2 = oldParent.logKey
             },
-            { "Detach child $str1 kept in parent $str2" }
+            { "Detach child $str1 kept in parent $str2" },
         )
     }
 
@@ -52,13 +52,13 @@
                 str1 = child.logKey
                 str2 = newParent.logKey
             },
-            { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" }
+            { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" },
         )
     }
 
     fun logRemoveTransientFromContainer(
         childEntry: NotificationEntry,
-        containerEntry: NotificationEntry
+        containerEntry: NotificationEntry,
     ) {
         notificationRenderBuffer.log(
             TAG,
@@ -67,25 +67,20 @@
                 str1 = childEntry.logKey
                 str2 = containerEntry.logKey
             },
-            { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" }
+            { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" },
         )
     }
 
-    fun logRemoveTransientFromNssl(
-        childEntry: NotificationEntry,
-    ) {
+    fun logRemoveTransientFromNssl(childEntry: NotificationEntry) {
         notificationRenderBuffer.log(
             TAG,
             LogLevel.INFO,
             { str1 = childEntry.logKey },
-            { "RemoveTransientRow from Nssl: childKey: $str1" }
+            { "RemoveTransientRow from Nssl: childKey: $str1" },
         )
     }
 
-    fun logRemoveTransientFromViewGroup(
-        childEntry: NotificationEntry,
-        containerView: ViewGroup,
-    ) {
+    fun logRemoveTransientFromViewGroup(childEntry: NotificationEntry, containerView: ViewGroup) {
         notificationRenderBuffer.log(
             TAG,
             LogLevel.WARNING,
@@ -93,14 +88,14 @@
                 str1 = childEntry.logKey
                 str2 = containerView.toString()
             },
-            { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" }
+            { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" },
         )
     }
 
     fun logAddTransientRow(
         childEntry: NotificationEntry,
         containerEntry: NotificationEntry,
-        index: Int
+        index: Int,
     ) {
         notificationRenderBuffer.log(
             TAG,
@@ -110,14 +105,11 @@
                 str2 = containerEntry.logKey
                 int1 = index
             },
-            { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" }
+            { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" },
         )
     }
 
-    fun logRemoveTransientRow(
-        childEntry: NotificationEntry,
-        containerEntry: NotificationEntry,
-    ) {
+    fun logRemoveTransientRow(childEntry: NotificationEntry, containerEntry: NotificationEntry) {
         notificationRenderBuffer.log(
             TAG,
             LogLevel.ERROR,
@@ -125,7 +117,7 @@
                 str1 = childEntry.logKey
                 str2 = containerEntry.logKey
             },
-            { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }
+            { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" },
         )
     }
 
@@ -134,7 +126,7 @@
             TAG,
             LogLevel.INFO,
             { str1 = entry.logKey },
-            { "resetAllContentAlphas: $str1" }
+            { "resetAllContentAlphas: $str1" },
         )
     }
 
@@ -143,7 +135,72 @@
             TAG,
             LogLevel.INFO,
             { str1 = entry.logKey },
-            { "Skip resetAllContentAlphas: $str1" }
+            { "Skip resetAllContentAlphas: $str1" },
+        )
+    }
+
+    fun logStartAppearAnimation(entry: NotificationEntry, isAppear: Boolean) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = entry.logKey
+                bool1 = isAppear
+            },
+            { "startAppearAnimation childKey: $str1 isAppear:$bool1" },
+        )
+    }
+
+    fun logCancelAppearDrawing(entry: NotificationEntry, wasDrawing: Boolean) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.WARNING,
+            {
+                str1 = entry.logKey
+                bool1 = wasDrawing
+            },
+            { "cancelAppearDrawing childKey: $str1 wasDrawing:$bool1" },
+        )
+    }
+
+    fun logAppearAnimationStarted(entry: NotificationEntry, isAppear: Boolean) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = entry.logKey
+                bool1 = isAppear
+            },
+            { "onAppearAnimationStarted childKey: $str1 isAppear:$bool1" },
+        )
+    }
+
+    fun logAppearAnimationSkipped(entry: NotificationEntry, isAppear: Boolean) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.WARNING,
+            {
+                str1 = entry.logKey
+                bool1 = isAppear
+            },
+            { "Skipped an appear animation childKey: $str1 isAppear:$bool1" },
+        )
+    }
+
+    fun logAppearAnimationFinished(
+        entry: NotificationEntry,
+        isAppear: Boolean,
+        cancelled: Boolean,
+    ) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = entry.logKey
+                bool1 = isAppear
+                bool2 = cancelled
+            },
+            { "onAppearAnimationFinished childKey: $str1 isAppear:$bool1 cancelled:$bool2" },
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 43ade5c..4feb78a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -18,6 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProviderModule;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderModule;
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 
 import dagger.Binds;
@@ -29,7 +30,7 @@
 /**
  * Dagger Module containing notification row and view inflation implementations.
  */
-@Module(includes = {AppIconProviderModule.class})
+@Module(includes = {AppIconProviderModule.class, NotificationIconStyleProviderModule.class})
 public abstract class NotificationRowModule {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
new file mode 100644
index 0000000..165c1a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.statusbar.notification.row.icon
+
+import android.annotation.WorkerThread
+import android.app.Flags
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.service.notification.StatusBarNotification
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+import javax.inject.Provider
+
+/**
+ * A provider used to cache and fetch information about which icon should be displayed by
+ * notifications.
+ */
+interface NotificationIconStyleProvider {
+    @WorkerThread
+    fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean
+}
+
+@SysUISingleton
+class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIconStyleProvider {
+    override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean {
+        val packageContext = notification.getPackageContext(context)
+        return !belongsToHeadlessSystemApp(packageContext)
+    }
+
+    @WorkerThread
+    private fun belongsToHeadlessSystemApp(context: Context): Boolean {
+        val info = context.applicationInfo
+        if (info != null) {
+            if ((info.flags and ApplicationInfo.FLAG_SYSTEM) == 0) {
+                // It's not a system app at all.
+                return false
+            } else {
+                // If there's no launch intent, it's probably a headless app.
+                val pm = context.packageManager
+                return (pm.getLaunchIntentForPackage(info.packageName) == null)
+            }
+        } else {
+            // If for some reason we don't have the app info, we don't know; best assume it's
+            // not a system app.
+            return false
+        }
+    }
+}
+
+class NoOpIconStyleProvider : NotificationIconStyleProvider {
+    companion object {
+        const val TAG = "NoOpIconStyleProvider"
+    }
+
+    override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean {
+        Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.")
+        return true
+    }
+}
+
+@Module
+class NotificationIconStyleProviderModule {
+    @Provides
+    @SysUISingleton
+    fun provideImpl(
+        realImpl: Provider<NotificationIconStyleProviderImpl>
+    ): NotificationIconStyleProvider =
+        if (Flags.notificationsRedesignAppIcons()) {
+            realImpl.get()
+        } else {
+            NoOpIconStyleProvider()
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
index 2522e58..79defd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -32,7 +32,10 @@
  */
 class NotificationRowIconViewInflaterFactory
 @Inject
-constructor(private val appIconProvider: AppIconProvider) : NotifRemoteViewsFactory {
+constructor(
+    private val appIconProvider: AppIconProvider,
+    private val iconStyleProvider: NotificationIconStyleProvider,
+) : NotifRemoteViewsFactory {
     override fun instantiate(
         row: ExpandableNotificationRow,
         @NotificationRowContentBinder.InflationFlag layoutType: Int,
@@ -48,8 +51,7 @@
                     view.setIconProvider(
                         object : NotificationRowIconView.NotificationIconProvider {
                             override fun shouldShowAppIcon(): Boolean {
-                                // TODO(b/371174789): implement me
-                                return true
+                                return iconStyleProvider.shouldShowAppIcon(row.entry.sbn, context)
                             }
 
                             override fun getAppIcon(): Drawable {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f3437b5..379a67e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -2098,13 +2098,8 @@
     }
 
     class TouchHandler implements Gefingerpoken {
-        private boolean mSwipeWantsIt = false;
-
         @Override
         public boolean onInterceptTouchEvent(MotionEvent ev) {
-            // Reset on each call to intercept, and share swipe state with onTouchEvent()
-            // below when this method returns true.
-            mSwipeWantsIt = false;
             mView.initDownStates(ev);
             mView.handleEmptySpaceClick(ev);
 
@@ -2127,20 +2122,18 @@
             boolean hunWantsIt = false;
             if (shouldHeadsUpHandleTouch()) {
                 hunWantsIt = mHeadsUpTouchHelper.onInterceptTouchEvent(ev);
-                if (hunWantsIt) {
-                    mView.startDraggingOnHun();
-                }
             }
+            boolean swipeWantsIt = false;
             if (mLongPressedView == null && !mView.isBeingDragged()
                     && !mView.isExpandingNotification()
                     && !mView.getExpandedInThisMotion()
                     && !mView.getOnlyScrollingInThisMotion()
                     && !mView.getDisallowDismissInThisMotion()) {
-                mSwipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+                swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
             }
             // Check if we need to clear any snooze leavebehinds
             boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
-            if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !mSwipeWantsIt &&
+            if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
                     !expandWantsIt && !scrollWantsIt) {
                 mView.setCheckForLeaveBehind(false);
                 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
@@ -2159,8 +2152,7 @@
                     && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
                 mJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
             }
-            return mSwipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt ||
-                    hunWantsIt;
+            return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt || hunWantsIt;
         }
 
         @Override
@@ -2197,7 +2189,7 @@
                     }
                 }
             }
-            boolean horizontalSwipeWantsIt = mSwipeWantsIt;
+            boolean horizontalSwipeWantsIt = false;
             boolean scrollerWantsIt = false;
             // NOTE: the order of these is important. If reversed, onScrollTouch will reset on an
             // UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes.
@@ -2206,7 +2198,7 @@
                     && !mView.getExpandedInThisMotion()
                     && !onlyScrollingInThisMotion
                     && !mView.getDisallowDismissInThisMotion()) {
-                mSwipeHelper.onTouchEvent(ev);
+                horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
             }
             if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
                     && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
@@ -2215,6 +2207,9 @@
             boolean hunWantsIt = false;
             if (shouldHeadsUpHandleTouch()) {
                 hunWantsIt = mHeadsUpTouchHelper.onTouchEvent(ev);
+                if (hunWantsIt) {
+                    mView.startDraggingOnHun();
+                }
             }
 
             // Check if we need to clear any snooze leavebehinds
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 17bd538..74c6e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -47,7 +47,6 @@
 
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.AuthKeyguardMessageArea;
 import com.android.keyguard.KeyguardMessageAreaController;
 import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -68,7 +67,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -77,9 +75,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.model.DismissAction;
-import com.android.systemui.keyguard.shared.model.Edge;
 import com.android.systemui.keyguard.shared.model.KeyguardDone;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.TaskbarDelegate;
@@ -165,7 +161,6 @@
     private final DreamOverlayStateController mDreamOverlayStateController;
     @Nullable
     private final FoldAodAnimationController mFoldAodAnimationController;
-    KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
     private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
     private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -463,11 +458,6 @@
             onPanelExpansionChanged(currentState);
         }
         mNotificationContainer = notificationContainer;
-        if (!DeviceEntryUdfpsRefactor.isEnabled()) {
-            mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
-                    centralSurfaces.getKeyguardMessageArea());
-        }
-
         mCentralSurfacesRegistered = true;
 
         registerListeners();
@@ -518,24 +508,11 @@
             mListenForCanShowAlternateBouncer.cancel(null);
         }
         mListenForCanShowAlternateBouncer = null;
-        if (!DeviceEntryUdfpsRefactor.isEnabled()) {
-            mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow(
-                    mKeyguardTransitionInteractor
-                            .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)),
-                    this::consumeFromAlternateBouncerTransitionSteps
-            );
-
-            mListenForKeyguardAuthenticatedBiometricsHandled = mJavaAdapter.alwaysCollectFlow(
-                    mPrimaryBouncerInteractor.getKeyguardAuthenticatedBiometricsHandled(),
-                    this::consumeKeyguardAuthenticatedBiometricsHandled
-            );
-        } else {
-            // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
-            mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
-                    mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
-                    this::consumeCanShowAlternateBouncer
-            );
-        }
+        // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
+        mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
+                mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
+                this::consumeCanShowAlternateBouncer
+        );
 
         if (KeyguardWmStateRefactor.isEnabled()) {
             // Show the keyguard views whenever we've told WM that the lockscreen is visible.
@@ -792,21 +769,12 @@
             return;
         }
 
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) {
-                Log.d(TAG, "showBouncer:alternateBouncer.forceShow()");
-                mAlternateBouncerInteractor.forceShow();
-                updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
-            } else {
-                showPrimaryBouncer(scrimmed);
-            }
-            return;
-        }
-
-        if (!mAlternateBouncerInteractor.show()) {
-            showPrimaryBouncer(scrimmed);
-        } else {
+        if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) {
+            Log.d(TAG, "showBouncer:alternateBouncer.forceShow()");
+            mAlternateBouncerInteractor.forceShow();
             updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
+        } else {
+            showPrimaryBouncer(scrimmed);
         }
     }
 
@@ -921,13 +889,9 @@
                         mKeyguardGoneCancelAction = null;
                     }
 
-                    if (DeviceEntryUdfpsRefactor.isEnabled()) {
-                        Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()");
-                        mAlternateBouncerInteractor.forceShow();
-                        updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
-                    } else {
-                        updateAlternateBouncerShowing(mAlternateBouncerInteractor.show());
-                    }
+                    Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()");
+                    mAlternateBouncerInteractor.forceShow();
+                    updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
                     setKeyguardMessage(message, null, null);
                     return;
                 }
@@ -1033,11 +997,6 @@
         }
 
         final boolean isShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState();
-        if (mKeyguardMessageAreaController != null) {
-            DeviceEntryUdfpsRefactor.assertInLegacyMode();
-            mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
-            mKeyguardMessageAreaController.setMessage("");
-        }
         if (!SceneContainerFlag.isEnabled()) {
             mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
         }
@@ -1646,12 +1605,7 @@
     /** Display security message to relevant KeyguardMessageArea. */
     public void setKeyguardMessage(String message, ColorStateList colorState,
             BiometricSourceType biometricSourceType) {
-        if (mAlternateBouncerInteractor.isVisibleState()) {
-            if (mKeyguardMessageAreaController != null) {
-                DeviceEntryUdfpsRefactor.assertInLegacyMode();
-                mKeyguardMessageAreaController.setMessage(message, biometricSourceType);
-            }
-        } else {
+        if (!mAlternateBouncerInteractor.isVisibleState()) {
             mPrimaryBouncerInteractor.showMessage(message, colorState);
         }
     }
@@ -1778,66 +1732,6 @@
         }
     }
 
-    /**
-     * An opportunity for the AlternateBouncer to handle the touch instead of sending
-     * the touch to NPVC child views.
-     * @return true if the alternate bouncer should consime the touch and prevent it from
-     * going to its child views
-     */
-    public boolean dispatchTouchEvent(MotionEvent event) {
-        if (shouldInterceptTouchEvent(event)
-                && !mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(event)) {
-            onTouch(event);
-        }
-        return shouldInterceptTouchEvent(event);
-    }
-
-    /**
-     * Whether the touch should be intercepted by the AlternateBouncer before going to the
-     * notification shade's child views.
-     */
-    public boolean shouldInterceptTouchEvent(MotionEvent event) {
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            return false;
-        }
-        return mAlternateBouncerInteractor.isVisibleState();
-    }
-
-    /**
-     * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently
-     * showing.
-     */
-    public boolean onTouch(MotionEvent event) {
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            return false;
-        }
-
-        boolean handleTouch = shouldInterceptTouchEvent(event);
-        if (handleTouch) {
-            final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN;
-            final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch()
-                    && event.getActionMasked() == MotionEvent.ACTION_UP;
-            final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade =
-                    mKeyguardUpdateManager.isUdfpsEnrolled();
-            final boolean actionOutsideShouldDismissAlternateBouncer =
-                    event.getActionMasked() == MotionEvent.ACTION_OUTSIDE
-                    && !udfpsOverlayWillForwardEventsOutsideNotificationShade;
-            if (actionDown) {
-                mAlternateBouncerInteractor.setReceivedDownTouch(true);
-            } else if ((actionDownThenUp || actionOutsideShouldDismissAlternateBouncer)
-                    && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) {
-                showPrimaryBouncer(true);
-            }
-        }
-
-        // Forward NPVC touches to callbacks in case they want to respond to touches
-        for (KeyguardViewManagerCallback callback: mCallbacks) {
-            callback.onTouch(event);
-        }
-
-        return handleTouch;
-    }
-
     /** Update keyguard position based on a tapped X coordinate. */
     public void updateKeyguardPosition(float x) {
         mPrimaryBouncerInteractor.setKeyguardPosition(x);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 5f864e5..09e191d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -18,10 +18,12 @@
 import android.view.Display
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Default
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.core.CommandQueueInitializer
 import com.android.systemui.statusbar.core.MultiDisplayStatusBarInitializerStore
+import com.android.systemui.statusbar.core.MultiDisplayStatusBarStarter
 import com.android.systemui.statusbar.core.SingleDisplayStatusBarInitializerStore
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.core.StatusBarInitializer
@@ -29,7 +31,9 @@
 import com.android.systemui.statusbar.core.StatusBarInitializerStore
 import com.android.systemui.statusbar.core.StatusBarOrchestrator
 import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
 import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl
 import dagger.Binds
@@ -38,6 +42,7 @@
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineScope
 
 /** Similar in purpose to [StatusBarModule], but scoped only to phones */
 @Module
@@ -58,24 +63,56 @@
         implFactory: StatusBarInitializerImpl.Factory
     ): StatusBarInitializer.Factory
 
-    /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
-    @Binds
-    @IntoMap
-    @ClassKey(StatusBarInitializerImpl::class)
-    fun bindStatusBarInitializer(@Default impl: StatusBarInitializerImpl): CoreStartable
-
     @Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer
 
     companion object {
+        /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(StatusBarInitializer::class)
+        fun bindStatusBarInitializer(
+            @Default defaultInitializerLazy: Lazy<StatusBarInitializerImpl>
+        ): CoreStartable {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                // Will be started through MultiDisplayStatusBarStarter
+                CoreStartable.NOP
+            } else {
+                defaultInitializerLazy.get()
+            }
+        }
+
         // Dagger doesn't support providing AssistedInject types, without a qualifier. Using the
         // Default qualifier for this reason.
         @Default
         @Provides
         @SysUISingleton
         fun statusBarInitializerImpl(
-            implFactory: StatusBarInitializerImpl.Factory
+            implFactory: StatusBarInitializerImpl.Factory,
+            statusBarWindowControllerStore: StatusBarWindowControllerStore,
         ): StatusBarInitializerImpl {
-            return implFactory.create(displayId = Display.DEFAULT_DISPLAY)
+            return implFactory.create(statusBarWindowControllerStore.defaultDisplay)
+        }
+
+        @Provides
+        @SysUISingleton
+        @Default // Dagger does not support providing @AssistedInject types without a qualifier
+        fun orchestrator(
+            @Background backgroundApplicationScope: CoroutineScope,
+            statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+            statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+            initializerStore: StatusBarInitializerStore,
+            statusBarWindowControllerStore: StatusBarWindowControllerStore,
+            statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
+        ): StatusBarOrchestrator {
+            return statusBarOrchestratorFactory.create(
+                Display.DEFAULT_DISPLAY,
+                backgroundApplicationScope,
+                statusBarWindowStateRepositoryStore.defaultDisplay,
+                statusBarModeRepositoryStore.defaultDisplay,
+                initializerStore.defaultDisplay,
+                statusBarWindowControllerStore.defaultDisplay,
+            )
         }
 
         @Provides
@@ -83,11 +120,29 @@
         @IntoMap
         @ClassKey(StatusBarOrchestrator::class)
         fun orchestratorCoreStartable(
-            orchestratorLazy: Lazy<StatusBarOrchestrator>
+            @Default orchestratorLazy: Lazy<StatusBarOrchestrator>
         ): CoreStartable {
-            return if (StatusBarSimpleFragment.isEnabled) {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                // Will be started through MultiDisplayStatusBarStarter
+                CoreStartable.NOP
+            } else if (StatusBarSimpleFragment.isEnabled) {
                 orchestratorLazy.get()
             } else {
+                // Will be started through CentralSurfacesImpl
+                CoreStartable.NOP
+            }
+        }
+
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(MultiDisplayStatusBarStarter::class)
+        fun multiDisplayStarter(
+            multiDisplayStatusBarStarterLazy: Lazy<MultiDisplayStatusBarStarter>
+        ): CoreStartable {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                multiDisplayStatusBarStarterLazy.get()
+            } else {
                 CoreStartable.NOP
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index c258095..d868519 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -29,6 +29,7 @@
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -55,7 +56,7 @@
 import com.android.systemui.statusbar.OperatorNameView;
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
@@ -333,7 +334,7 @@
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
-        mDumpManager.registerDumpable(getClass().getSimpleName(), this);
+        mDumpManager.registerDumpable(getDumpableName(), this);
         mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(
                 (PhoneStatusBarView) getView());
         mStatusBarFragmentComponent.init();
@@ -374,6 +375,14 @@
                 mStatusBar, mCollapsedStatusBarViewModel, mStatusBarVisibilityChangeListener);
     }
 
+    private String getDumpableName() {
+        if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+            return getClass().getSimpleName();
+        } else {
+            return getClass().getSimpleName() + getContext().getDisplayId();
+        }
+    }
+
     @Override
     public void onCameraLaunchGestureDetected(int source) {
         mWaitingForWindowStateChangeAfterCameraLaunch = true;
@@ -470,7 +479,7 @@
             startable.stop();
             mStartableStates.put(startable, Startable.State.STOPPED);
         }
-        mDumpManager.unregisterDumpable(getClass().getSimpleName());
+        mDumpManager.unregisterDumpable(getDumpableName());
         if (mNicBindingDisposable != null) {
             mNicBindingDisposable.dispose();
             mNicBindingDisposable = null;
@@ -486,7 +495,12 @@
         NotificationIconContainer notificationIcons =
                 notificationIconArea.requireViewById(R.id.notificationIcons);
         mNotificationIconAreaInner = notificationIcons;
-        mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+        if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+            //TODO(b/369337701): implement notification icons for all displays.
+            // Currently if we try to bind for all displays, there is a crash, because the same
+            // notification icon view can't have multiple parents.
+            mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+        }
 
         if (!StatusBarSimpleFragment.isEnabled()) {
             updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
@@ -642,7 +656,7 @@
         }
         boolean showSecondaryOngoingActivityChip =
                 Flags.statusBarScreenSharingChips()
-                        && StatusBarRonChips.isEnabled()
+                        && StatusBarNotifChips.isEnabled()
                         && mHasSecondaryOngoingActivity;
 
         return new StatusBarVisibilityModel(
@@ -684,7 +698,7 @@
 
         boolean showSecondaryOngoingActivityChip =
                 // Secondary chips are only supported when RONs are enabled.
-                StatusBarRonChips.isEnabled()
+                StatusBarNotifChips.isEnabled()
                         && visibilityModel.getShowSecondaryOngoingActivityChip()
                         && !disableNotifications;
         if (showSecondaryOngoingActivityChip) {
@@ -811,7 +825,7 @@
     }
 
     private void showSecondaryOngoingActivityChip(boolean animate) {
-        StatusBarRonChips.assertInNewMode();
+        StatusBarNotifChips.assertInNewMode();
         StatusBarSimpleFragment.assertInLegacyMode();
         animateShow(mSecondaryOngoingActivityChip, animate);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index 9c168be..3a07d9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.core.StatusBarSimpleFragment
@@ -83,7 +84,7 @@
                     }
                 }
 
-                if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) {
+                if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) {
                     val primaryChipView: View =
                         view.requireViewById(R.id.ongoing_activity_chip_primary)
                     launch {
@@ -119,7 +120,7 @@
                     }
                 }
 
-                if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) {
+                if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) {
                     val primaryChipView: View =
                         view.requireViewById(R.id.ongoing_activity_chip_primary)
                     val secondaryChipView: View =
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 2135817..d0817d7 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -61,7 +61,6 @@
 
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
 
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
         requestWindowFeature(Window.FEATURE_NO_TITLE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
deleted file mode 100644
index c51aa04..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
+++ /dev/null
@@ -1,245 +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.keyguard;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.AnimatedStateListDrawable;
-import android.util.Pair;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.ImageView;
-
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.doze.util.BurnInHelperKt;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
-
-public class LegacyLockIconViewControllerBaseTest extends SysuiTestCase {
-    protected static final String UNLOCKED_LABEL = "unlocked";
-    protected static final String LOCKED_LABEL = "locked";
-    protected static final int PADDING = 10;
-
-    protected MockitoSession mStaticMockSession;
-
-    protected final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
-    protected @Mock DeviceEntryInteractor mDeviceEntryInteractor;
-    protected @Mock LockIconView mLockIconView;
-    protected @Mock ImageView mLockIcon;
-    protected @Mock AnimatedStateListDrawable mIconDrawable;
-    protected @Mock Context mContext;
-    protected @Mock Resources mResources;
-    protected @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager;
-    protected @Mock StatusBarStateController mStatusBarStateController;
-    protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    protected @Mock KeyguardViewController mKeyguardViewController;
-    protected @Mock KeyguardStateController mKeyguardStateController;
-    protected @Mock FalsingManager mFalsingManager;
-    protected @Mock AuthController mAuthController;
-    protected @Mock DumpManager mDumpManager;
-    protected @Mock AccessibilityManager mAccessibilityManager;
-    protected @Mock ConfigurationController mConfigurationController;
-    protected @Mock VibratorHelper mVibrator;
-    protected @Mock AuthRippleController mAuthRippleController;
-    protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
-    protected FakeFeatureFlags mFeatureFlags;
-
-    protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-
-    protected LegacyLockIconViewController mUnderTest;
-
-    // Capture listeners so that they can be used to send events
-    @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
-            ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-
-    @Captor protected ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor =
-            ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
-    protected KeyguardStateController.Callback mKeyguardStateCallback;
-
-    @Captor protected ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor =
-            ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-    protected StatusBarStateController.StateListener mStatusBarStateListener;
-
-    @Captor protected ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor;
-    protected AuthController.Callback mAuthControllerCallback;
-
-    @Captor protected ArgumentCaptor<KeyguardUpdateMonitorCallback>
-            mKeyguardUpdateMonitorCallbackCaptor =
-            ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-    protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
-
-    @Captor protected ArgumentCaptor<Point> mPointCaptor;
-
-    @Before
-    public void setUp() throws Exception {
-        mStaticMockSession = mockitoSession()
-                .mockStatic(BurnInHelperKt.class)
-                .strictness(Strictness.LENIENT)
-                .startMocking();
-        MockitoAnnotations.initMocks(this);
-
-        setupLockIconViewMocks();
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
-        Rect windowBounds = new Rect(0, 0, 800, 1200);
-        when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds);
-        when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL);
-        when(mResources.getString(R.string.accessibility_lock_icon)).thenReturn(LOCKED_LABEL);
-        when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable);
-        when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING);
-        when(mAuthController.getScaleFactor()).thenReturn(1f);
-
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
-        when(mStatusBarStateController.isDozing()).thenReturn(false);
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
-
-        if (!SceneContainerFlag.isEnabled()) {
-            mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
-            //TODO move this to use @DisableFlags annotation if needed
-            mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
-        }
-
-        mFeatureFlags = new FakeFeatureFlags();
-        mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
-        mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
-
-        mUnderTest = new LegacyLockIconViewController(
-                mStatusBarStateController,
-                mKeyguardUpdateMonitor,
-                mKeyguardViewController,
-                mKeyguardStateController,
-                mFalsingManager,
-                mAuthController,
-                mDumpManager,
-                mAccessibilityManager,
-                mConfigurationController,
-                mDelayableExecutor,
-                mVibrator,
-                mAuthRippleController,
-                mResources,
-                mKosmos.getKeyguardTransitionInteractor(),
-                KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
-                mFeatureFlags,
-                mPrimaryBouncerInteractor,
-                mContext,
-                () -> mDeviceEntryInteractor
-        );
-    }
-
-    @After
-    public void tearDown() {
-        mStaticMockSession.finishMocking();
-    }
-
-    protected Pair<Float, Point> setupUdfps() {
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
-        final Point udfpsLocation = new Point(50, 75);
-        final float radius = 33f;
-        when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
-        when(mAuthController.getUdfpsRadius()).thenReturn(radius);
-
-        return new Pair(radius, udfpsLocation);
-    }
-
-    protected void setupShowLockIcon() {
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
-        when(mStatusBarStateController.isDozing()).thenReturn(false);
-        when(mStatusBarStateController.getDozeAmount()).thenReturn(0f);
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
-    }
-
-    protected void captureAuthControllerCallback() {
-        verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture());
-        mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue();
-    }
-
-    protected void captureKeyguardStateCallback() {
-        verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture());
-        mKeyguardStateCallback = mKeyguardStateCaptor.getValue();
-    }
-
-    protected void captureStatusBarStateListener() {
-        verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture());
-        mStatusBarStateListener = mStatusBarStateCaptor.getValue();
-    }
-
-    protected void captureKeyguardUpdateMonitorCallback() {
-        verify(mKeyguardUpdateMonitor).registerCallback(
-                mKeyguardUpdateMonitorCallbackCaptor.capture());
-        mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue();
-    }
-
-    protected void setupLockIconViewMocks() {
-        when(mLockIconView.getResources()).thenReturn(mResources);
-        when(mLockIconView.getContext()).thenReturn(mContext);
-        when(mLockIconView.getLockIcon()).thenReturn(mLockIcon);
-    }
-
-    protected void resetLockIconView() {
-        reset(mLockIconView);
-        setupLockIconViewMocks();
-    }
-
-    protected void init(boolean useDozeMigrationFlag) {
-        mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag);
-        mUnderTest.setLockIconView(mLockIconView);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
deleted file mode 100644
index c1ba39e..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-
-import static com.android.keyguard.LockIconView.ICON_LOCK;
-import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.graphics.Point;
-import android.hardware.biometrics.BiometricSourceType;
-import android.testing.TestableLooper;
-import android.util.Pair;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.biometrics.UdfpsController;
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.doze.util.BurnInHelperKt;
-import com.android.systemui.flags.EnableSceneContainer;
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class LegacyLockIconViewControllerTest extends LegacyLockIconViewControllerBaseTest {
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        when(mLockIconView.isAttachedToWindow()).thenReturn(true);
-    }
-
-    @Test
-    public void testUpdateFingerprintLocationOnInit() {
-        // GIVEN fp sensor location is available pre-attached
-        Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location
-
-        // WHEN lock icon view controller is initialized and attached
-        init(/* useMigrationFlag= */false);
-
-        // THEN lock icon view location is updated to the udfps location with UDFPS radius
-        verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
-                eq(PADDING));
-    }
-
-    @Test
-    public void testUpdatePaddingBasedOnResolutionScale() {
-        // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5
-        Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location
-        when(mAuthController.getScaleFactor()).thenReturn(5f);
-
-        // WHEN lock icon view controller is initialized and attached
-        init(/* useMigrationFlag= */false);
-
-        // THEN lock icon view location is updated with the scaled radius
-        verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
-                eq(PADDING * 5));
-    }
-
-    @Test
-    public void testUpdateLockIconLocationOnAuthenticatorsRegistered() {
-        // GIVEN fp sensor location is not available pre-init
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
-        when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
-        init(/* useMigrationFlag= */false);
-        resetLockIconView(); // reset any method call counts for when we verify method calls later
-
-        // GIVEN fp sensor location is available post-attached
-        captureAuthControllerCallback();
-        Pair<Float, Point> udfps = setupUdfps();
-
-        // WHEN all authenticators are registered
-        mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
-        mDelayableExecutor.runAllReady();
-
-        // THEN lock icon view location is updated with the same coordinates as auth controller vals
-        verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
-                eq(PADDING));
-    }
-
-    @Test
-    public void testUpdateLockIconLocationOnUdfpsLocationChanged() {
-        // GIVEN fp sensor location is not available pre-init
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
-        when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
-        init(/* useMigrationFlag= */false);
-        resetLockIconView(); // reset any method call counts for when we verify method calls later
-
-        // GIVEN fp sensor location is available post-attached
-        captureAuthControllerCallback();
-        Pair<Float, Point> udfps = setupUdfps();
-
-        // WHEN udfps location changes
-        mAuthControllerCallback.onUdfpsLocationChanged(new UdfpsOverlayParams());
-        mDelayableExecutor.runAllReady();
-
-        // THEN lock icon view location is updated with the same coordinates as auth controller vals
-        verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
-                eq(PADDING));
-    }
-
-    @Test
-    public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() {
-        // GIVEN Udpfs sensor location is available
-        setupUdfps();
-
-        // WHEN the view is attached
-        init(/* useMigrationFlag= */false);
-
-        // THEN the lock icon view background should be enabled
-        verify(mLockIconView).setUseBackground(true);
-    }
-
-    @Test
-    public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() {
-        // GIVEN Udfps sensor location is not supported
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
-
-        // WHEN the view is attached
-        init(/* useMigrationFlag= */false);
-
-        // THEN the lock icon view background should be disabled
-        verify(mLockIconView).setUseBackground(false);
-    }
-
-    @Test
-    public void testLockIconStartState() {
-        // GIVEN lock icon state
-        setupShowLockIcon();
-
-        // WHEN lock icon controller is initialized
-        init(/* useMigrationFlag= */false);
-
-        // THEN the lock icon should show
-        verify(mLockIconView).updateIcon(ICON_LOCK, false);
-    }
-
-    @Test
-    public void testLockIcon_updateToUnlock() {
-        // GIVEN starting state for the lock icon
-        setupShowLockIcon();
-
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        reset(mLockIconView);
-
-        // WHEN the unlocked state changes to canDismissLockScreen=true
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the unlock should show
-        verify(mLockIconView).updateIcon(ICON_UNLOCK, false);
-    }
-
-    @Test
-    public void testLockIcon_clearsIconWhenUnlocked() {
-        // GIVEN udfps not enrolled
-        setupUdfps();
-        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false);
-
-        // GIVEN starting state for the lock icon
-        setupShowLockIcon();
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureStatusBarStateListener();
-        reset(mLockIconView);
-
-        // WHEN the dozing state changes
-        mStatusBarStateListener.onDozingChanged(false /* isDozing */);
-
-        // THEN the icon is cleared
-        verify(mLockIconView).clearIcon();
-    }
-
-    @Test
-    public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() {
-        // GIVEN udfps enrolled
-        setupUdfps();
-        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
-        // GIVEN starting state for the lock icon
-        setupShowLockIcon();
-
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureStatusBarStateListener();
-        reset(mLockIconView);
-
-        // WHEN the dozing state changes
-        mStatusBarStateListener.onDozingChanged(true /* isDozing */);
-
-        // THEN the AOD lock icon should show
-        verify(mLockIconView).updateIcon(ICON_LOCK, true);
-    }
-
-    @Test
-    public void testBurnInOffsetsUpdated_onDozeAmountChanged() {
-        // GIVEN udfps enrolled
-        setupUdfps();
-        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
-        // GIVEN burn-in offset = 5
-        int burnInOffset = 5;
-        when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset);
-
-        // GIVEN starting state for the lock icon (keyguard)
-        setupShowLockIcon();
-        init(/* useMigrationFlag= */false);
-        captureStatusBarStateListener();
-        reset(mLockIconView);
-
-        // WHEN dozing updates
-        mStatusBarStateListener.onDozingChanged(true /* isDozing */);
-        mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
-
-        // THEN the view's translation is updated to use the AoD burn-in offsets
-        verify(mLockIconView).setTranslationY(burnInOffset);
-        verify(mLockIconView).setTranslationX(burnInOffset);
-        reset(mLockIconView);
-
-        // WHEN the device is no longer dozing
-        mStatusBarStateListener.onDozingChanged(false /* isDozing */);
-        mStatusBarStateListener.onDozeAmountChanged(0f, 0f);
-
-        // THEN the view is updated to NO translation (no burn-in offsets anymore)
-        verify(mLockIconView).setTranslationY(0);
-        verify(mLockIconView).setTranslationX(0);
-    }
-
-    @Test
-    public void lockIconShows_afterUnlockStateChanges() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-
-        // WHEN the unlocked state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setContentDescription(LOCKED_LABEL);
-    }
-
-    @Test
-    public void lockIconAccessibility_notVisibleToUser() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-        when(mLockIconView.isVisibleToUser()).thenReturn(false);
-
-        // WHEN the unlocked state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-    }
-
-    @Test
-    public void lockIconAccessibility_bouncerAnimatingAway() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-        when(mLockIconView.isVisibleToUser()).thenReturn(true);
-        when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true);
-
-        // WHEN the unlocked state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-    }
-
-    @Test
-    public void lockIconAccessibility_bouncerNotAnimatingAway_viewVisible() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-        when(mLockIconView.isVisibleToUser()).thenReturn(true);
-        when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(false);
-
-        // WHEN the unlocked state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-    }
-
-    @Test
-    public void playHaptic_onTouchExploration_performHapticFeedback() {
-       // WHEN request to vibrate on touch exploration
-        mUnderTest.vibrateOnTouchExploration();
-
-        // THEN performHapticFeedback is used
-        verify(mVibrator).performHapticFeedback(any(), eq(HapticFeedbackConstants.CONTEXT_CLICK));
-    }
-
-    @Test
-    public void playHaptic_onLongPress_performHapticFeedback() {
-        // WHEN request to vibrate on long press
-        mUnderTest.vibrateOnLongPress();
-
-        // THEN uses perform haptic feedback
-        verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
-    }
-
-    @Test
-    public void longPress_showBouncer_sceneContainerNotEnabled() {
-        init(/* useMigrationFlag= */ false);
-        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
-
-        // WHEN longPress
-        mUnderTest.onLongPress();
-
-        // THEN show primary bouncer via keyguard view controller, not scene container
-        verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean());
-        verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
-    }
-
-    @Test
-    @EnableSceneContainer
-    public void longPress_showBouncer() {
-        init(/* useMigrationFlag= */ false);
-        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
-
-        // WHEN longPress
-        mUnderTest.onLongPress();
-
-        // THEN show primary bouncer
-        verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
-        verify(mDeviceEntryInteractor).attemptDeviceEntry();
-    }
-
-    @Test
-    @EnableSceneContainer
-    public void longPress_falsingTriggered_doesNotShowBouncer() {
-        init(/* useMigrationFlag= */ false);
-        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
-
-        // WHEN longPress
-        mUnderTest.onLongPress();
-
-        // THEN don't show primary bouncer
-        verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
-        verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
deleted file mode 100644
index 2fd3cb0..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,149 +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.keyguard
-
-import android.view.View
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.keyguard.LockIconView.ICON_LOCK
-import com.android.systemui.doze.util.getBurnInOffset
-import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.util.mockito.whenever
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControllerBaseTest() {
-
-    /** After migration, replaces LockIconViewControllerTest version */
-    @Test
-    fun testLockIcon_clearsIconWhenUnlocked() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN udfps not enrolled
-            setupUdfps()
-            whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false)
-
-            // GIVEN starting state for the lock icon
-            setupShowLockIcon()
-            whenever(mStatusBarStateController.state).thenReturn(StatusBarState.SHADE)
-
-            // GIVEN lock icon controller is initialized and view is attached
-            init(/* useMigrationFlag= */ true)
-            reset(mLockIconView)
-
-            // WHEN the dozing state changes
-            mUnderTest.mIsDozingCallback.accept(false)
-            // THEN the icon is cleared
-            verify(mLockIconView).clearIcon()
-        }
-
-    /** After migration, replaces LockIconViewControllerTest version */
-    @Test
-    fun testLockIcon_updateToAodLock_whenUdfpsEnrolled() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN udfps enrolled
-            setupUdfps()
-            whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true)
-
-            // GIVEN starting state for the lock icon
-            setupShowLockIcon()
-
-            // GIVEN lock icon controller is initialized and view is attached
-            init(/* useMigrationFlag= */ true)
-            reset(mLockIconView)
-
-            // WHEN the dozing state changes
-            mUnderTest.mIsDozingCallback.accept(true)
-
-            // THEN the AOD lock icon should show
-            verify(mLockIconView).updateIcon(ICON_LOCK, true)
-        }
-
-    /** After migration, replaces LockIconViewControllerTest version */
-    @Test
-    fun testBurnInOffsetsUpdated_onDozeAmountChanged() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN udfps enrolled
-            setupUdfps()
-            whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true)
-
-            // GIVEN burn-in offset = 5
-            val burnInOffset = 5
-            whenever(getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset)
-
-            // GIVEN starting state for the lock icon (keyguard)
-            setupShowLockIcon()
-            init(/* useMigrationFlag= */ true)
-            reset(mLockIconView)
-
-            // WHEN dozing updates
-            mUnderTest.mIsDozingCallback.accept(true)
-            mUnderTest.mDozeTransitionCallback.accept(1f)
-
-            // THEN the view's translation is updated to use the AoD burn-in offsets
-            verify(mLockIconView).setTranslationY(burnInOffset.toFloat())
-            verify(mLockIconView).setTranslationX(burnInOffset.toFloat())
-            reset(mLockIconView)
-
-            // WHEN the device is no longer dozing
-            mUnderTest.mIsDozingCallback.accept(false)
-            mUnderTest.mDozeTransitionCallback.accept(0f)
-
-            // THEN the view is updated to NO translation (no burn-in offsets anymore)
-            verify(mLockIconView).setTranslationY(0f)
-            verify(mLockIconView).setTranslationX(0f)
-        }
-
-    @Test
-    fun testHideLockIconView_onLockscreenHostedDreamStateChanged() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN starting state for the lock icon (keyguard) and wallpaper dream enabled
-            mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true)
-            setupShowLockIcon()
-            init(/* useMigrationFlag= */ true)
-            reset(mLockIconView)
-
-            // WHEN dream starts
-            mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept(
-                true /* isActiveDreamLockscreenHosted */
-            )
-
-            // THEN the lock icon is hidden
-            verify(mLockIconView).visibility = View.INVISIBLE
-            reset(mLockIconView)
-
-            // WHEN the device is no longer dreaming
-            mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept(
-                false /* isActiveDreamLockscreenHosted */
-            )
-
-            // THEN lock icon is visible
-            verify(mLockIconView).visibility = View.VISIBLE
-        }
-
-    companion object {
-        private val IMMEDIATE = Dispatchers.Main.immediate
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index 8b427fb..071acfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -156,7 +156,7 @@
             createEndState(transitionContainer),
             backgroundLayer,
             fadeWindowBackgroundLayer,
-            useSpring,
+            useSpring = useSpring,
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
deleted file mode 100644
index 6dc4b10..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.graphics.Point
-import android.hardware.biometrics.BiometricSourceType
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.testing.TestableLooper.RunWithLooper
-import android.util.DisplayMetrics
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.keyguard.logging.KeyguardLogger
-import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
-import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.LightRevealScrim
-import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.leak.RotationUtils
-import com.android.systemui.util.mockito.any
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import org.junit.After
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
-import javax.inject.Provider
-
-
-@ExperimentalCoroutinesApi
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class AuthRippleControllerTest : SysuiTestCase() {
-    private lateinit var staticMockSession: MockitoSession
-
-    private lateinit var controller: AuthRippleController
-    @Mock private lateinit var rippleView: AuthRippleView
-    @Mock private lateinit var commandRegistry: CommandRegistry
-    @Mock private lateinit var configurationController: ConfigurationController
-    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var authController: AuthController
-    @Mock private lateinit var authRippleInteractor: AuthRippleInteractor
-    @Mock private lateinit var keyguardStateController: KeyguardStateController
-    @Mock
-    private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
-    @Mock
-    private lateinit var notificationShadeWindowController: NotificationShadeWindowController
-    @Mock
-    private lateinit var biometricUnlockController: BiometricUnlockController
-    @Mock
-    private lateinit var udfpsControllerProvider: Provider<UdfpsController>
-    @Mock
-    private lateinit var udfpsController: UdfpsController
-    @Mock
-    private lateinit var statusBarStateController: StatusBarStateController
-    @Mock
-    private lateinit var lightRevealScrim: LightRevealScrim
-    @Mock
-    private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
-
-    private val facePropertyRepository = FakeFacePropertyRepository()
-    private val displayMetrics = DisplayMetrics()
-
-    @Captor
-    private lateinit var biometricUnlockListener:
-            ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>
-
-    @Before
-    fun setUp() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        MockitoAnnotations.initMocks(this)
-        staticMockSession = mockitoSession()
-                .mockStatic(RotationUtils::class.java)
-                .strictness(Strictness.LENIENT)
-                .startMocking()
-
-        `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE)
-        `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp))
-        `when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
-
-        controller = AuthRippleController(
-            context,
-            authController,
-            configurationController,
-            keyguardUpdateMonitor,
-            keyguardStateController,
-            wakefulnessLifecycle,
-            commandRegistry,
-            notificationShadeWindowController,
-            udfpsControllerProvider,
-            statusBarStateController,
-            displayMetrics,
-            KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
-            biometricUnlockController,
-            lightRevealScrim,
-            authRippleInteractor,
-            facePropertyRepository,
-            rippleView,
-        )
-        controller.init()
-    }
-
-    @After
-    fun tearDown() {
-        staticMockSession.finishMocking()
-    }
-
-    @Test
-    fun testFingerprintTrigger_KeyguardShowing_Ripple() {
-        // GIVEN fp exists, keyguard is showing, unlocking with fp allowed
-        val fpsLocation = Point(5, 5)
-        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
-        controller.onViewAttached()
-        `when`(keyguardStateController.isShowing).thenReturn(true)
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
-
-        // WHEN fingerprint authenticated
-        verify(biometricUnlockController).addListener(biometricUnlockListener.capture())
-        biometricUnlockListener.value
-                .onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType.FINGERPRINT)
-
-        // THEN update sensor location and show ripple
-        verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
-        verify(rippleView).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() {
-        // GIVEN fp exists & unlocking with fp allowed
-        val fpsLocation = Point(5, 5)
-        `when`(authController.udfpsLocation).thenReturn(fpsLocation)
-        controller.onViewAttached()
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
-
-        // WHEN keyguard is NOT showing & fingerprint authenticated
-        `when`(keyguardStateController.isShowing).thenReturn(false)
-        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
-        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-        captor.value.onBiometricAuthenticated(
-            0 /* userId */,
-            BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */)
-
-        // THEN no ripple
-        verify(rippleView, never()).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() {
-        // GIVEN fp exists & keyguard is showing
-        val fpsLocation = Point(5, 5)
-        `when`(authController.udfpsLocation).thenReturn(fpsLocation)
-        controller.onViewAttached()
-        `when`(keyguardStateController.isShowing).thenReturn(true)
-
-        // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                eq(BiometricSourceType.FINGERPRINT))).thenReturn(false)
-        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
-        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-        captor.value.onBiometricAuthenticated(
-            0 /* userId */,
-            BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */)
-
-        // THEN no ripple
-        verify(rippleView, never()).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun testNullFaceSensorLocationDoesNothing() {
-        facePropertyRepository.setSensorLocation(null)
-        controller.onViewAttached()
-
-        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
-        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
-        captor.value.onBiometricAuthenticated(
-            0 /* userId */,
-            BiometricSourceType.FACE /* type */,
-            false /* isStrongBiometric */)
-        verify(rippleView, never()).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun testNullFingerprintSensorLocationDoesNothing() {
-        `when`(authController.fingerprintSensorLocation).thenReturn(null)
-        controller.onViewAttached()
-
-        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
-        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
-        captor.value.onBiometricAuthenticated(
-            0 /* userId */,
-            BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */)
-        verify(rippleView, never()).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun registersAndDeregisters() {
-        controller.onViewAttached()
-        val captor = ArgumentCaptor
-            .forClass(KeyguardStateController.Callback::class.java)
-        verify(keyguardStateController).addCallback(captor.capture())
-        val captor2 = ArgumentCaptor
-            .forClass(WakefulnessLifecycle.Observer::class.java)
-        verify(wakefulnessLifecycle).addObserver(captor2.capture())
-        controller.onViewDetached()
-        verify(keyguardStateController).removeCallback(any())
-        verify(wakefulnessLifecycle).removeObserver(any())
-    }
-
-    @Test
-    @RunWithLooper(setAsMainLooper = true)
-    fun testAnimatorRunWhenWakeAndUnlock_fingerprint() {
-        mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
-        val fpsLocation = Point(5, 5)
-        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
-        controller.onViewAttached()
-        `when`(keyguardStateController.isShowing).thenReturn(true)
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                BiometricSourceType.FINGERPRINT)).thenReturn(true)
-        `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
-
-        controller.showUnlockRipple(BiometricSourceType.FINGERPRINT)
-        assertTrue("reveal didn't start on keyguardFadingAway",
-            controller.startLightRevealScrimOnKeyguardFadingAway)
-        `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
-        controller.onKeyguardFadingAwayChanged()
-        assertFalse("reveal triggers multiple times",
-            controller.startLightRevealScrimOnKeyguardFadingAway)
-    }
-
-    @Test
-    @RunWithLooper(setAsMainLooper = true)
-    fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() {
-        mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
-        val faceLocation = Point(5, 5)
-        facePropertyRepository.setSensorLocation(faceLocation)
-        controller.onViewAttached()
-        `when`(keyguardStateController.isShowing).thenReturn(true)
-        `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
-        `when`(authController.isUdfpsFingerDown).thenReturn(true)
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                eq(BiometricSourceType.FACE))).thenReturn(true)
-
-        controller.showUnlockRipple(BiometricSourceType.FACE)
-        assertTrue("reveal didn't start on keyguardFadingAway",
-                controller.startLightRevealScrimOnKeyguardFadingAway)
-        `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
-        controller.onKeyguardFadingAwayChanged()
-        assertFalse("reveal triggers multiple times",
-                controller.startLightRevealScrimOnKeyguardFadingAway)
-    }
-
-    @Test
-    fun testUpdateRippleColor() {
-        controller.onViewAttached()
-        val captor = ArgumentCaptor
-            .forClass(ConfigurationController.ConfigurationListener::class.java)
-        verify(configurationController).addCallback(captor.capture())
-
-        reset(rippleView)
-        captor.value.onThemeChanged()
-        verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
-
-        reset(rippleView)
-        captor.value.onUiModeChanged()
-        verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
-    }
-
-    @Test
-    fun testUdfps_onFingerDown_runningForDeviceEntry_showDwellRipple() {
-        // GIVEN fingerprint detection is running on keyguard
-        `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true)
-
-        // GIVEN view is already attached
-        controller.onViewAttached()
-        val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
-        verify(udfpsController).addCallback(captor.capture())
-
-        // GIVEN fp is updated to Point(5, 5)
-        val fpsLocation = Point(5, 5)
-        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
-
-        // WHEN finger is down
-        captor.value.onFingerDown()
-
-        // THEN update sensor location and show ripple
-        verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
-        verify(rippleView).startDwellRipple(false)
-    }
-
-    @Test
-    fun testUdfps_onFingerDown_notDeviceEntry_doesNotShowDwellRipple() {
-        // GIVEN fingerprint detection is NOT running on keyguard
-        `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(false)
-
-        // GIVEN view is already attached
-        controller.onViewAttached()
-        val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
-        verify(udfpsController).addCallback(captor.capture())
-
-        // WHEN finger is down
-        captor.value.onFingerDown()
-
-        // THEN doesn't show dwell ripple
-        verify(rippleView, never()).startDwellRipple(false)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index e2a6a55..4baca71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -16,21 +16,14 @@
 
 package com.android.systemui.biometrics
 
-import android.graphics.Rect
 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
 import android.hardware.biometrics.BiometricRequestConstants.RequestReason
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
 import android.testing.TestableLooper.RunWithLooper
 import android.view.LayoutInflater
 import android.view.MotionEvent
-import android.view.Surface
-import android.view.Surface.Rotation
 import android.view.View
-import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -38,7 +31,6 @@
 import com.android.app.viewcapture.ViewCapture
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
@@ -61,9 +53,7 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -86,7 +76,6 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
@@ -118,7 +107,6 @@
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var dialogManager: SystemUIDialogManager
     @Mock private lateinit var dumpManager: DumpManager
-    @Mock private lateinit var transitionController: LockscreenShadeTransitionController
     @Mock private lateinit var configurationController: ConfigurationController
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock
@@ -126,8 +114,6 @@
     @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
     @Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
     @Mock private lateinit var udfpsController: UdfpsController
-    @Mock private lateinit var udfpsView: UdfpsView
-    @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy
     @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
     @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
     @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@@ -147,7 +133,7 @@
     private lateinit var powerInteractor: PowerInteractor
     private lateinit var testScope: TestScope
 
-    private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
+    private val onTouch = { _: View, _: MotionEvent -> true }
     private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
     private lateinit var controllerOverlay: UdfpsControllerOverlay
 
@@ -158,53 +144,37 @@
         powerInteractor = kosmos.powerInteractor
         keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
         keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
-        whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView)
-        whenever(inflater.inflate(R.layout.udfps_bp_view, null))
-            .thenReturn(mock(UdfpsBpView::class.java))
-        whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
-            .thenReturn(mUdfpsKeyguardViewLegacy)
-        whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null))
-            .thenReturn(mock(UdfpsFpmEmptyView::class.java))
     }
 
     private suspend fun withReasonSuspend(
         @RequestReason reason: Int,
         isDebuggable: Boolean = false,
-        enableDeviceEntryUdfpsRefactor: Boolean = false,
         block: suspend () -> Unit,
     ) {
-        withReason(
-            reason,
-            isDebuggable,
-            enableDeviceEntryUdfpsRefactor,
-        )
+        withReason(reason, isDebuggable)
         block()
     }
 
     private fun withReason(
         @RequestReason reason: Int,
         isDebuggable: Boolean = false,
-        enableDeviceEntryUdfpsRefactor: Boolean = false,
         block: () -> Unit = {},
     ) {
-        if (enableDeviceEntryUdfpsRefactor) {
-            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        } else {
-            mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        }
         controllerOverlay =
             UdfpsControllerOverlay(
                 context,
                 inflater,
-                ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
-                        isViewCaptureEnabled = false),
+                ViewCaptureAwareWindowManager(
+                    windowManager,
+                    lazyViewCapture,
+                    isViewCaptureEnabled = false,
+                ),
                 accessibilityManager,
                 statusBarStateController,
                 statusBarKeyguardViewManager,
                 keyguardUpdateMonitor,
                 dialogManager,
                 dumpManager,
-                transitionController,
                 configurationController,
                 keyguardStateController,
                 unlockedScreenOffAnimationController,
@@ -230,117 +200,6 @@
         block()
     }
 
-    @Test fun showUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { showUdfpsOverlay() }
-
-    @Test
-    fun showUdfpsOverlay_keyguard() =
-        withReason(REASON_AUTH_KEYGUARD) {
-            showUdfpsOverlay()
-            verify(mUdfpsKeyguardViewLegacy).updateSensorLocation(eq(overlayParams.sensorBounds))
-        }
-
-    @Test fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() }
-
-    private fun withRotation(@Rotation rotation: Int, block: () -> Unit) {
-        // Sensor that's in the top left corner of the display in natural orientation.
-        val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
-        val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
-        overlayParams =
-            UdfpsOverlayParams(
-                sensorBounds,
-                overlayBounds,
-                DISPLAY_WIDTH,
-                DISPLAY_HEIGHT,
-                scaleFactor = 1f,
-                rotation
-            )
-        block()
-    }
-
-    @Test
-    fun showUdfpsOverlay_withRotation0() =
-        withRotation(Surface.ROTATION_0) {
-            withReason(REASON_AUTH_BP) {
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // ROTATION_0 is the native orientation. Sensor should stay in the top left corner.
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.x).isEqualTo(0)
-                assertThat(lp.y).isEqualTo(0)
-                assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
-                assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
-            }
-        }
-
-    @Test
-    fun showUdfpsOverlay_withRotation180() =
-        withRotation(Surface.ROTATION_180) {
-            withReason(REASON_AUTH_BP) {
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // ROTATION_180 is not supported. Sensor should stay in the top left corner.
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.x).isEqualTo(0)
-                assertThat(lp.y).isEqualTo(0)
-                assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
-                assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
-            }
-        }
-
-    @Test
-    fun showUdfpsOverlay_withRotation90() =
-        withRotation(Surface.ROTATION_90) {
-            withReason(REASON_AUTH_BP) {
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // Sensor should be in the bottom left corner in ROTATION_90.
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.x).isEqualTo(0)
-                assertThat(lp.y).isEqualTo(0)
-                assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
-                assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
-            }
-        }
-
-    @Test
-    fun showUdfpsOverlay_withRotation270() =
-        withRotation(Surface.ROTATION_270) {
-            withReason(REASON_AUTH_BP) {
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // Sensor should be in the top right corner in ROTATION_270.
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.x).isEqualTo(0)
-                assertThat(lp.y).isEqualTo(0)
-                assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
-                assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
-            }
-        }
-
-    @Test
-    fun showUdfpsOverlay_awake() =
-        testScope.runTest {
-            withReason(REASON_AUTH_KEYGUARD) {
-                powerRepository.updateWakefulness(
-                    rawState = WakefulnessState.AWAKE,
-                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
-                    lastSleepReason = WakeSleepReason.OTHER,
-                )
-                runCurrent()
-                controllerOverlay.show(udfpsController, overlayParams)
-                runCurrent()
-                verify(windowManager).addView(any(), any())
-            }
-        }
-
     @Test
     fun showUdfpsOverlay_whileGoingToSleep() =
         testScope.runTest {
@@ -412,91 +271,9 @@
         }
 
     @Test
-    fun showUdfpsOverlay_afterFinishedTransitioningToAOD() =
-        testScope.runTest {
-            withReasonSuspend(REASON_AUTH_KEYGUARD) {
-                keyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.OFF,
-                    to = KeyguardState.GONE,
-                    testScope = this,
-                )
-                powerRepository.updateWakefulness(
-                    rawState = WakefulnessState.STARTING_TO_SLEEP,
-                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
-                    lastSleepReason = WakeSleepReason.OTHER,
-                )
-                runCurrent()
-
-                // WHEN a request comes to show the view
-                controllerOverlay.show(udfpsController, overlayParams)
-                runCurrent()
-
-                // THEN the view does not get added immediately
-                verify(windowManager, never()).addView(any(), any())
-
-                // WHEN the device finishes transitioning to AOD
-                keyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    testScope = this,
-                )
-                runCurrent()
-
-                // THEN the view gets added
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-            }
-        }
-
-    private fun showUdfpsOverlay() {
-        val didShow = controllerOverlay.show(udfpsController, overlayParams)
-
-        verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any())
-        verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
-        verify(udfpsView).animationViewController = any()
-        verify(udfpsView).addView(any())
-
-        assertThat(didShow).isTrue()
-        assertThat(controllerOverlay.isShowing).isTrue()
-        assertThat(controllerOverlay.isHiding).isFalse()
-        assertThat(controllerOverlay.getTouchOverlay()).isNotNull()
-    }
-
-    @Test fun hideUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { hideUdfpsOverlay() }
-
-    @Test fun hideUdfpsOverlay_keyguard() = withReason(REASON_AUTH_KEYGUARD) { hideUdfpsOverlay() }
-
-    @Test fun hideUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { hideUdfpsOverlay() }
-
-    @Test fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() }
-
-    private fun hideUdfpsOverlay() {
-        val didShow = controllerOverlay.show(udfpsController, overlayParams)
-        val view = controllerOverlay.getTouchOverlay()
-        view?.let { whenever(view.parent).thenReturn(mock(ViewGroup::class.java)) }
-        val didHide = controllerOverlay.hide()
-
-        verify(windowManager).removeView(eq(view))
-
-        assertThat(didShow).isTrue()
-        assertThat(didHide).isTrue()
-        assertThat(controllerOverlay.getTouchOverlay()).isNull()
-        assertThat(controllerOverlay.animationViewController).isNull()
-        assertThat(controllerOverlay.isShowing).isFalse()
-        assertThat(controllerOverlay.isHiding).isTrue()
-    }
-
-    @Test
     fun canNotHide() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.hide()).isFalse() }
 
     @Test
-    fun canNotReshow() =
-        withReason(REASON_AUTH_BP) {
-            assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue()
-            assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse()
-        }
-
-    @Test
     fun cancels() =
         withReason(REASON_AUTH_BP) {
             controllerOverlay.cancel()
@@ -504,16 +281,6 @@
         }
 
     @Test
-    fun unconfigureDisplayOnHide() =
-        withReason(REASON_AUTH_BP) {
-            whenever(udfpsView.isDisplayConfigured).thenReturn(true)
-
-            controllerOverlay.show(udfpsController, overlayParams)
-            controllerOverlay.hide()
-            verify(udfpsView).unconfigureDisplay()
-        }
-
-    @Test
     fun matchesRequestIds() =
         withReason(REASON_AUTH_BP) {
             assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue()
@@ -521,29 +288,9 @@
         }
 
     @Test
-    fun smallOverlayOnEnrollmentWithA11y() =
-        withRotation(Surface.ROTATION_0) {
-            withReason(REASON_ENROLL_ENROLLING) {
-                // When a11y enabled during enrollment
-                whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true)
-
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // Layout params should use sensor bounds
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.width).isEqualTo(overlayParams.sensorBounds.width())
-                assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
-            }
-        }
-
-    @Test
     fun addViewPending_layoutIsNotUpdated() =
         testScope.runTest {
             withReasonSuspend(REASON_AUTH_KEYGUARD) {
-                mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-
                 // GIVEN going to sleep
                 keyguardTransitionRepository.sendTransitionSteps(
                     from = KeyguardState.OFF,
@@ -574,26 +321,4 @@
                 controllerOverlay.hide()
             }
         }
-
-    @Test
-    fun updateOverlayParams_viewLayoutUpdated() =
-        testScope.runTest {
-            withReasonSuspend(REASON_AUTH_KEYGUARD) {
-                powerRepository.updateWakefulness(
-                    rawState = WakefulnessState.AWAKE,
-                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
-                    lastSleepReason = WakeSleepReason.OTHER,
-                )
-                runCurrent()
-                controllerOverlay.show(udfpsController, overlayParams)
-                runCurrent()
-                verify(windowManager).addView(any(), any())
-
-                // WHEN updateOverlayParams gets called
-                controllerOverlay.updateOverlayParams(overlayParams)
-
-                // THEN the view layout is updated
-                verify(windowManager).updateViewLayout(any(), any())
-            }
-        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
deleted file mode 100644
index 9fbe096..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.hardware.biometrics.SensorLocationInternal
-import android.testing.TestableLooper
-import android.testing.ViewUtils
-import android.view.LayoutInflater
-import android.view.Surface
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.nullable
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-
-private const val SENSOR_X = 50
-private const val SENSOR_Y = 250
-private const val SENSOR_RADIUS = 10
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-class UdfpsViewTest : SysuiTestCase() {
-
-    @JvmField @Rule
-    var rule = MockitoJUnit.rule()
-
-    @Mock
-    lateinit var hbmProvider: UdfpsDisplayModeProvider
-    @Mock
-    lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
-
-    private lateinit var view: UdfpsView
-
-    @Before
-    fun setup() {
-        context.setTheme(androidx.appcompat.R.style.Theme_AppCompat)
-        view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
-        view.animationViewController = animationViewController
-        val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
-        view.overlayParams = UdfpsOverlayParams(sensorBounds, sensorBounds, 1920,
-            1080, 1f, Surface.ROTATION_0)
-        view.setUdfpsDisplayModeProvider(hbmProvider)
-        ViewUtils.attachView(view)
-    }
-
-    @After
-    fun cleanup() {
-        ViewUtils.detachView(view)
-    }
-
-    // TODO: Add test to verify view is size of screen
-
-    @Test
-    fun startAndStopIllumination() {
-        val onDone: Runnable = mock()
-        view.configureDisplay(onDone)
-
-        val illuminator = withArgCaptor<Runnable> {
-            verify(hbmProvider).enable(capture())
-        }
-
-        assertThat(view.isDisplayConfigured).isTrue()
-        verify(animationViewController).onDisplayConfiguring()
-        verify(animationViewController, never()).onDisplayUnconfigured()
-        verify(onDone, never()).run()
-
-        // fake illumination event
-        illuminator.run()
-        waitForLooper()
-        verify(onDone).run()
-        verify(hbmProvider, never()).disable(any())
-
-        view.unconfigureDisplay()
-        assertThat(view.isDisplayConfigured).isFalse()
-        verify(animationViewController).onDisplayUnconfigured()
-        verify(hbmProvider).disable(nullable(Runnable::class.java))
-    }
-
-    private fun waitForLooper() = TestableLooper.get(this).processAllMessages()
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index b87647e..eb72f29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -48,11 +48,11 @@
 import android.app.ActivityManager;
 import android.content.res.Configuration;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
-import android.testing.UiThreadTest;
+import androidx.test.annotation.UiThreadTest;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -69,7 +69,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @UiThreadTest
 public class DozeMachineTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
index ae2a9ad..6fce108 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
@@ -20,10 +20,10 @@
 import android.content.Context
 import android.service.quicksettings.Tile
 import android.testing.AndroidTestingRunner
-import android.testing.UiThreadTest
 import android.view.ContextThemeWrapper
 import android.view.View
 import android.widget.ImageView
+import androidx.test.annotation.UiThreadTest
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.qs.QSTile
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index 643debf..7b24233 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -17,13 +17,13 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.testing.TestableLooper;
-import android.testing.UiThreadTest;
 import android.view.View;
 import android.view.Window;
 import android.widget.LinearLayout;
 import android.widget.Switch;
 import android.widget.TextView;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index 2d8e692..c8ef663 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -31,12 +31,12 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.testing.AndroidTestingRunner;
-import android.testing.UiThreadTest;
 import android.util.FloatProperty;
 import android.util.Property;
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.app.animation.Interpolators;
@@ -270,4 +270,4 @@
         PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
         assertTrue(PropertyAnimator.isAnimating(mView, mProperty));
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ed7383c..e3e2491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -612,6 +612,8 @@
         Assert.assertNull(child.getParent());
         Assert.assertNull(child.getNotificationParent());
         Assert.assertFalse(child.keepInParentForDismissAnimation());
+        verify(mNotificationTestHelper.getMockLogger())
+                .logCancelAppearDrawing(child.getEntry(), false);
         verifyNoMoreInteractions(mNotificationTestHelper.getMockLogger());
     }
 
@@ -1013,7 +1015,7 @@
         assertThat(row.isHeadsUpAnimatingAway()).isTrue();
 
         // on disappear animation ends
-        row.onAppearAnimationFinished(/* wasAppearing = */ false);
+        row.onAppearAnimationFinished(/* wasAppearing = */ false, /* cancelled = */ false);
         assertThat(row.isHeadsUpAnimatingAway()).isFalse();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 44d81a7..8f64287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -512,10 +512,10 @@
                 mLightBarController,
                 mAutoHideController,
                 new StatusBarInitializerImpl(
-                        mContext.getDisplayId(),
-                        mStatusBarWindowControllerStore,
+                        mStatusBarWindowController,
                         mCollapsedStatusBarFragmentProvider,
-                        emptySet()),
+                        emptySet()
+                ),
                 mStatusBarWindowControllerStore,
                 mStatusBarWindowStateController,
                 new FakeStatusBarModeRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index e57e8d1..15ef917 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,7 +16,6 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS;
 import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
 import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -61,6 +60,7 @@
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.OperatorNameViewController;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
@@ -633,7 +633,7 @@
     @Test
     @EnableFlags({
             FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
-            FLAG_STATUS_BAR_RON_CHIPS,
+            StatusBarNotifChips.FLAG_NAME,
             FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() {
         resumeAndGetFragment();
@@ -660,8 +660,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
         resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -673,7 +673,7 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
         resumeAndGetFragment();
@@ -689,8 +689,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -705,9 +705,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() {
+    public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -724,8 +724,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -740,9 +740,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() {
+    public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -759,8 +759,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
         resumeAndGetFragment();
 
         // Ongoing activity started
@@ -781,9 +781,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() {
+    public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
         resumeAndGetFragment();
 
         // Ongoing activity started
@@ -804,7 +804,7 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void secondaryOngoingActivityEnded_chipHidden() {
         resumeAndGetFragment();
@@ -828,8 +828,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
         fragment.enableAnimationsForTesting();
@@ -846,9 +846,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() {
+    public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
         fragment.enableAnimationsForTesting();
@@ -866,8 +866,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN there *is* an ongoing call via old callback
@@ -898,9 +898,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() {
+    public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN there *is* an ongoing call via old callback
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
index baaf604..703e2d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
@@ -49,8 +49,6 @@
     private val _isAlternateBouncerVisible = MutableStateFlow(false)
     override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
     override var lastAlternateBouncerVisibleTime: Long = 0L
-    private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
-    override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
     override val lastShownSecurityMode: MutableStateFlow<KeyguardSecurityModel.SecurityMode> =
         MutableStateFlow(KeyguardSecurityModel.SecurityMode.Invalid)
     override var bouncerDismissActionModel: BouncerDismissActionModel? = null
@@ -63,10 +61,6 @@
         _isAlternateBouncerVisible.value = isVisible
     }
 
-    override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
-        _isAlternateBouncerUIAvailable.value = isAvailable
-    }
-
     override fun setPrimaryShow(isShowing: Boolean) {
         _primaryBouncerShow.value = isShowing
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
index 63323b2..8bbb8a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -16,33 +16,23 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
-import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.statusbar.policy.keyguardStateController
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.systemClock
 
 val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by
     Kosmos.Fixture {
         AlternateBouncerInteractor(
-            statusBarStateController = statusBarStateController,
-            keyguardStateController = keyguardStateController,
             bouncerRepository = keyguardBouncerRepository,
             fingerprintPropertyRepository = fingerprintPropertyRepository,
-            biometricSettingsRepository = biometricSettingsRepository,
             systemClock = systemClock,
-            keyguardUpdateMonitor = keyguardUpdateMonitor,
             deviceEntryBiometricsAllowedInteractor = { deviceEntryBiometricsAllowedInteractor },
             keyguardInteractor = { keyguardInteractor },
             keyguardTransitionInteractor = { keyguardTransitionInteractor },
@@ -54,21 +44,9 @@
 fun Kosmos.givenCanShowAlternateBouncer() {
     this.givenAlternateBouncerSupported()
     this.keyguardBouncerRepository.setPrimaryShow(false)
-    this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
-    this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
     this.deviceEntryFaceAuthRepository.setLockedOut(false)
-    whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
-    whenever(this.keyguardStateController.isUnlocked).thenReturn(false)
 }
 
 fun Kosmos.givenAlternateBouncerSupported() {
-    if (DeviceEntryUdfpsRefactor.isEnabled) {
-        this.fingerprintPropertyRepository.supportsUdfps()
-    } else {
-        this.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true)
-    }
-}
-
-fun Kosmos.givenCannotShowAlternateBouncer() {
-    this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+    this.fingerprintPropertyRepository.supportsUdfps()
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index c0152b26..41402ba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.flags
 
 import android.platform.test.annotations.EnableFlags
-import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
 import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
@@ -36,7 +35,6 @@
     FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN,
     FLAG_PREDICTIVE_BACK_SYSUI,
     FLAG_SCENE_CONTAINER,
-    FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
 )
 @Retention(AnnotationRetention.RUNTIME)
 @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
index 4ccee6f..2aa2744 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
@@ -22,18 +22,16 @@
 import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 
 val Kosmos.deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor by
     Kosmos.Fixture {
         DeviceEntrySideFpsOverlayInteractor(
-            applicationScope = testScope.backgroundScope,
             context = applicationContext,
             deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
             sceneInteractor = sceneInteractor,
             primaryBouncerInteractor = primaryBouncerInteractor,
             alternateBouncerInteractor = alternateBouncerInteractor,
-            keyguardUpdateMonitor = keyguardUpdateMonitor
+            keyguardUpdateMonitor = keyguardUpdateMonitor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
new file mode 100644
index 0000000..e16756b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices
+
+import com.android.systemui.accessibility.qs.QSAccessibilityModule
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+
+val Kosmos.qsHearingDevicesTileConfig by
+    Kosmos.Fixture { QSAccessibilityModule.provideHearingDevicesTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
similarity index 85%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
index c0d65a0..2316a2f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
 
 import android.content.packageManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.commandline.commandRegistry
 import com.android.systemui.util.time.fakeSystemClock
 
-val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by
+val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by
     Kosmos.Fixture {
-        DemoRonChipViewModel(
+        DemoNotifChipViewModel(
             commandRegistry = commandRegistry,
             packageManager = packageManager,
             systemClock = fakeSystemClock,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
new file mode 100644
index 0000000..af24c37
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.statusbar.chips.notification.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+
+val Kosmos.notifChipsViewModel: NotifChipsViewModel by
+    Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index 5382c1c..0300bf4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -20,7 +20,8 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel
 import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel
 import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
 import com.android.systemui.statusbar.chips.statusBarChipsLogger
@@ -33,7 +34,8 @@
             shareToAppChipViewModel = shareToAppChipViewModel,
             castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel,
             callChipViewModel = callChipViewModel,
-            demoRonChipViewModel = demoRonChipViewModel,
+            notifChipsViewModel = notifChipsViewModel,
+            demoNotifChipViewModel = demoNotifChipViewModel,
             logger = statusBarChipsLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
index edd6604..9fa3abf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
@@ -19,11 +19,18 @@
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import org.mockito.kotlin.mock
 
-class FakeStatusBarInitializer(
-    private val statusBarViewController: PhoneStatusBarViewController,
-    private val statusBarTransitions: PhoneStatusBarTransitions,
-) : StatusBarInitializer {
+class FakeStatusBarInitializer : StatusBarInitializer {
+
+    val statusBarViewController = mock<PhoneStatusBarViewController>()
+    val statusBarTransitions = mock<PhoneStatusBarTransitions>()
+
+    var startedByCoreStartable: Boolean = false
+        private set
+
+    var initializedByCentralSurfaces: Boolean = false
+        private set
 
     override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
         set(value) {
@@ -31,5 +38,11 @@
             value?.onStatusBarViewUpdated(statusBarViewController, statusBarTransitions)
         }
 
-    override fun initializeStatusBar() {}
+    override fun initializeStatusBar() {
+        initializedByCentralSurfaces = true
+    }
+
+    override fun start() {
+        startedByCoreStartable = true
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
index 73ed228..8c218be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
@@ -16,14 +16,11 @@
 
 package com.android.systemui.statusbar.core
 
-import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import com.android.systemui.statusbar.window.StatusBarWindowController
 
-class FakeStatusBarInitializerFactory(
-    private val statusBarViewController: PhoneStatusBarViewController,
-    private val statusBarTransitions: PhoneStatusBarTransitions,
-) : StatusBarInitializer.Factory {
+class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory {
 
-    override fun create(displayId: Int): StatusBarInitializer =
-        FakeStatusBarInitializer(statusBarViewController, statusBarTransitions)
+    override fun create(
+        statusBarWindowController: StatusBarWindowController
+    ): StatusBarInitializer = FakeStatusBarInitializer()
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt
new file mode 100644
index 0000000..0c2cba9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.statusbar.core
+
+import android.view.Display
+
+class FakeStatusBarInitializerStore : StatusBarInitializerStore {
+
+    private val initializers = mutableMapOf<Int, FakeStatusBarInitializer>()
+
+    override val defaultDisplay: FakeStatusBarInitializer
+        get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+    override fun forDisplay(displayId: Int): FakeStatusBarInitializer {
+        return initializers.computeIfAbsent(displayId) { FakeStatusBarInitializer() }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
new file mode 100644
index 0000000..9197dcd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.statusbar.core
+
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
+import kotlinx.coroutines.CoroutineScope
+import org.mockito.kotlin.mock
+
+class FakeStatusBarOrchestratorFactory : StatusBarOrchestrator.Factory {
+
+    private val createdOrchestrators = mutableMapOf<Int, StatusBarOrchestrator>()
+
+    fun createdOrchestratorForDisplay(displayId: Int): StatusBarOrchestrator? =
+        createdOrchestrators[displayId]
+
+    override fun create(
+        displayId: Int,
+        displayScope: CoroutineScope,
+        statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+        statusBarModeRepository: StatusBarModePerDisplayRepository,
+        statusBarInitializer: StatusBarInitializer,
+        statusBarWindowController: StatusBarWindowController,
+    ): StatusBarOrchestrator =
+        mock<StatusBarOrchestrator>().also { createdOrchestrators[displayId] = it }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
index 7ad715b..8066b91 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -19,20 +19,13 @@
 import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.statusbar.phone.phoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.phoneStatusBarViewController
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
 
-val Kosmos.fakeStatusBarInitializer by
-    Kosmos.Fixture {
-        FakeStatusBarInitializer(phoneStatusBarViewController, phoneStatusBarTransitions)
-    }
+val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() }
 
 var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer }
 
-val Kosmos.fakeStatusBarInitializerFactory by
-    Kosmos.Fixture {
-        FakeStatusBarInitializerFactory(phoneStatusBarViewController, phoneStatusBarTransitions)
-    }
+val Kosmos.fakeStatusBarInitializerFactory by Kosmos.Fixture { FakeStatusBarInitializerFactory() }
 
 var Kosmos.statusBarInitializerFactory: StatusBarInitializer.Factory by
     Kosmos.Fixture { fakeStatusBarInitializerFactory }
@@ -43,5 +36,11 @@
             applicationCoroutineScope,
             fakeStatusBarInitializerFactory,
             displayRepository,
+            fakeStatusBarWindowControllerStore,
         )
     }
+
+val Kosmos.fakeStatusBarInitializerStore by Kosmos.Fixture { FakeStatusBarInitializerStore() }
+
+var Kosmos.statusBarInitializerStore: StatusBarInitializerStore by
+    Kosmos.Fixture { fakeStatusBarInitializerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
index 54de293..87f7142 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -16,7 +16,11 @@
 
 package com.android.systemui.statusbar.core
 
+import android.content.testableContext
 import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayScopeRepository
+import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.mockDemoModeController
@@ -24,20 +28,25 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.shade.mockNotificationShadeWindowViewController
 import com.android.systemui.shade.mockShadeSurface
-import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.data.repository.statusBarModeRepository
 import com.android.systemui.statusbar.mockNotificationRemoteInputManager
 import com.android.systemui.statusbar.phone.mockAutoHideController
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
 import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.statusbar.window.statusBarWindowControllerStore
 import com.android.wm.shell.bubbles.bubblesOptional
 
 val Kosmos.statusBarOrchestrator by
     Kosmos.Fixture {
         StatusBarOrchestrator(
+            testableContext.displayId,
             applicationCoroutineScope,
+            fakeStatusBarWindowStatePerDisplayRepository,
+            fakeStatusBarModePerDisplayRepository,
             fakeStatusBarInitializer,
-            fakeStatusBarModeRepository,
-            fakeStatusBarWindowControllerStore,
+            fakeStatusBarWindowController,
             mockDemoModeController,
             mockPluginDependencyProvider,
             mockAutoHideController,
@@ -45,8 +54,28 @@
             { mockNotificationShadeWindowViewController },
             mockShadeSurface,
             bubblesOptional,
-            statusBarWindowStateRepositoryStore,
+            dumpManager,
             powerInteractor,
             primaryBouncerInteractor,
         )
     }
+
+val Kosmos.fakeStatusBarOrchestratorFactory by Kosmos.Fixture { FakeStatusBarOrchestratorFactory() }
+
+var Kosmos.statusBarOrchestratorFactory: StatusBarOrchestrator.Factory by
+    Kosmos.Fixture { fakeStatusBarOrchestratorFactory }
+
+val Kosmos.multiDisplayStatusBarStarter by
+    Kosmos.Fixture {
+        MultiDisplayStatusBarStarter(
+            applicationCoroutineScope,
+            displayScopeRepository,
+            statusBarOrchestratorFactory,
+            statusBarWindowStateRepositoryStore,
+            statusBarModeRepository,
+            displayRepository,
+            statusBarInitializerStore,
+            statusBarWindowControllerStore,
+            statusBarInitializerStore,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 6069083..285cebb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.data.model.StatusBarAppearance
 import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.google.common.truth.Truth.assertThat
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
@@ -37,7 +36,6 @@
         FakeStatusBarModePerDisplayRepository()
 
     override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository {
-        assertThat(displayId).isEqualTo(DISPLAY_ID)
         return defaultDisplay
     }
 }
@@ -51,6 +49,7 @@
     override fun showTransient() {
         isTransientShown.value = true
     }
+
     override fun clearTransient() {
         isTransientShown.value = false
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
index 0f2b477..12db2f741 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
@@ -18,6 +18,9 @@
 
 import com.android.systemui.kosmos.Kosmos
 
+val Kosmos.fakeStatusBarModePerDisplayRepository by
+    Kosmos.Fixture { FakeStatusBarModePerDisplayRepository() }
+
 val Kosmos.statusBarModeRepository: StatusBarModeRepositoryStore by
     Kosmos.Fixture { fakeStatusBarModeRepository }
 val Kosmos.fakeStatusBarModeRepository by Kosmos.Fixture { FakeStatusBarModeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index a7a6195..7f4c670 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -70,6 +70,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
 import com.android.systemui.statusbar.notification.row.icon.AppIconProviderImpl
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderImpl
 import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
@@ -287,7 +288,10 @@
             BigPictureLayoutInflaterFactory(),
             NotificationOptimizedLinearLayoutFactory(),
             { Mockito.mock(NotificationViewFlipperFactory::class.java) },
-            NotificationRowIconViewInflaterFactory(AppIconProviderImpl(context)),
+            NotificationRowIconViewInflaterFactory(
+                AppIconProviderImpl(context),
+                NotificationIconStyleProviderImpl(),
+            ),
         )
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
new file mode 100644
index 0000000..611c90a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.statusbar.notification.row.icon
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.notificationIconStyleProvider by Kosmos.Fixture { NotificationIconStyleProviderImpl() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
new file mode 100644
index 0000000..14f4d75
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.statusbar.policy
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeBluetoothController by Kosmos.Fixture { FakeBluetoothController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt
new file mode 100644
index 0000000..4876cd8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.statusbar.policy
+
+import android.bluetooth.BluetoothAdapter
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.statusbar.policy.BluetoothController.Callback
+import java.io.PrintWriter
+import java.util.Collections
+import java.util.concurrent.Executor
+
+class FakeBluetoothController : BluetoothController {
+
+    private var callbacks = mutableListOf<Callback>()
+    private var enabled = false
+
+    override fun addCallback(listener: Callback) {
+        callbacks += listener
+        listener.onBluetoothStateChange(isBluetoothEnabled)
+    }
+
+    override fun removeCallback(listener: Callback) {
+        callbacks -= listener
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {}
+
+    override fun isBluetoothSupported(): Boolean = false
+
+    override fun isBluetoothEnabled(): Boolean = enabled
+
+    override fun getBluetoothState(): Int = 0
+
+    override fun isBluetoothConnected(): Boolean = false
+
+    override fun isBluetoothConnecting(): Boolean = false
+
+    override fun isBluetoothAudioProfileOnly(): Boolean = false
+
+    override fun isBluetoothAudioActive(): Boolean = false
+
+    override fun getConnectedDeviceName(): String? = null
+
+    override fun setBluetoothEnabled(enabled: Boolean) {
+        this.enabled = enabled
+        callbacks.forEach { it.onBluetoothStateChange(enabled) }
+    }
+
+    override fun canConfigBluetooth(): Boolean = false
+
+    override fun getConnectedDevices(): MutableList<CachedBluetoothDevice> = Collections.emptyList()
+
+    override fun addOnMetadataChangedListener(
+        device: CachedBluetoothDevice?,
+        executor: Executor?,
+        listener: BluetoothAdapter.OnMetadataChangedListener?,
+    ) {}
+
+    override fun removeOnMetadataChangedListener(
+        device: CachedBluetoothDevice?,
+        listener: BluetoothAdapter.OnMetadataChangedListener?,
+    ) {}
+
+    /** Trigger the [Callback.onBluetoothDevicesChanged] method for all registered callbacks. */
+    @VisibleForTesting
+    fun onBluetoothDevicesChanged() {
+        callbacks.forEach { it.onBluetoothDevicesChanged() }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
index 2205a3b..cbaf2bd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
@@ -21,6 +21,9 @@
 import com.android.systemui.settings.displayTracker
 import com.android.systemui.statusbar.commandQueue
 
+val Kosmos.fakeStatusBarWindowStatePerDisplayRepository by
+    Kosmos.Fixture { FakeStatusBarWindowStatePerDisplayRepository() }
+
 val Kosmos.fakeStatusBarWindowStateRepositoryStore by
     Kosmos.Fixture { FakeStatusBarWindowStateRepositoryStore() }
 
diff --git a/packages/Vcn/OWNERS b/packages/Vcn/OWNERS
new file mode 100644
index 0000000..ff2146e
--- /dev/null
+++ b/packages/Vcn/OWNERS
@@ -0,0 +1,6 @@
+evitayan@google.com
+nharold@google.com
+benedictwong@google.com #{LAST_RESORT_SUGGESTION}
+yangji@google.com #{LAST_RESORT_SUGGESTION}
+
+include platform/packages/modules/common:/MODULES_OWNERS
\ No newline at end of file
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index bfa801f..8e884bc 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -372,6 +372,7 @@
 android_ravenwood_libgroup {
     name: "ravenwood-runtime",
     data: [
+        ":system-build.prop",
         ":framework-res",
         ":ravenwood-empty-res",
         ":framework-platform-compat-config",
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 5894476..24950e6 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -20,12 +20,14 @@
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath;
 
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.ResourcesManager;
@@ -81,6 +83,8 @@
     private static final String MAIN_THREAD_NAME = "RavenwoodMain";
     private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
     private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
+    private static final String RAVENWOOD_BUILD_PROP =
+            getRavenwoodRuntimePath() + "ravenwood-data/build.prop";
 
     /**
      * When enabled, attempt to dump all thread stacks just before we hit the
@@ -158,7 +162,8 @@
         System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
 
         // Do the basic set up for the android sysprops.
-        setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
+        RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP);
+        setSystemProperties(null);
 
         // Make sure libandroid_runtime is loaded.
         RavenwoodNativeLoader.loadFrameworkNativeCode();
@@ -329,7 +334,7 @@
         LocalServices.removeAllServicesForTest();
         ServiceManager.reset$ravenwood();
 
-        setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
+        setSystemProperties(null);
         if (sOriginalIdentityToken != -1) {
             Binder.restoreCallingIdentity(sOriginalIdentityToken);
         }
@@ -388,9 +393,10 @@
     /**
      * Set the current configuration to the actual SystemProperties.
      */
-    private static void setSystemProperties(RavenwoodSystemProperties systemProperties) {
+    private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
         SystemProperties.clearChangeCallbacksForTest();
         RavenwoodRuntimeNative.clearSystemProperties();
+        if (systemProperties == null) systemProperties = new RavenwoodSystemProperties();
         sProps = new RavenwoodSystemProperties(systemProperties, true);
         for (var entry : systemProperties.getValues().entrySet()) {
             RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue());
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index f1e1ef6..ced1519 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -18,12 +18,94 @@
 
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP;
 
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
 public class RavenwoodSystemProperties {
+    private static final String TAG = "RavenwoodSystemProperties";
+
+    private static final Map<String, String> sDefaultValues = new HashMap<>();
+
+    private static final String[] PARTITIONS = {
+            "bootimage",
+            "odm",
+            "product",
+            "system",
+            "system_ext",
+            "vendor",
+            "vendor_dlkm",
+    };
+
+    /**
+     * More info about property file loading: system/core/init/property_service.cpp
+     * In the following logic, the only partition we would need to consider is "system",
+     * since we only read from system-build.prop
+     */
+    static void initialize(String propFile) {
+        // Load all properties from build.prop
+        try {
+            Files.readAllLines(Path.of(propFile)).stream()
+                    .map(String::trim)
+                    .filter(s -> !s.startsWith("#"))
+                    .map(s -> s.split("\\s*=\\s*", 2))
+                    .filter(a -> a.length == 2)
+                    .forEach(a -> sDefaultValues.put(a[0], a[1]));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        // If ro.product.${name} is not set, derive from ro.product.${partition}.${name}
+        // If ro.product.cpu.abilist* is not set, derive from ro.${partition}.product.cpu.abilist*
+        for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
+            final String key;
+            if (entry.getKey().startsWith("ro.product.system.")) {
+                var name = entry.getKey().substring(18);
+                key = "ro.product." + name;
+
+            } else if (entry.getKey().startsWith("ro.system.product.cpu.abilist")) {
+                var name = entry.getKey().substring(22);
+                key = "ro.product.cpu." + name;
+            } else {
+                continue;
+            }
+            if (!sDefaultValues.containsKey(key)) {
+                sDefaultValues.put(key, entry.getValue());
+            }
+        }
+
+        // Some other custom values
+        sDefaultValues.put("ro.board.first_api_level", "1");
+        sDefaultValues.put("ro.product.first_api_level", "1");
+        sDefaultValues.put("ro.soc.manufacturer", "Android");
+        sDefaultValues.put("ro.soc.model", "Ravenwood");
+        sDefaultValues.put(RAVENWOOD_SYSPROP, "1");
+
+        // Log all values
+        sDefaultValues.forEach((key, value) -> RavenwoodCommonUtils.log(TAG, key + "=" + value));
+
+        // Copy ro.product.* and ro.build.* to all partitions, just in case
+        // We don't want to log these because these are just a lot of duplicate values
+        for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
+            var key = entry.getKey();
+            if (key.startsWith("ro.product.") || key.startsWith("ro.build.")) {
+                var name = key.substring(3);
+                for (String partition : PARTITIONS) {
+                    var newKey = "ro." + partition + "." + name;
+                    if (!sDefaultValues.containsKey(newKey)) {
+                        sDefaultValues.put(newKey, entry.getValue());
+                    }
+                }
+            }
+        }
+    }
+
     private volatile boolean mIsImmutable;
 
     private final Map<String, String> mValues = new HashMap<>();
@@ -35,47 +117,15 @@
     private final Set<String> mKeyWritable = new HashSet<>();
 
     public RavenwoodSystemProperties() {
-        // TODO: load these values from build.prop generated files
-        setValueForPartitions("product.brand", "Android");
-        setValueForPartitions("product.device", "Ravenwood");
-        setValueForPartitions("product.manufacturer", "Android");
-        setValueForPartitions("product.model", "Ravenwood");
-        setValueForPartitions("product.name", "Ravenwood");
-
-        setValueForPartitions("product.cpu.abilist", "x86_64");
-        setValueForPartitions("product.cpu.abilist32", "");
-        setValueForPartitions("product.cpu.abilist64", "x86_64");
-
-        setValueForPartitions("build.date", "Thu Jan 01 00:00:00 GMT 2024");
-        setValueForPartitions("build.date.utc", "1704092400");
-        setValueForPartitions("build.id", "MAIN");
-        setValueForPartitions("build.tags", "dev-keys");
-        setValueForPartitions("build.type", "userdebug");
-        setValueForPartitions("build.version.all_codenames", "REL");
-        setValueForPartitions("build.version.codename", "REL");
-        setValueForPartitions("build.version.incremental", "userdebug.ravenwood.20240101");
-        setValueForPartitions("build.version.known_codenames", "REL");
-        setValueForPartitions("build.version.release", "14");
-        setValueForPartitions("build.version.release_or_codename", "VanillaIceCream");
-        setValueForPartitions("build.version.sdk", "34");
-
-        setValue("ro.board.first_api_level", "1");
-        setValue("ro.product.first_api_level", "1");
-
-        setValue("ro.soc.manufacturer", "Android");
-        setValue("ro.soc.model", "Ravenwood");
-
-        setValue("ro.debuggable", "1");
-
-        setValue(RAVENWOOD_SYSPROP, "1");
+        mValues.putAll(sDefaultValues);
     }
 
     /** Copy constructor */
     public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) {
-        this.mKeyReadable.addAll(source.mKeyReadable);
-        this.mKeyWritable.addAll(source.mKeyWritable);
-        this.mValues.putAll(source.mValues);
-        this.mIsImmutable = immutable;
+        mKeyReadable.addAll(source.mKeyReadable);
+        mKeyWritable.addAll(source.mKeyWritable);
+        mValues.putAll(source.mValues);
+        mIsImmutable = immutable;
     }
 
     public Map<String, String> getValues() {
@@ -123,36 +173,12 @@
         return mKeyWritable.contains(key);
     }
 
-    private static final String[] PARTITIONS = {
-            "bootimage",
-            "odm",
-            "product",
-            "system",
-            "system_ext",
-            "vendor",
-            "vendor_dlkm",
-    };
-
     private void ensureNotImmutable() {
         if (mIsImmutable) {
             throw new RuntimeException("Unable to update immutable instance");
         }
     }
 
-    /**
-     * Set the given property for all possible partitions where it could be defined. For
-     * example, the value of {@code ro.build.type} is typically also mirrored under
-     * {@code ro.system.build.type}, etc.
-     */
-    private void setValueForPartitions(String key, String value) {
-        ensureNotImmutable();
-
-        setValue("ro." + key, value);
-        for (String partition : PARTITIONS) {
-            setValue("ro." + partition + "." + key, value);
-        }
-    }
-
     public void setValue(String key, Object value) {
         ensureNotImmutable();
 
@@ -195,11 +221,4 @@
             return key;
         }
     }
-
-    /**
-     * Return an immutable, default instance.
-     */
-    // Create a default instance, and make an immutable copy of it.
-    public static final RavenwoodSystemProperties DEFAULT_VALUES =
-            new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true);
 }
diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/tests/minimum-test/Android.bp
similarity index 100%
rename from ravenwood/minimum-test/Android.bp
rename to ravenwood/tests/minimum-test/Android.bp
diff --git a/ravenwood/minimum-test/README.md b/ravenwood/tests/minimum-test/README.md
similarity index 100%
rename from ravenwood/minimum-test/README.md
rename to ravenwood/tests/minimum-test/README.md
diff --git a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
similarity index 100%
rename from ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
rename to ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
diff --git a/ravenwood/mockito/Android.bp b/ravenwood/tests/mockito/Android.bp
similarity index 100%
rename from ravenwood/mockito/Android.bp
rename to ravenwood/tests/mockito/Android.bp
diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/tests/mockito/AndroidManifest.xml
similarity index 100%
rename from ravenwood/mockito/AndroidManifest.xml
rename to ravenwood/tests/mockito/AndroidManifest.xml
diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/tests/mockito/AndroidTest.xml
similarity index 100%
rename from ravenwood/mockito/AndroidTest.xml
rename to ravenwood/tests/mockito/AndroidTest.xml
diff --git a/ravenwood/mockito/README.md b/ravenwood/tests/mockito/README.md
similarity index 100%
rename from ravenwood/mockito/README.md
rename to ravenwood/tests/mockito/README.md
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
similarity index 100%
rename from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
rename to ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
similarity index 100%
rename from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
rename to ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
similarity index 100%
rename from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
rename to ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
diff --git a/ravenwood/resapk_test/Android.bp b/ravenwood/tests/resapk_test/Android.bp
similarity index 100%
rename from ravenwood/resapk_test/Android.bp
rename to ravenwood/tests/resapk_test/Android.bp
diff --git a/ravenwood/resapk_test/apk/Android.bp b/ravenwood/tests/resapk_test/apk/Android.bp
similarity index 100%
rename from ravenwood/resapk_test/apk/Android.bp
rename to ravenwood/tests/resapk_test/apk/Android.bp
diff --git a/ravenwood/resapk_test/apk/AndroidManifest.xml b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml
similarity index 100%
rename from ravenwood/resapk_test/apk/AndroidManifest.xml
rename to ravenwood/tests/resapk_test/apk/AndroidManifest.xml
diff --git a/ravenwood/resapk_test/apk/res/values/strings.xml b/ravenwood/tests/resapk_test/apk/res/values/strings.xml
similarity index 100%
rename from ravenwood/resapk_test/apk/res/values/strings.xml
rename to ravenwood/tests/resapk_test/apk/res/values/strings.xml
diff --git a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
similarity index 100%
rename from ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
rename to ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
diff --git a/ravenwood/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp
similarity index 100%
rename from ravenwood/runtime-test/Android.bp
rename to ravenwood/tests/runtime-test/Android.bp
diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
similarity index 100%
rename from ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
rename to ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
similarity index 100%
rename from ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
rename to ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
diff --git a/ravenwood/services-test/Android.bp b/ravenwood/tests/services-test/Android.bp
similarity index 100%
rename from ravenwood/services-test/Android.bp
rename to ravenwood/tests/services-test/Android.bp
diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
similarity index 100%
rename from ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
rename to ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
similarity index 100%
rename from ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
rename to ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
diff --git a/ravenwood/tools/hoststubgen/.gitignore b/ravenwood/tools/hoststubgen/.gitignore
new file mode 100644
index 0000000..82158c9
--- /dev/null
+++ b/ravenwood/tools/hoststubgen/.gitignore
@@ -0,0 +1,4 @@
+framework-all-stub-out
+out/
+*-out/
+*.log
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/ravenwood/tools/hoststubgen/Android.bp
similarity index 100%
rename from tools/hoststubgen/hoststubgen/Android.bp
rename to ravenwood/tools/hoststubgen/Android.bp
diff --git a/tools/hoststubgen/README.md b/ravenwood/tools/hoststubgen/README.md
similarity index 84%
rename from tools/hoststubgen/README.md
rename to ravenwood/tools/hoststubgen/README.md
index 1a895dc..615e767 100644
--- a/tools/hoststubgen/README.md
+++ b/ravenwood/tools/hoststubgen/README.md
@@ -11,7 +11,7 @@
 
 - HostStubGen itself is design to be agnostic to Android. It doesn't use any Android APIs
 (hidden or not). But it may use Android specific knowledge -- e.g. as of now,
-AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes. 
+AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes.
 
 - `test-tiny-framework/` contains basic tests that are agnostic to Android.
 
@@ -20,19 +20,16 @@
 
 ## Directories and files
 
-- `hoststubgen/`
-  Contains source code of the "hoststubgen" tool and relevant code
+- `src/`
 
-  - `src/`
+  HostStubGen tool source code.
 
-    HostStubGen tool source code.
+- `annotations-src/` See `Android.bp`.
+- `helper-framework-buildtime-src/` See `Android.bp`.
+- `helper-framework-runtime-src/` See `Android.bp`.
+- `helper-runtime-src/` See `Android.bp`.
 
-  - `annotations-src/` See `Android.bp`.
-  - `helper-framework-buildtime-src/` See `Android.bp`.
-  - `helper-framework-runtime-src/` See `Android.bp`.
-  - `helper-runtime-src/` See `Android.bp`.
-
-  - `test-tiny-framework/` See `README.md` in it.
+- `test-tiny-framework/` See `README.md` in it.
 
 - `scripts`
   - `dump-jar.sh`
@@ -78,4 +75,4 @@
 
 - At some point, we can move or delete all Android specific code to `frameworks/base/ravenwood`.
   - `helper-framework-*-src` should be moved to `frameworks/base/ravenwood`
-  - `test-framework` should be deleted.
\ No newline at end of file
+  - `test-framework` should be deleted.
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
diff --git a/tools/hoststubgen/common.sh b/ravenwood/tools/hoststubgen/common.sh
similarity index 100%
rename from tools/hoststubgen/common.sh
rename to ravenwood/tools/hoststubgen/common.sh
diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/ravenwood/tools/hoststubgen/framework-policy-override.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/framework-policy-override.txt
rename to ravenwood/tools/hoststubgen/framework-policy-override.txt
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
rename to ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
diff --git a/tools/hoststubgen/hoststubgen/invoketest/Android.bp b/ravenwood/tools/hoststubgen/invoketest/Android.bp
similarity index 100%
rename from tools/hoststubgen/hoststubgen/invoketest/Android.bp
rename to ravenwood/tools/hoststubgen/invoketest/Android.bp
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh
similarity index 100%
rename from tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
rename to ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh
diff --git a/tools/hoststubgen/hoststubgen/jarjar-rules.txt b/ravenwood/tools/hoststubgen/jarjar-rules.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/jarjar-rules.txt
rename to ravenwood/tools/hoststubgen/jarjar-rules.txt
diff --git a/tools/hoststubgen/scripts/Android.bp b/ravenwood/tools/hoststubgen/scripts/Android.bp
similarity index 100%
rename from tools/hoststubgen/scripts/Android.bp
rename to ravenwood/tools/hoststubgen/scripts/Android.bp
diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh b/ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
similarity index 100%
rename from tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
rename to ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
diff --git a/tools/hoststubgen/scripts/dump-jar b/ravenwood/tools/hoststubgen/scripts/dump-jar
similarity index 100%
rename from tools/hoststubgen/scripts/dump-jar
rename to ravenwood/tools/hoststubgen/scripts/dump-jar
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp
similarity index 98%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
rename to ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp
index ba2c869..1570549 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp
@@ -16,7 +16,7 @@
     static_libs: [
         "hoststubgen-annotations",
     ],
-    visibility: ["//frameworks/base/tools/hoststubgen:__subpackages__"],
+    visibility: ["//frameworks/base/ravenwood/tools/hoststubgen:__subpackages__"],
 }
 
 // Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules.
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml b/ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml
rename to ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/README.md
rename to ravenwood/tools/hoststubgen/test-tiny-framework/README.md
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
rename to ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
rename to ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
rename to ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
rename to ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
rename to ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
rename to ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 5d57408..89f14b0 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -68,7 +68,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
+import java.util.WeakHashMap;
 import java.util.concurrent.CompletionException;
 import java.util.concurrent.Executor;
 
@@ -81,7 +84,8 @@
     private final ServiceHelper mInternalServiceHelper;
     private final ServiceConfig mServiceConfig;
     private final Context mContext;
-    private final Object mLock = new Object();
+    private final Map<String, Object> mLocks = new WeakHashMap<>();
+
 
     public AppFunctionManagerServiceImpl(@NonNull Context context) {
         this(
@@ -225,12 +229,6 @@
                 .thenAccept(
                         canExecute -> {
                             if (!canExecute) {
-                                safeExecuteAppFunctionCallback.onResult(
-                                        ExecuteAppFunctionResponse.newFailure(
-                                                ExecuteAppFunctionResponse.RESULT_DENIED,
-                                                "Caller does not have permission to execute the"
-                                                        + " appfunction",
-                                                /* extras= */ null));
                                 throw new SecurityException(
                                         "Caller does not have permission to execute the"
                                                 + " appfunction");
@@ -322,9 +320,7 @@
         THREAD_POOL_EXECUTOR.execute(
                 () -> {
                     try {
-                        // TODO(357551503): Instead of holding a global lock, hold a per-package
-                        //  lock.
-                        synchronized (mLock) {
+                        synchronized (getLockForPackage(callingPackage)) {
                             setAppFunctionEnabledInternalLocked(
                                     callingPackage, functionIdentifier, userHandle, enabledState);
                         }
@@ -352,7 +348,7 @@
      * process.
      */
     @WorkerThread
-    @GuardedBy("mLock")
+    @GuardedBy("getLockForPackage(callingPackage)")
     private void setAppFunctionEnabledInternalLocked(
             @NonNull String callingPackage,
             @NonNull String functionIdentifier,
@@ -547,6 +543,26 @@
                                     });
         }
     }
+    /**
+     * Retrieves the lock object associated with the given package name.
+     *
+     * This method returns the lock object from the {@code mLocks} map if it exists.
+     * If no lock is found for the given package name, a new lock object is created,
+     * stored in the map, and returned.
+     */
+    @VisibleForTesting
+    @NonNull
+    Object getLockForPackage(String callingPackage) {
+        // Synchronized the access to mLocks to prevent race condition.
+        synchronized (mLocks) {
+            // By using a WeakHashMap, we allow the garbage collector to reclaim memory by removing
+            // entries associated with unused callingPackage keys. Therefore, we remove the null
+            // values before getting/computing a new value. The goal is to not let the size of this
+            // map grow without an upper bound.
+            mLocks.values().removeAll(Collections.singleton(null)); // Remove null values
+            return mLocks.computeIfAbsent(callingPackage, k -> new Object());
+        }
+    }
 
     private static class AppFunctionMetadataObserver implements ObserverCallback {
         @Nullable private final MetadataSyncAdapter mPerUserMetadataSyncAdapter;
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 504137a..6a1e319 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -46,6 +46,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.RoSystemFeatures;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.build.UnboundedSdkLevel;
 import com.android.server.pm.permission.PermissionAllowlist;
@@ -212,6 +213,30 @@
         }
     }
 
+    /**
+     * Utility class for testing interaction with compile-time defined system features.
+     * @hide
+    */
+    @VisibleForTesting
+    public static class Injector {
+        /** Whether a system feature is defined as enabled and available at compile-time. */
+        public boolean isReadOnlySystemEnabledFeature(String featureName, int version) {
+            return Boolean.TRUE.equals(RoSystemFeatures.maybeHasFeature(featureName, version));
+        }
+
+        /** Whether a system feature is defined as disabled and unavailable at compile-time. */
+        public boolean isReadOnlySystemDisabledFeature(String featureName, int version) {
+            return Boolean.FALSE.equals(RoSystemFeatures.maybeHasFeature(featureName, version));
+        }
+
+        /** The full set of system features defined as compile-time enabled and available. */
+        public ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+            return RoSystemFeatures.getReadOnlySystemEnabledFeatures();
+        }
+    }
+
+    private final Injector mInjector;
+
     // These are the built-in shared libraries that were read from the
     // system configuration files. Keys are the library names; values are
     // the individual entries that contain information such as filename
@@ -220,7 +245,7 @@
 
     // These are the features this devices supports that were read from the
     // system configuration files.
-    final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
+    final ArrayMap<String, FeatureInfo> mAvailableFeatures;
 
     // These are the features which this device doesn't support; the OEM
     // partition uses these to opt-out of features from the system image.
@@ -602,12 +627,26 @@
     public ArrayMap<String, Integer> getOemDefinedUids() {
         return mOemDefinedUids;
     }
+
     /**
      * Only use for testing. Do NOT use in production code.
      * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
      */
     @VisibleForTesting
     public SystemConfig(boolean readPermissions) {
+        this(readPermissions, new Injector());
+    }
+
+    /**
+     * Only use for testing. Do NOT use in production code.
+     * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
+     * @param injector Additional dependency injection for testing.
+     */
+    @VisibleForTesting
+    public SystemConfig(boolean readPermissions, Injector injector) {
+        mInjector = injector;
+        mAvailableFeatures = mInjector.getReadOnlySystemEnabledFeatures();
+
         if (readPermissions) {
             Slog.w(TAG, "Constructing a test SystemConfig");
             readAllPermissions();
@@ -617,6 +656,9 @@
     }
 
     SystemConfig() {
+        mInjector = new Injector();
+        mAvailableFeatures = mInjector.getReadOnlySystemEnabledFeatures();
+
         TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
         log.traceBegin("readAllPermissions");
         try {
@@ -1775,6 +1817,10 @@
     }
 
     private void addFeature(String name, int version) {
+        if (mInjector.isReadOnlySystemDisabledFeature(name, version)) {
+            Slog.w(TAG, "Skipping feature addition for compile-time disabled feature: " + name);
+            return;
+        }
         FeatureInfo fi = mAvailableFeatures.get(name);
         if (fi == null) {
             fi = new FeatureInfo();
@@ -1787,6 +1833,10 @@
     }
 
     private void removeFeature(String name) {
+        if (mInjector.isReadOnlySystemEnabledFeature(name, /*version=*/0)) {
+            Slog.w(TAG, "Skipping feature removal for compile-time enabled feature: " + name);
+            return;
+        }
         if (mAvailableFeatures.remove(name) != null) {
             Slog.d(TAG, "Removed unavailable feature " + name);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7f1d912..379522f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12371,7 +12371,7 @@
                         continue;
                     }
                     endTime = SystemClock.currentThreadTimeMillis();
-                    hasSwapPss = mi.hasSwappedOutPss;
+                    hasSwapPss = hasSwapPss || mi.hasSwappedOutPss;
                     memtrackGraphics = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS);
                     memtrackGl = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GL);
                 } else {
@@ -13049,7 +13049,7 @@
                     continue;
                 }
                 endTime = SystemClock.currentThreadTimeMillis();
-                hasSwapPss = mi.hasSwappedOutPss;
+                hasSwapPss = hasSwapPss || mi.hasSwappedOutPss;
             } else {
                 reportType = ProcessStats.ADD_PSS_EXTERNAL;
                 startTime = SystemClock.currentThreadTimeMillis();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 5236b03..7831c39 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -813,10 +813,8 @@
     private final Object mProcessChangeLock = new Object();
 
     /**
-     * All of the applications we currently have running organized by name.
-     * The keys are strings of the application package name (as
-     * returned by the package manager), and the keys are ApplicationRecord
-     * objects.
+     * All of the processes that are running organized by name.
+     * The keys are process names and the values are the associated ProcessRecord objects.
      */
     @CompositeRWLock({"mService", "mProcLock"})
     private final MyProcessMap mProcessNames = new MyProcessMap();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 939aad4..83044c2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1536,7 +1536,16 @@
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-        return settings.getMethodMap().get(settings.getSelectedInputMethod());
+        final String selectedImeId;
+        if (Flags.consistentGetCurrentInputMethodInfo()) {
+            final var bindingController = getInputMethodBindingController(userId);
+            synchronized (ImfLock.class) {
+                selectedImeId = bindingController.getSelectedMethodId();
+            }
+        } else {
+            selectedImeId = settings.getSelectedInputMethod();
+        }
+        return settings.getMethodMap().get(selectedImeId);
     }
 
     @BinderThread
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index d1d5d48..27bc1cf 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -677,24 +677,15 @@
             @NonNull IMediaRouter2Manager manager,
             int requestId,
             @NonNull RoutingSessionInfo oldSession,
-            @NonNull MediaRoute2Info route,
-            @NonNull UserHandle transferInitiatorUserHandle,
-            @NonNull String transferInitiatorPackageName) {
+            @NonNull MediaRoute2Info route) {
         Objects.requireNonNull(manager, "manager must not be null");
         Objects.requireNonNull(oldSession, "oldSession must not be null");
         Objects.requireNonNull(route, "route must not be null");
-        Objects.requireNonNull(transferInitiatorUserHandle);
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestCreateSessionWithManagerLocked(
-                        requestId,
-                        manager,
-                        oldSession,
-                        route,
-                        transferInitiatorUserHandle,
-                        transferInitiatorPackageName);
+                requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -1738,9 +1729,7 @@
             int requestId,
             @NonNull IMediaRouter2Manager manager,
             @NonNull RoutingSessionInfo oldSession,
-            @NonNull MediaRoute2Info route,
-            @NonNull UserHandle transferInitiatorUserHandle,
-            @NonNull String transferInitiatorPackageName) {
+            @NonNull MediaRoute2Info route) {
         ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
         if (managerRecord == null) {
             return;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 363b8e4..68e195d 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -607,16 +607,8 @@
             IMediaRouter2Manager manager,
             int requestId,
             RoutingSessionInfo oldSession,
-            MediaRoute2Info route,
-            UserHandle transferInitiatorUserHandle,
-            String transferInitiatorPackageName) {
-        mService2.requestCreateSessionWithManager(
-                manager,
-                requestId,
-                oldSession,
-                route,
-                transferInitiatorUserHandle,
-                transferInitiatorPackageName);
+            MediaRoute2Info route) {
+        mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a6f4c0e..2a3be1e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3100,11 +3100,16 @@
         }
 
         synchronized (mUidRulesFirstLock) {
-            final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
-            policy |= oldPolicy;
-            if (oldPolicy != policy) {
-                setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
-                mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+                policy |= oldPolicy;
+                if (oldPolicy != policy) {
+                    setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+                    mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
     }
@@ -3119,11 +3124,16 @@
         }
 
         synchronized (mUidRulesFirstLock) {
-            final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
-            policy = oldPolicy & ~policy;
-            if (oldPolicy != policy) {
-                setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
-                mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+                policy = oldPolicy & ~policy;
+                if (oldPolicy != policy) {
+                    setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+                    mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pinner/PinnerService.java b/services/core/java/com/android/server/pinner/PinnerService.java
index d7ac520..2c75926 100644
--- a/services/core/java/com/android/server/pinner/PinnerService.java
+++ b/services/core/java/com/android/server/pinner/PinnerService.java
@@ -121,9 +121,6 @@
     private static boolean PROP_PIN_PINLIST =
             SystemProperties.getBoolean("pinner.use_pinlist", true);
 
-    private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
-    private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app.
-
     public static final String ANON_REGION_STAT_NAME = "[anon]";
 
     private static final String SYSTEM_GROUP_NAME = "system";
@@ -179,8 +176,10 @@
 
     // Resource-configured pinner flags;
     private final boolean mConfiguredToPinCamera;
+    private final int mConfiguredCameraPinBytes;
     private final int mConfiguredHomePinBytes;
     private final boolean mConfiguredToPinAssistant;
+    private final int mConfiguredAssistantPinBytes;
     private final int mConfiguredWebviewPinBytes;
 
     // This is the percentage of total device memory that will be used to set the global quota.
@@ -250,6 +249,10 @@
         mDeviceConfigInterface = mInjector.getDeviceConfigInterface();
         mConfiguredToPinCamera = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerCameraApp);
+        mConfiguredCameraPinBytes = context.getResources().getInteger(
+                com.android.internal.R.integer.config_pinnerCameraPinBytes);
+        mConfiguredAssistantPinBytes = context.getResources().getInteger(
+                com.android.internal.R.integer.config_pinnerAssistantPinBytes);
         mConfiguredHomePinBytes = context.getResources().getInteger(
                 com.android.internal.R.integer.config_pinnerHomePinBytes);
         mConfiguredToPinAssistant = context.getResources().getBoolean(
@@ -812,11 +815,11 @@
     private int getSizeLimitForKey(@AppKey int key) {
         switch (key) {
             case KEY_CAMERA:
-                return MAX_CAMERA_PIN_SIZE;
+                return mConfiguredCameraPinBytes;
             case KEY_HOME:
                 return mConfiguredHomePinBytes;
             case KEY_ASSISTANT:
-                return MAX_ASSISTANT_PIN_SIZE;
+                return mConfiguredAssistantPinBytes;
             default:
                 return 0;
         }
@@ -1303,6 +1306,10 @@
             pw.format("   Maximum Pinner quota: %d bytes (%.2f MB)\n", mConfiguredMaxPinnedMemory,
                     mConfiguredMaxPinnedMemory / bytesPerMB);
             pw.format("   Max Home App Pin Bytes (without deps): %d\n", mConfiguredHomePinBytes);
+            pw.format("   Max Assistant App Pin Bytes (without deps): %d\n",
+                    mConfiguredAssistantPinBytes);
+            pw.format(
+                    "   Max Camera App Pin Bytes (without deps): %d\n", mConfiguredCameraPinBytes);
             pw.format("\nPinned Files:\n");
             synchronized (PinnerService.this) {
                 long totalSize = 0;
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 9e8598a..0b58c75 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -791,16 +791,6 @@
             }
             try {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
-                // This mirrors logic from commitReconciledScanResultLocked, where the library
-                // files needed for dexopt are assigned.
-                PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
-                // Unfortunately, the updated system app flag is only tracked on this
-                // PackageSetting
-                boolean isUpdatedSystemApp =
-                        installRequest.getScannedPackageSetting().isUpdatedSystemApp();
-                realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
-
                 DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
                         installRequest, dexoptOptions);
                 installRequest.onDexoptFinished(dexOptResult);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 91a17a9..4589d26 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -381,10 +381,12 @@
                     // service to populate the hardware list.
                     serviceState = new ServiceState(component, userId);
                     userState.serviceStateMap.put(component, serviceState);
-                    updateServiceConnectionLocked(component, userId);
                 } else {
                     inputList.addAll(serviceState.hardwareInputMap.values());
                 }
+                if (serviceState.needInit) {
+                    updateServiceConnectionLocked(component, userId);
+                }
             } else {
                 try {
                     TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
@@ -489,6 +491,27 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void cleanUpHdmiDevices(int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "cleanUpHdmiDevices: user " + userId);
+        }
+        UserState userState = getOrCreateUserStateLocked(userId);
+        for (ServiceState serviceState : userState.serviceStateMap.values()) {
+            for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
+                try {
+                    if (serviceState.service != null) {
+                        serviceState.service.notifyHdmiDeviceRemoved(device);
+                    } else {
+                        serviceState.hdmiDeviceRemovedBuffer.add(device);
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
+                }
+            }
+        }
+    }
+
     private void startUser(int userId) {
         synchronized (mLock) {
             if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
@@ -500,9 +523,13 @@
             if (userInfo.isProfile()
                     && parentInfo != null
                     && parentInfo.id == mCurrentUserId) {
-                // only the children of the current user can be started in background
+                int prevUserId = mCurrentUserId;
                 mCurrentUserId = userId;
-                startProfileLocked(userId);
+                // only the children of the current user can be started in background
+                releaseSessionOfUserLocked(prevUserId);
+                cleanUpHdmiDevices(prevUserId);
+                unbindServiceOfUserLocked(prevUserId);
+                startProfileLocked(mCurrentUserId);
             }
         }
     }
@@ -515,6 +542,7 @@
             }
 
             releaseSessionOfUserLocked(userId);
+            cleanUpHdmiDevices(userId);
             unbindServiceOfUserLocked(userId);
             mRunningProfiles.remove(userId);
         }
@@ -543,15 +571,19 @@
                 unbindServiceOfUserLocked(runningId);
             }
             mRunningProfiles.clear();
-            releaseSessionOfUserLocked(mCurrentUserId);
-            unbindServiceOfUserLocked(mCurrentUserId);
 
+            int prevUserId = mCurrentUserId;
             mCurrentUserId = userId;
-            buildTvInputListLocked(userId, null);
-            buildTvContentRatingSystemListLocked(userId);
+
+            releaseSessionOfUserLocked(prevUserId);
+            cleanUpHdmiDevices(prevUserId);
+            unbindServiceOfUserLocked(prevUserId);
+
+            buildTvInputListLocked(mCurrentUserId, null);
+            buildTvContentRatingSystemListLocked(mCurrentUserId);
             mMessageHandler
                     .obtainMessage(MessageHandler.MSG_SWITCH_CONTENT_RESOLVER,
-                            getContentResolverForUser(userId))
+                            getContentResolverForUser(mCurrentUserId))
                     .sendToTarget();
         }
     }
@@ -590,6 +622,9 @@
 
     @GuardedBy("mLock")
     private void unbindServiceOfUserLocked(int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "unbindServiceOfUserLocked: user " + userId);
+        }
         UserState userState = getUserStateLocked(userId);
         if (userState == null) {
             return;
@@ -600,7 +635,12 @@
             ServiceState serviceState = userState.serviceStateMap.get(component);
             if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
                 unbindService(serviceState);
-                it.remove();
+                if (!serviceState.isHardware) {
+                    it.remove();
+                } else {
+                    serviceState.hardwareInputMap.clear();
+                    serviceState.needInit = true;
+                }
             }
         }
     }
@@ -774,7 +814,7 @@
         boolean shouldBind;
         if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
             shouldBind = !serviceState.sessionTokens.isEmpty()
-                    || (serviceState.isHardware && serviceState.neverConnected);
+                    || (serviceState.isHardware && serviceState.needInit);
         } else {
             // For a non-current user,
             // if sessionTokens is not empty, it contains recording sessions only
@@ -3404,13 +3444,13 @@
         private ServiceCallback callback;
         private boolean bound;
         private boolean reconnecting;
-        private boolean neverConnected;
+        private boolean needInit;
 
         private ServiceState(ComponentName component, int userId) {
             this.component = component;
             this.connection = new InputServiceConnection(component, userId);
             this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
-            this.neverConnected = true;
+            this.needInit = true;
         }
     }
 
@@ -3618,11 +3658,9 @@
         }
         ComponentName component = mTvInputHardwareManager.getInputMap().get(inputId).getComponent();
         ServiceState serviceState = getServiceStateLocked(component, userId);
-        boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
-        if (removed) {
-            buildTvInputListLocked(userId, null);
-            mTvInputHardwareManager.removeHardwareInput(inputId);
-        }
+        serviceState.hardwareInputMap.remove(inputId);
+        buildTvInputListLocked(userId, null);
+        mTvInputHardwareManager.removeHardwareInput(inputId);
     }
 
     private final class InputServiceConnection implements ServiceConnection {
@@ -3648,7 +3686,7 @@
                 }
                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
                 serviceState.service = ITvInputService.Stub.asInterface(service);
-                serviceState.neverConnected = false;
+                serviceState.needInit = false;
 
                 // Register a callback, if we need to.
                 if (serviceState.isHardware && serviceState.callback == null) {
@@ -3841,9 +3879,12 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    Slog.d(TAG, "ServiceCallback: removeHardwareInput, inputId: " + inputId +
-                                " by " + mComponent + ", userId: " + mUserId);
-                    removeHardwareInputLocked(inputId, mUserId);
+                    if (mUserId == mCurrentUserId) {
+                        Slog.d(TAG,
+                                "ServiceCallback: removeHardwareInput, inputId: " + inputId + " by "
+                                        + mComponent + ", userId: " + mUserId);
+                        removeHardwareInputLocked(inputId, mUserId);
+                    }
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -4578,6 +4619,11 @@
     private final class HardwareListener implements TvInputHardwareManager.Listener {
         @Override
         public void onStateChanged(String inputId, int state) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onStateChanged: inputId " + (inputId != null ? inputId : "null")
+                                + ", state " + state);
+            }
             synchronized (mLock) {
                 setStateLocked(inputId, state, mCurrentUserId);
             }
@@ -4585,6 +4631,11 @@
 
         @Override
         public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHardwareDeviceAdded: TvInputHardwareInfo "
+                                + (info != null ? info.toString() : "null"));
+            }
             synchronized (mLock) {
                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
@@ -4607,6 +4658,11 @@
 
         @Override
         public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHardwareDeviceRemoved: TvInputHardwareInfo "
+                                + (info != null ? info.toString() : "null"));
+            }
             synchronized (mLock) {
                 String relatedInputId =
                         mTvInputHardwareManager.getHardwareInputIdMap().get(info.getDeviceId());
@@ -4634,6 +4690,11 @@
 
         @Override
         public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHdmiDeviceAdded: HdmiDeviceInfo "
+                                + (deviceInfo != null ? deviceInfo.toString() : "null"));
+            }
             synchronized (mLock) {
                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
@@ -4656,6 +4717,11 @@
 
         @Override
         public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHdmiDeviceRemoved: HdmiDeviceInfo "
+                                + (deviceInfo != null ? deviceInfo.toString() : "null"));
+            }
             synchronized (mLock) {
                 String relatedInputId =
                         mTvInputHardwareManager.getHdmiInputIdMap().get(deviceInfo.getId());
@@ -4683,6 +4749,12 @@
 
         @Override
         public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHdmiDeviceUpdated: inputId " + (inputId != null ? inputId : "null")
+                                + ", deviceInfo: "
+                                + (deviceInfo != null ? deviceInfo.toString() : "null"));
+            }
             synchronized (mLock) {
                 Integer state;
                 switch (deviceInfo.getDevicePowerStatus()) {
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index fbcc856..d192e64 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -21,6 +21,8 @@
 import android.os.CombinedVibration;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.VibrationEffectSegment;
 import android.util.SparseArray;
@@ -145,19 +147,30 @@
                 originalEffect, mScaleLevel, mAdaptiveScale);
     }
 
-    /**
-     * Returns true if this vibration can pipeline with the specified one.
-     *
-     * <p>Note that currently, repeating vibrations can't pipeline with following vibrations,
-     * because the cancel() call to stop the repetition will cancel a pending vibration too. This
-     * can be changed if we have a use-case to reason around behavior for. It may also be nice to
-     * pipeline very short vibrations together, regardless of the flag.
-     */
-    public boolean canPipelineWith(HalVibration vib) {
-        return callerInfo.uid == vib.callerInfo.uid && callerInfo.attrs.isFlagSet(
-                VibrationAttributes.FLAG_PIPELINED_EFFECT)
-                && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
-                && (mOriginalEffect.getDuration() != Long.MAX_VALUE);
+    /** Returns true if this vibration can pipeline with the specified one. */
+    public boolean canPipelineWith(HalVibration vib,
+            @Nullable SparseArray<VibratorInfo> vibratorInfos, int durationThresholdMs) {
+        long effectDuration = Flags.vibrationPipelineEnabled() && (vibratorInfos != null)
+                ? mEffectToPlay.getDuration(vibratorInfos)
+                : mEffectToPlay.getDuration();
+        if (effectDuration == Long.MAX_VALUE) {
+            // Repeating vibrations can't pipeline with following vibrations, because the cancel()
+            // call to stop the repetition will cancel a pending vibration too. This can be changed
+            // if we have a use-case, requiring changes to how pipelined vibrations are cancelled.
+            return false;
+        }
+        if (Flags.vibrationPipelineEnabled()
+                && (effectDuration > 0) && (effectDuration < durationThresholdMs)) {
+            // Duration is known and it's less than the pipeline threshold, so allow it.
+            // No need to check UID, as we want to avoid cancelling any short effect and let the
+            // vibrator hardware gracefully finish the vibration.
+            return true;
+        }
+        // Check the same app is requesting multiple vibrations with the pipeline flag,
+        // independently of the effect durations.
+        return callerInfo.uid == vib.callerInfo.uid
+                && callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
+                && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT);
     }
 
     private void fillFallbacksForEffect(CombinedVibration effect,
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 9b7bdec..7d5d34d 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -168,12 +168,15 @@
 
     @VisibleForTesting
     final VibrationSettings mVibrationSettings;
+    private final VibrationConfig mVibrationConfig;
     private final VibrationScaler mVibrationScaler;
     private final VibratorControlService mVibratorControlService;
     private final InputDeviceDelegate mInputDeviceDelegate;
     private final DeviceAdapter mDeviceAdapter;
 
     @GuardedBy("mLock")
+    @Nullable private SparseArray<VibratorInfo> mVibratorInfos;
+    @GuardedBy("mLock")
     @Nullable private VibratorInfo mCombinedVibratorInfo;
     @GuardedBy("mLock")
     @Nullable private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
@@ -247,9 +250,9 @@
         mHandler = injector.createHandler(Looper.myLooper());
         mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
 
-        VibrationConfig vibrationConfig = new VibrationConfig(context.getResources());
-        mVibrationSettings = new VibrationSettings(mContext, mHandler, vibrationConfig);
-        mVibrationScaler = new VibrationScaler(vibrationConfig, mVibrationSettings);
+        mVibrationConfig = new VibrationConfig(context.getResources());
+        mVibrationSettings = new VibrationSettings(mContext, mHandler, mVibrationConfig);
+        mVibrationScaler = new VibrationScaler(mVibrationConfig, mVibrationSettings);
         mVibratorControlService = new VibratorControlService(mContext,
                 injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings,
                 mFrameworkStatsLogger, mLock);
@@ -295,7 +298,9 @@
             mVibratorIds = vibratorIds;
             mVibrators = new SparseArray<>(mVibratorIds.length);
             for (int vibratorId : vibratorIds) {
-                mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener));
+                VibratorController vibratorController =
+                        injector.createVibratorController(vibratorId, listener);
+                mVibrators.put(vibratorId, vibratorController);
             }
         }
 
@@ -334,6 +339,15 @@
                 mVibrators.valueAt(i).reloadVibratorInfoIfNeeded();
             }
 
+            synchronized (mLock) {
+                mVibratorInfos = transformAllVibratorsLocked(VibratorController::getVibratorInfo);
+                VibratorInfo[] infos = new VibratorInfo[mVibratorInfos.size()];
+                for (int i = 0; i < mVibratorInfos.size(); i++) {
+                    infos[i] = mVibratorInfos.valueAt(i);
+                }
+                mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos);
+            }
+
             mVibrationSettings.onSystemReady();
             mInputDeviceDelegate.onSystemReady();
 
@@ -633,7 +647,8 @@
                         endExternalVibrateLocked(Status.CANCELLED_SUPERSEDED, callerInfo,
                                 /* continueExternalControl= */ false);
                     } else if (mCurrentVibration != null) {
-                        if (mCurrentVibration.getVibration().canPipelineWith(vib)) {
+                        if (mCurrentVibration.getVibration().canPipelineWith(vib, mVibratorInfos,
+                                mVibrationConfig.getVibrationPipelineMaxDurationMs())) {
                             // Don't cancel the current vibration if it's pipeline-able.
                             // Note that if there is a pending next vibration that can't be
                             // pipelined, it will have already cancelled the current one, so we
@@ -1871,33 +1886,11 @@
         }
     }
 
+    @Nullable
     private VibratorInfo getCombinedVibratorInfo() {
         synchronized (mLock) {
-            // Used a cached resolving vibrator if one exists.
-            if (mCombinedVibratorInfo != null) {
-                return mCombinedVibratorInfo;
-            }
-
-            // Return an empty resolving vibrator if the service has no vibrator.
-            if (mVibratorIds.length == 0) {
-                return mCombinedVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
-            }
-
-            // Combine the vibrator infos of all the service's vibrator to create a single resolving
-            // vibrator that is based on the combined info.
-            VibratorInfo[] infos = new VibratorInfo[mVibratorIds.length];
-            for (int i = 0; i < mVibratorIds.length; i++) {
-                VibratorInfo info = getVibratorInfo(mVibratorIds[i]);
-                // If any one of the service's vibrator does not have a valid vibrator info, stop
-                // trying to create and cache a combined resolving vibrator. Combine the infos only
-                // when infos for all vibrators are available.
-                if (info == null) {
-                    return null;
-                }
-                infos[i] = info;
-            }
-
-            return mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos);
+            // This is only initialized at system ready, when all vibrator infos are fully loaded.
+            return mCombinedVibratorInfo;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index a380ba1..f9902cf 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -66,6 +66,7 @@
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -129,6 +130,7 @@
     /** If enabled the creator will not allow BAL on its behalf by default. */
     @ChangeId
     @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
+    @Overridable
     private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR =
             296478951;
     public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 6a23aaa..e9c6e93 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -274,34 +274,7 @@
         if (caller != controlTarget) {
             if (Flags.refactorInsetsController()) {
                 if (isImeInputTarget(caller)) {
-                    // In case of the multi window mode, update the requestedVisibleTypes from
-                    // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController.
-                    // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with
-                    // its new requested visibility for the IME
-                    boolean imeVisible = caller.isRequestedVisible(WindowInsets.Type.ime());
-                    if (controlTarget != null) {
-                        ImeTracker.forLogging().onProgress(statsToken,
-                                ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
-                        controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
-                    } else if (caller instanceof InsetsControlTarget) {
-                        // In case of a virtual display that cannot show the IME, the
-                        // controlTarget will be null here, as no controlTarget was set yet. In
-                        // that case, proceed similar to the multi window mode (fallback =
-                        // RemoteInsetsControlTarget of the default display)
-                        controlTarget = mDisplayContent.getImeHostOrFallback(
-                                ((InsetsControlTarget) caller).getWindow());
-
-                        if (controlTarget != caller) {
-                            ImeTracker.forLogging().onProgress(statsToken,
-                                    ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
-                            controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
-                        } else {
-                            ImeTracker.forLogging().onFailed(statsToken,
-                                    ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
-                        }
-                    }
-
-                    invokeOnImeRequestedChangedListener(caller, statsToken);
+                    reportImeInputTargetStateToControlTarget(caller, controlTarget, statsToken);
                 } else {
                     // TODO(b/353463205) add ImeTracker?
                 }
@@ -332,16 +305,44 @@
         if (Flags.refactorInsetsController() && target != null) {
             InsetsControlTarget imeControlTarget = getControlTarget();
             if (target != imeControlTarget) {
-                // If the targetWin is not the imeControlTarget (=RemoteInsetsControlTarget) let it
-                // know about the new requestedVisibleTypes for the IME.
-                if (imeControlTarget != null) {
-                    imeControlTarget.setImeInputTargetRequestedVisibility(
-                            (target.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
-                }
+                // TODO(b/353463205): start new request here?
+                reportImeInputTargetStateToControlTarget(target, imeControlTarget,
+                        null /* statsToken */);
             }
         }
     }
 
+    private void reportImeInputTargetStateToControlTarget(@NonNull InsetsTarget imeInsetsTarget,
+            InsetsControlTarget controlTarget, @Nullable ImeTracker.Token statsToken) {
+        // In case of the multi window mode, update the requestedVisibleTypes from
+        // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController.
+        // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with
+        // its new requested visibility for the IME
+        boolean imeVisible = imeInsetsTarget.isRequestedVisible(WindowInsets.Type.ime());
+        if (controlTarget != null) {
+            ImeTracker.forLogging().onProgress(statsToken,
+                    ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
+            controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+        } else if (imeInsetsTarget instanceof InsetsControlTarget) {
+            // In case of a virtual display that cannot show the IME, the
+            // controlTarget will be null here, as no controlTarget was set yet. In
+            // that case, proceed similar to the multi window mode (fallback =
+            // RemoteInsetsControlTarget of the default display)
+            controlTarget = mDisplayContent.getImeHostOrFallback(
+                    ((InsetsControlTarget) imeInsetsTarget).getWindow());
+
+            if (controlTarget != imeInsetsTarget) {
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
+                controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+            } else {
+                ImeTracker.forLogging().onFailed(statsToken,
+                        ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
+            }
+        }
+        invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken);
+    }
+
     // TODO(b/353463205) check callers to see if we can make statsToken @NonNull
     private void invokeOnImeRequestedChangedListener(InsetsTarget insetsTarget,
             @Nullable ImeTracker.Token statsToken) {
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index cc340c0..891c334 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -58,6 +58,7 @@
 import android.os.UserManager;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
@@ -1737,6 +1738,11 @@
         pw.decreaseIndent();
     }
 
+    @Override
+    protected void onUnhandledException(int code, int flags, Exception e) {
+        Slog.wtf(TAG, "Uncaught exception in AudioService: " + code + ", " + flags, e);
+    }
+
     @GuardedBy("mUsbMidiLock")
     private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) {
         String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
diff --git a/services/tests/appfunctions/Android.bp b/services/tests/appfunctions/Android.bp
index c841643..836f90b 100644
--- a/services/tests/appfunctions/Android.bp
+++ b/services/tests/appfunctions/Android.bp
@@ -36,7 +36,9 @@
         "androidx.test.core",
         "androidx.test.runner",
         "androidx.test.ext.truth",
+        "androidx.core_core-ktx",
         "kotlin-test",
+        "kotlinx_coroutines_test",
         "platform-test-annotations",
         "services.appfunctions",
         "servicestests-core-utils",
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
new file mode 100644
index 0000000..a69e902
--- /dev/null
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.server.appfunctions
+
+import android.app.appfunctions.flags.Flags
+import android.content.Context
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+class AppFunctionManagerServiceImplTest {
+    @get:Rule
+    val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+    private val context: Context
+        get() = ApplicationProvider.getApplicationContext()
+
+    private val serviceImpl = AppFunctionManagerServiceImpl(context)
+
+    @Test
+    fun testGetLockForPackage_samePackage() {
+        val packageName = "com.example.app"
+        val lock1 = serviceImpl.getLockForPackage(packageName)
+        val lock2 = serviceImpl.getLockForPackage(packageName)
+
+        // Assert that the same lock object is returned for the same package name
+        assertThat(lock1).isEqualTo(lock2)
+    }
+
+    @Test
+    fun testGetLockForPackage_differentPackages() {
+        val packageName1 = "com.example.app1"
+        val packageName2 = "com.example.app2"
+        val lock1 = serviceImpl.getLockForPackage(packageName1)
+        val lock2 = serviceImpl.getLockForPackage(packageName2)
+
+        // Assert that different lock objects are returned for different package names
+        assertThat(lock1).isNotEqualTo(lock2)
+    }
+
+    @Ignore("Hard to deterministically trigger the garbage collector.")
+    @Test
+    fun testWeakReference_garbageCollected_differentLockAfterGC() = runTest {
+        // Create a large number of temporary objects to put pressure on the GC
+        val tempObjects = MutableList<Any?>(10000000) { Any() }
+        var callingPackage: String? = "com.example.app"
+        var lock1: Any? = serviceImpl.getLockForPackage(callingPackage)
+        callingPackage = null // Set the key to null
+        val lock1Hash = lock1.hashCode()
+        lock1 = null
+
+        // Create memory pressure
+        repeat(3) {
+            for (i in 1..100) {
+                "a".repeat(10000)
+            }
+            System.gc() // Suggest garbage collection
+            System.runFinalization()
+        }
+        // Get the lock again - it should be a different object now
+        val lock2 = serviceImpl.getLockForPackage("com.example.app")
+        // Assert that the lock objects are different
+        assertThat(lock1Hash).isNotEqualTo(lock2.hashCode())
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 3b5a386..b917af4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -1658,6 +1658,70 @@
     }
 
     @Test
+    public void testGetDisplayIdsByGroupsIds() throws Exception {
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        // Create display 1
+        FakeDisplayDevice displayDevice1 =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1);
+        final int groupId1 = display1.getDisplayInfoLocked().displayGroupId;
+        // Create display 2
+        FakeDisplayDevice displayDevice2 =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2);
+        final int groupId2 = display2.getDisplayInfoLocked().displayGroupId;
+        // Both displays should be in the same display group
+        assertEquals(groupId1, groupId2);
+        final int[] displayIds = new int[]{
+                display1.getDisplayIdLocked(), display2.getDisplayIdLocked()};
+        final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>();
+        expectedDisplayGroups.put(groupId1, displayIds);
+
+        final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds();
+
+        for (int i = 0; i < expectedDisplayGroups.size(); i++) {
+            final int groupId = expectedDisplayGroups.keyAt(i);
+            assertTrue(displayGroups.contains(groupId));
+            assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId));
+        }
+    }
+
+    @Test
+    public void testGetDisplayIdsByGroupsIds_multipleDisplayGroups() throws Exception {
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        // Create display 1
+        FakeDisplayDevice displayDevice1 =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1);
+        final int groupId1 = display1.getDisplayInfoLocked().displayGroupId;
+        // Create display 2
+        FakeDisplayDevice displayDevice2 =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+        LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2);
+        final int groupId2 = display2.getDisplayInfoLocked().displayGroupId;
+        // Both displays should be in different display groups
+        assertNotEquals(groupId1, groupId2);
+        final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>();
+        expectedDisplayGroups.put(groupId1, new int[]{display1.getDisplayIdLocked()});
+        expectedDisplayGroups.put(groupId2, new int[]{display2.getDisplayIdLocked()});
+
+        final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds();
+
+        assertEquals(expectedDisplayGroups.size(), displayGroups.size());
+        for (int i = 0; i < expectedDisplayGroups.size(); i++) {
+            final int groupId = expectedDisplayGroups.keyAt(i);
+            assertTrue(displayGroups.contains(groupId));
+            assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId));
+        }
+    }
+
+    @Test
     public void testCreateVirtualDisplay_isValidProjection_notValid()
             throws RemoteException {
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 31157f9..9781851 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -95,7 +95,6 @@
 import com.android.server.job.JobStore;
 import com.android.server.job.controllers.QuotaController.ExecutionStats;
 import com.android.server.job.controllers.QuotaController.QcConstants;
-import com.android.server.job.controllers.QuotaController.QuotaBump;
 import com.android.server.job.controllers.QuotaController.ShrinkableDebits;
 import com.android.server.job.controllers.QuotaController.TimedEvent;
 import com.android.server.job.controllers.QuotaController.TimingSession;
@@ -136,7 +135,6 @@
     private QuotaController.QcConstants mQcConstants;
     private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
     private int mSourceUid;
-    private AppStandbyInternal.AppIdleStateChangeListener mAppIdleStateChangeListener;
     private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener;
     private IUidObserver mUidObserver;
     private UsageStatsManagerInternal.UsageEventListener mUsageEventListener;
@@ -191,8 +189,7 @@
         when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
         doReturn(mActivityMangerInternal)
                 .when(() -> LocalServices.getService(ActivityManagerInternal.class));
-        final AppStandbyInternal appStandbyInternal = mock(AppStandbyInternal.class);
-        doReturn(appStandbyInternal)
+        doReturn(mock(AppStandbyInternal.class))
                 .when(() -> LocalServices.getService(AppStandbyInternal.class));
         doReturn(mock(BatteryManagerInternal.class))
                 .when(() -> LocalServices.getService(BatteryManagerInternal.class));
@@ -239,8 +236,6 @@
 
         // Initialize real objects.
         // Capture the listeners.
-        ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> aiscListenerCaptor =
-                ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
         ArgumentCaptor<IUidObserver> uidObserverCaptor =
                 ArgumentCaptor.forClass(IUidObserver.class);
         ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor =
@@ -250,8 +245,6 @@
         mQuotaController = new QuotaController(mJobSchedulerService,
                 mock(BackgroundJobsController.class), mock(ConnectivityController.class));
 
-        verify(appStandbyInternal).addListener(aiscListenerCaptor.capture());
-        mAppIdleStateChangeListener = aiscListenerCaptor.getValue();
         verify(mPowerAllowlistInternal)
                 .registerTempAllowlistChangeListener(taChangeCaptor.capture());
         mTempAllowlistListener = taChangeCaptor.getValue();
@@ -488,14 +481,12 @@
                 now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
         TimingSession two = createTimingSession(
                 now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
-        QuotaBump bump1 = new QuotaBump(now - 2 * HOUR_IN_MILLIS);
         TimingSession thr = createTimingSession(
                 now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
         // Overlaps 24 hour boundary.
         TimingSession fou = createTimingSession(
                 now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1);
         // Way past the 24 hour boundary.
-        QuotaBump bump2 = new QuotaBump(now - 24 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
         TimingSession fiv = createTimingSession(
                 now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4);
         List<TimedEvent> expectedRegular = new ArrayList<>();
@@ -503,16 +494,13 @@
         // Added in correct (chronological) order.
         expectedRegular.add(fou);
         expectedRegular.add(thr);
-        expectedRegular.add(bump1);
         expectedRegular.add(two);
         expectedRegular.add(one);
         expectedEJ.add(fou);
         expectedEJ.add(one);
         mQuotaController.saveTimingSession(0, "com.android.test", fiv, false);
-        mQuotaController.getTimingSessions(0, "com.android.test").add(bump2);
         mQuotaController.saveTimingSession(0, "com.android.test", fou, false);
         mQuotaController.saveTimingSession(0, "com.android.test", thr, false);
-        mQuotaController.getTimingSessions(0, "com.android.test").add(bump1);
         mQuotaController.saveTimingSession(0, "com.android.test", two, false);
         mQuotaController.saveTimingSession(0, "com.android.test", one, false);
         mQuotaController.saveTimingSession(0, "com.android.test", fiv, true);
@@ -1847,129 +1835,6 @@
         }
     }
 
-    /**
-     * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
-     * window and there are valid QuotaBumps in the history.
-     */
-    @Test
-    public void testGetTimeUntilQuotaConsumedLocked_QuotaBump() {
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-
-        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        // Close to RARE boundary.
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
-                        30 * SECOND_IN_MILLIS, 5), false);
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS));
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS));
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS));
-        // Far away from FREQUENT boundary.
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
-        // Overlap WORKING_SET boundary.
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 2 * HOUR_IN_MILLIS));
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
-                        3 * MINUTE_IN_MILLIS, 5), false);
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS));
-        // Close to ACTIVE boundary.
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
-
-        setStandbyBucket(RARE_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(3 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(4 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-
-        setStandbyBucket(FREQUENT_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(4 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(4 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-
-        setStandbyBucket(WORKING_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(8 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(10 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-
-        // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
-        // max execution time.
-        setStandbyBucket(ACTIVE_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(10 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-    }
-
-    /**
-     * Test getTimeUntilQuotaConsumedLocked when there are valid QuotaBumps in recent history that
-     * provide enough additional quota to bridge gaps between sessions.
-     */
-    @Test
-    public void testGetTimeUntilQuotaConsumedLocked_QuotaBump_CrucialBumps() {
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-
-        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (25 * HOUR_IN_MILLIS),
-                        30 * MINUTE_IN_MILLIS, 25), false);
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS));
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS));
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS));
-        // Without the valid quota bumps, the app would only 3 minutes until the quota was consumed.
-        // The quota bumps provide enough quota to bridge the gap between the two earliest sessions.
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (8 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (8 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS),
-                        2 * MINUTE_IN_MILLIS, 5), false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
-                        3 * MINUTE_IN_MILLIS, 1), false);
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS));
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (9 * MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 1), false);
-
-        setStandbyBucket(FREQUENT_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(2 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(7 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-    }
-
     @Test
     public void testIsWithinQuotaLocked_NeverApp() {
         synchronized (mQuotaController.mLock) {
@@ -2316,272 +2181,6 @@
         }
     }
 
-    @Test
-    public void testIsWithinQuotaLocked_WithQuotaBump_Duration() {
-        setDischarging();
-        int standbyBucket = WORKING_INDEX;
-        setStandbyBucket(standbyBucket);
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
-                5 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
-        long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(
-                        now - (HOUR_IN_MILLIS - 2 * MINUTE_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 1),
-                false);
-        final ExecutionStats stats;
-        synchronized (mQuotaController.mLock) {
-            stats = mQuotaController.getExecutionStatsLocked(
-                    SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
-            mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 1);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(5 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-
-        advanceElapsedClock(HOUR_IN_MILLIS);
-
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-
-        // Emulate a quota bump while some jobs are executing
-        JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 1);
-        JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 2);
-
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStartTrackingJobLocked(job1, null);
-            mQuotaController.prepareForExecutionLocked(job1);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-            mQuotaController.maybeStartTrackingJobLocked(job2, null);
-            mQuotaController.prepareForExecutionLocked(job2);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null);
-            mQuotaController.maybeStopTrackingJobLocked(job2, null);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-
-        // Phase out the first session
-        advanceElapsedClock(5 * MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-
-        // Phase out the first quota bump
-        advanceElapsedClock(7 * HOUR_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-    }
-
-    @Test
-    public void testIsWithinQuotaLocked_WithQuotaBump_JobCount() {
-        setDischarging();
-        int standbyBucket = WORKING_INDEX;
-        setStandbyBucket(standbyBucket);
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
-                20 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 1);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
-        long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS - HOUR_IN_MILLIS),
-                        5 * MINUTE_IN_MILLIS, 10), false);
-        final ExecutionStats stats;
-        synchronized (mQuotaController.mLock) {
-            stats = mQuotaController.getExecutionStatsLocked(
-                    SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
-            mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 10);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(10, stats.jobCountLimit);
-        }
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(11, stats.jobCountLimit);
-        }
-
-        advanceElapsedClock(HOUR_IN_MILLIS);
-
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(11, stats.jobCountLimit);
-        }
-
-        // Emulate a quota bump while some jobs are executing
-        JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1);
-        JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2);
-
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStartTrackingJobLocked(job1, null);
-            mQuotaController.prepareForExecutionLocked(job1);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(12, stats.jobCountLimit);
-            mQuotaController.maybeStartTrackingJobLocked(job2, null);
-            mQuotaController.prepareForExecutionLocked(job2);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null);
-            mQuotaController.maybeStopTrackingJobLocked(job2, null);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(12, stats.jobCountLimit);
-        }
-
-        // Phase out the first session
-        advanceElapsedClock(3 * MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(12, stats.jobCountLimit);
-        }
-
-        // Phase out the first quota bump
-        advanceElapsedClock(7 * HOUR_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(11, stats.jobCountLimit);
-        }
-    }
-
-    @Test
-    public void testIsWithinQuotaLocked_WithQuotaBump_SessionCount() {
-        setDischarging();
-        int standbyBucket = WORKING_INDEX;
-        setStandbyBucket(standbyBucket);
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
-                20 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 2);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 1);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
-        long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS - HOUR_IN_MILLIS),
-                        5 * MINUTE_IN_MILLIS, 1), false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
-        final ExecutionStats stats;
-        synchronized (mQuotaController.mLock) {
-            stats = mQuotaController.getExecutionStatsLocked(
-                    SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(2, stats.sessionCountLimit);
-        }
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(3, stats.sessionCountLimit);
-        }
-
-        advanceElapsedClock(HOUR_IN_MILLIS);
-
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(3, stats.sessionCountLimit);
-        }
-
-        // Emulate a quota bump while some jobs are executing
-        JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1);
-        JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2);
-
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStartTrackingJobLocked(job1, null);
-            mQuotaController.prepareForExecutionLocked(job1);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null);
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(4, stats.sessionCountLimit);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStartTrackingJobLocked(job2, null);
-            mQuotaController.prepareForExecutionLocked(job2);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job2, null);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(4, stats.sessionCountLimit);
-        }
-
-        // Phase out the first session
-        advanceElapsedClock(2 * MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(4, stats.sessionCountLimit);
-        }
-
-        // Phase out the first quota bump
-        advanceElapsedClock(7 * HOUR_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(3, stats.sessionCountLimit);
-        }
-    }
 
     @Test
     public void testIsWithinEJQuotaLocked_NeverApp() {
@@ -3590,12 +3189,6 @@
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS,
                 84 * SECOND_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
-                93 * SECOND_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 92);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 91);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 90 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 89);
 
         assertEquals(8 * MINUTE_IN_MILLIS,
                 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3653,11 +3246,6 @@
         assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
         assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
         assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());
-        assertEquals(93 * SECOND_IN_MILLIS, mQuotaController.getQuotaBumpAdditionDurationMs());
-        assertEquals(92, mQuotaController.getQuotaBumpAdditionJobCount());
-        assertEquals(91, mQuotaController.getQuotaBumpAdditionSessionCount());
-        assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
-        assertEquals(89, mQuotaController.getQuotaBumpLimit());
     }
 
     @Test
@@ -3710,11 +3298,6 @@
         setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, -1);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, -1);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, -1);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 59 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, -1);
 
         assertEquals(MINUTE_IN_MILLIS,
                 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3765,11 +3348,6 @@
         assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs());
         assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs());
         assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs());
-        assertEquals(0, mQuotaController.getQuotaBumpAdditionDurationMs());
-        assertEquals(0, mQuotaController.getQuotaBumpAdditionJobCount());
-        assertEquals(0, mQuotaController.getQuotaBumpAdditionSessionCount());
-        assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
-        assertEquals(0, mQuotaController.getQuotaBumpLimit());
 
         // Invalid configurations.
         // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
@@ -3827,7 +3405,6 @@
         setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS);
 
         assertEquals(24 * HOUR_IN_MILLIS,
                 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3868,7 +3445,6 @@
         assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
         assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
         assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());
-        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
     }
 
     /** Tests that TimingSessions aren't saved when the device is charging. */
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 639ae30..58489f3 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -81,7 +81,6 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -581,8 +580,6 @@
         HintManagerService service = createService();
         IBinder token = new Binder();
         int threadCount = 2;
-
-        // session 1 has 2 non-isolated tids
         long sessionPtr1 = 111;
         CountDownLatch stopLatch1 = new CountDownLatch(1);
         int[] tids1 = createThreads(threadCount, stopLatch1);
@@ -618,7 +615,6 @@
         IBinder token = new Binder();
         int threadCount = 2;
 
-        // session 1 has 2 non-isolated tids
         long sessionPtr1 = 111;
         CountDownLatch stopLatch1 = new CountDownLatch(1);
         int[] tids1 = createThreads(threadCount, stopLatch1);
@@ -630,67 +626,23 @@
                         SessionTag.OTHER, new SessionConfig());
         assertNotNull(session1);
 
-        // session 2 has 2 non-isolated tids and 2 isolated tids
-        long sessionPtr2 = 222;
-        CountDownLatch stopLatch2 = new CountDownLatch(1);
-        // negative value used for test only to avoid conflicting with any real thread that exists
-        int isoProc1 = -100;
-        int isoProc2 = 99999999;
-        when(mAmInternalMock.getIsolatedProcesses(eq(UID))).thenReturn(List.of(0));
-        int[] tids2 = createThreads(threadCount, stopLatch2);
-        int[] tids2WithIsolated = Arrays.copyOf(tids2, tids2.length + 2);
-        tids2WithIsolated[threadCount] = isoProc1;
-        tids2WithIsolated[threadCount + 1] = isoProc2;
-        int[] expectedTids2 = Arrays.copyOf(tids2, tids2.length + 1);
-        expectedTids2[tids2.length] = isoProc1;
-        when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
-                eq(tids2WithIsolated), eq(DEFAULT_TARGET_DURATION), anyInt(),
-                any(SessionConfig.class))).thenReturn(sessionPtr2);
-        AppHintSession session2 = (AppHintSession) service.getBinderServiceInstance()
-                .createHintSessionWithConfig(token, tids2WithIsolated,
-                        DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig());
-        assertNotNull(session2);
-
-        // trigger clean up through UID state change by making the process foreground->background
-        // this will remove the one unexpected isolated tid from session 2
-        service.mUidObserver.onUidStateChanged(UID,
-                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
-        service.mUidObserver.onUidStateChanged(UID,
-                ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
-                CLEAN_UP_UID_DELAY_MILLIS));
-        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
-        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
-        // the new TIDs pending list should be updated
-        assertArrayEquals(expectedTids2, session2.getTidsInternal());
-        reset(mNativeWrapperMock);
-
-        // this should resume and update the threads so those never-existed invalid isolated
-        // processes should be cleaned up
-        service.mUidObserver.onUidStateChanged(UID,
-                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
-        // wait for the async uid state change to trigger resume and setThreads
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
-        verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2), eq(expectedTids2));
-        reset(mNativeWrapperMock);
-
         // let all session 1 threads to exit and the cleanup should force pause the session 1
         stopLatch1.countDown();
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
         service.mUidObserver.onUidStateChanged(UID,
+                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
+        service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1));
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
-        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
         verifyAllHintsEnabled(session1, false);
-        verifyAllHintsEnabled(session2, false);
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
+        assertArrayEquals(tids1, session1.getTidsInternal());
         verifyAllHintsEnabled(session1, false);
-        verifyAllHintsEnabled(session2, true);
         reset(mNativeWrapperMock);
 
         // in foreground, set new tids for session 1 then it should be resumed and all hints allowed
@@ -704,8 +656,6 @@
 
         // let all session 1 and 2 non isolated threads to exit
         stopLatch1.countDown();
-        stopLatch2.countDown();
-        expectedTids2 = new int[]{isoProc1};
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
@@ -713,14 +663,11 @@
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1));
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
-        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
         // in background, set threads for session 1 then it should not be force paused next time
         session1.setThreads(SESSION_TIDS_A);
         // the new TIDs pending list should be updated
         assertArrayEquals(SESSION_TIDS_A, session1.getTidsInternal());
-        assertArrayEquals(expectedTids2, session2.getTidsInternal());
         verifyAllHintsEnabled(session1, false);
-        verifyAllHintsEnabled(session2, false);
         reset(mNativeWrapperMock);
 
         service.mUidObserver.onUidStateChanged(UID,
@@ -729,10 +676,7 @@
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr1),
                 eq(SESSION_TIDS_A));
-        verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2),
-                eq(expectedTids2));
         verifyAllHintsEnabled(session1, true);
-        verifyAllHintsEnabled(session2, true);
     }
 
     private void verifyAllHintsEnabled(AppHintSession session, boolean verifyEnabled) {
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt
new file mode 100644
index 0000000..22d894a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.server.systemconfig
+
+import android.content.Context
+import android.content.pm.FeatureInfo
+import android.util.ArrayMap
+import android.util.Xml
+
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.SystemConfig
+import com.google.common.truth.Truth.assertThat
+import org.junit.runner.RunWith
+import org.junit.Rule
+
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class SystemConfigReadOnlyFeaturesTest {
+
+    companion object {
+        private const val FEATURE_ONE = "feature.test.1"
+        private const val FEATURE_TWO = "feature.test.2"
+        private const val FEATURE_RUNTIME_AVAILABLE_TEMPLATE =
+        """
+            <permissions>
+                <feature name="%s" />
+            </permissions>
+        """
+        private const val FEATURE_RUNTIME_DISABLED_TEMPLATE =
+        """
+            <permissions>
+                <Disabled-feature name="%s" />
+            </permissions>
+        """
+
+        fun featureInfo(featureName: String) = FeatureInfo().apply { name = featureName }
+    }
+
+    private val context: Context = InstrumentationRegistry.getInstrumentation().context
+
+    @get:Rule
+    val tempFolder = TemporaryFolder(context.filesDir)
+
+    private val injector = TestInjector()
+
+    private var uniqueCounter = 0
+
+    @Test
+    fun empty() {
+        assertFeatures().isEmpty()
+    }
+
+    @Test
+    fun readOnlyEnabled() {
+        addReadOnlyEnabledFeature(FEATURE_ONE)
+        addReadOnlyEnabledFeature(FEATURE_TWO)
+
+        assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO)
+    }
+
+    @Test
+    fun readOnlyAndRuntimeEnabled() {
+        addReadOnlyEnabledFeature(FEATURE_ONE)
+        addRuntimeEnabledFeature(FEATURE_TWO)
+
+        // No issues with matching availability.
+        assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO)
+    }
+
+    @Test
+    fun readOnlyEnabledRuntimeDisabled() {
+        addReadOnlyEnabledFeature(FEATURE_ONE)
+        addRuntimeDisabledFeature(FEATURE_ONE)
+
+        // Read-only feature availability should take precedence.
+        assertFeatures().contains(FEATURE_ONE)
+    }
+
+    @Test
+    fun readOnlyDisabled() {
+        addReadOnlyDisabledFeature(FEATURE_ONE)
+
+        assertFeatures().doesNotContain(FEATURE_ONE)
+    }
+
+    @Test
+    fun readOnlyAndRuntimeDisabled() {
+        addReadOnlyDisabledFeature(FEATURE_ONE)
+        addRuntimeDisabledFeature(FEATURE_ONE)
+
+        // No issues with matching (un)availability.
+        assertFeatures().doesNotContain(FEATURE_ONE)
+    }
+
+    @Test
+    fun readOnlyDisabledRuntimeEnabled() {
+        addReadOnlyDisabledFeature(FEATURE_ONE)
+        addRuntimeEnabledFeature(FEATURE_ONE)
+        addRuntimeEnabledFeature(FEATURE_TWO)
+
+        // Read-only feature (un)availability should take precedence.
+        assertFeatures().doesNotContain(FEATURE_ONE)
+        assertFeatures().contains(FEATURE_TWO)
+    }
+
+    fun addReadOnlyEnabledFeature(featureName: String) {
+        injector.readOnlyEnabledFeatures[featureName] = featureInfo(featureName)
+    }
+
+    fun addReadOnlyDisabledFeature(featureName: String) {
+        injector.readOnlyDisabledFeatures.add(featureName)
+    }
+
+    fun addRuntimeEnabledFeature(featureName: String) {
+        FEATURE_RUNTIME_AVAILABLE_TEMPLATE.format(featureName).write()
+    }
+
+    fun addRuntimeDisabledFeature(featureName: String) {
+        FEATURE_RUNTIME_DISABLED_TEMPLATE.format(featureName).write()
+    }
+
+    private fun String.write() = tempFolder.root.resolve("${uniqueCounter++}.xml")
+            .writeText(this.trimIndent())
+
+    private fun assertFeatures() = assertThat(availableFeatures().keys)
+
+    private fun availableFeatures() = SystemConfig(false, injector).apply {
+        val parser = Xml.newPullParser()
+        readPermissions(parser, tempFolder.root, /*Grant all permission flags*/ 0.inv())
+    }.let { it.availableFeatures }
+
+    internal class TestInjector() : SystemConfig.Injector() {
+        val readOnlyEnabledFeatures = ArrayMap<String, FeatureInfo>()
+        val readOnlyDisabledFeatures = mutableSetOf<String>()
+
+        override fun isReadOnlySystemEnabledFeature(featureName: String, version: Int): Boolean {
+            return readOnlyEnabledFeatures.containsKey(featureName)
+        }
+
+        override fun isReadOnlySystemDisabledFeature(featureName: String, version: Int): Boolean {
+            return readOnlyDisabledFeatures.contains(featureName)
+        }
+
+        override fun getReadOnlySystemEnabledFeatures(): ArrayMap<String, FeatureInfo> {
+            return readOnlyEnabledFeatures
+        }
+    }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index b782162..7f5da41 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.vibrator;
 
-import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -28,6 +26,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -89,17 +88,14 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
-import android.view.flags.Flags;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -168,9 +164,7 @@
     @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
     @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Mock
     private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@@ -800,7 +794,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS)
     public void vibrate_thenDeniedAppOps_getsCancelled() throws Throwable {
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
@@ -894,7 +888,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
+    @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
     public void vibrate_thenFgUserRequestsMute_getsCancelled() throws Throwable {
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
@@ -1331,6 +1325,37 @@
     }
 
     @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_VIBRATION_PIPELINE_ENABLED)
+    public void vibrate_withPipelineFlagEnabledAndShortEffect_continuesOngoingEffect()
+            throws Exception {
+        assumeTrue(mVibrationConfig.getVibrationPipelineMaxDurationMs() > 0);
+
+        mockVibrators(1);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+        fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        fakeVibrator.setSupportedPrimitives(
+                VibrationEffect.Composition.PRIMITIVE_CLICK,
+                VibrationEffect.Composition.PRIMITIVE_THUD);
+        fakeVibrator.setPrimitiveDuration(
+                mVibrationConfig.getVibrationPipelineMaxDurationMs() - 1);
+        VibratorManagerService service = createSystemReadyService();
+
+        HalVibration firstVibration = vibrateWithUid(service, /* uid= */ 123,
+                VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .compose(), HAPTIC_FEEDBACK_ATTRS);
+        HalVibration secondVibration = vibrateWithUid(service, /* uid= */ 456,
+                VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+                        .compose(), HAPTIC_FEEDBACK_ATTRS);
+        secondVibration.waitForEnd();
+
+        assertThat(fakeVibrator.getAllEffectSegments()).hasSize(2);
+        assertThat(firstVibration.getStatus()).isEqualTo(Status.FINISHED);
+        assertThat(secondVibration.getStatus()).isEqualTo(Status.FINISHED);
+    }
+
+    @Test
     public void vibrate_withInputDevices_vibratesInputDevices() throws Exception {
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
@@ -1512,6 +1537,7 @@
     }
 
     @Test
+    @EnableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API)
     public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception {
         // Deny permissions that would have been required for regular vibrations, and check that
         // the vibration proceed as expected to verify that haptic feedback does not need these
@@ -1520,8 +1546,6 @@
         denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
         denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
         denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
-        // Flag override to enable the scroll feedack constants to bypass interruption policies.
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
         mHapticFeedbackVibrationMap.put(
                 HapticFeedbackConstants.SCROLL_TICK,
                 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -1544,6 +1568,10 @@
     }
 
     @Test
+    @EnableFlags({
+            android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+            android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+    })
     public void performHapticFeedbackForInputDevice_doesNotRequireVibrateOrBypassPermissions()
             throws Exception {
         // Deny permissions that would have been required for regular vibrations, and check that
@@ -1553,9 +1581,6 @@
         denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
         denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
         denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
-        // Flag override to enable the scroll feedback constants to bypass interruption policies.
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         mHapticFeedbackVibrationMapSourceRotary.put(
                 HapticFeedbackConstants.SCROLL_TICK,
                 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -1628,12 +1653,14 @@
     }
 
     @Test
+    @EnableFlags({
+            android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+            android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+    })
     public void performHapticFeedbackForInputDevice_restrictedConstantsWithoutPermission_doesNotVibrate()
             throws Exception {
         // Deny permission to vibrate with restricted constants
         denyPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         // Public constant, no permission required
         mHapticFeedbackVibrationMapSourceRotary.put(
                 HapticFeedbackConstants.CONFIRM,
@@ -1697,9 +1724,9 @@
     }
 
     @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED)
     public void performHapticFeedbackForInputDevice_restrictedConstantsWithPermission_playsVibration()
             throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         // Grant permission to vibrate with restricted constants
         grantPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
         // Public constant, no permission required
@@ -1732,9 +1759,11 @@
     }
 
     @Test
+    @EnableFlags({
+            android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+            android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+    })
     public void performHapticFeedback_doesNotVibrateWhenVibratorInfoNotReady() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         denyPermission(android.Manifest.permission.VIBRATE);
         mHapticFeedbackVibrationMap.put(
                 HapticFeedbackConstants.KEYBOARD_TAP,
@@ -1767,9 +1796,11 @@
     }
 
     @Test
+    @EnableFlags({
+            android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+            android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+    })
     public void performHapticFeedback_doesNotVibrateForInvalidConstant() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         denyPermission(android.Manifest.permission.VIBRATE);
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
@@ -1791,7 +1822,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
     public void vibrate_vendorEffectsWithoutPermission_doesNotVibrate() throws Exception {
         // Deny permission to vibrate with vendor effects
         denyPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
@@ -1816,7 +1847,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
     public void vibrate_vendorEffectsWithPermission_successful() throws Exception {
         // Grant permission to vibrate with vendor effects
         grantPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
@@ -1904,7 +1935,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void vibrate_withAdaptiveHaptics_appliesCorrectAdaptiveScales() throws Exception {
         // Keep user settings the same as device default so only adaptive scale is applied.
         setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
@@ -1947,7 +1978,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled({
+    @EnableFlags({
             android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
             android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
     })
@@ -2418,7 +2449,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales() {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
@@ -2465,7 +2496,7 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    @DisableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone() {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
@@ -2585,7 +2616,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
+    @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
     public void onExternalVibration_thenFgUserRequestsMute_doNotCancelVibration() throws Throwable {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
@@ -3105,9 +3136,20 @@
         return vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs);
     }
 
+    private HalVibration vibrateWithUid(VibratorManagerService service, int uid,
+            VibrationEffect effect, VibrationAttributes attrs) {
+        return vibrateWithUidAndDevice(service, uid, Context.DEVICE_ID_DEFAULT,
+                CombinedVibration.createParallel(effect), attrs);
+    }
+
     private HalVibration vibrateWithDevice(VibratorManagerService service, int deviceId,
             CombinedVibration effect, VibrationAttributes attrs) {
-        HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect,
+        return vibrateWithUidAndDevice(service, UID, deviceId, effect, attrs);
+    }
+
+    private HalVibration vibrateWithUidAndDevice(VibratorManagerService service, int uid,
+            int deviceId, CombinedVibration effect, VibrationAttributes attrs) {
+        HalVibration vib = service.vibrateWithPermissionCheck(uid, deviceId, PACKAGE_NAME, effect,
                 attrs, "some reason", service);
         if (vib != null) {
             mPendingVibrations.add(vib);
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 6dc1b10..75a9cedf 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -80,6 +80,7 @@
     private float[] mFrequenciesHz;
     private float[] mOutputAccelerationsGs;
     private long mVendorEffectDuration = EFFECT_DURATION;
+    private long mPrimitiveDuration = EFFECT_DURATION;
 
     void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) {
         mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment);
@@ -171,7 +172,7 @@
             }
             long duration = 0;
             for (PrimitiveSegment primitive : primitives) {
-                duration += EFFECT_DURATION + primitive.getDelay();
+                duration += mPrimitiveDuration + primitive.getDelay();
                 recordEffectSegment(vibrationId, primitive);
             }
             applyLatency(mOnLatency);
@@ -381,6 +382,11 @@
         mVendorEffectDuration = durationMs;
     }
 
+    /** Set the duration of primitives in fake vibrator hardware. */
+    public void setPrimitiveDuration(long primitiveDuration) {
+        mPrimitiveDuration = primitiveDuration;
+    }
+
     /**
      * Set the maximum number of envelope effects control points supported in fake vibrator
      * hardware.
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index ab00bfd..1f16776 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -71,6 +71,7 @@
         "CtsSurfaceValidatorLib",
         "service-sdksandbox.impl",
         "com.android.window.flags.window-aconfig-java",
+        "android.view.inputmethod.flags-aconfig-java",
         "flag-junit",
     ],
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index d2cf03d..ee56210 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -41,11 +41,13 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.StatusBarManager;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.view.InsetsFrameProvider;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
@@ -525,6 +527,59 @@
         assertTrue(win1.getWindowFrames().hasInsetsChanged());
     }
 
+    /**
+     * This test verifies that after setting {@link WindowContainer#mExcludeInsetsTypes}, the IME
+     * insets have a height of zero (applied in {@link InsetsPolicy#adjustVisibilityForIme}).
+     */
+    @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+    @SetupWindows(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testExcludeImeInsets() {
+        final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+        final InsetsSource imeSource = new InsetsSource(ID_IME, ime());
+        imeSource.setVisible(true);
+        mImeWindow.mHasSurface = true;
+
+        final WindowState win = addWindow(TYPE_APPLICATION, "win1");
+        win.setRequestedVisibleTypes(0, ime());
+
+        win.mAboveInsetsState.addSource(imeSource);
+        win.mHasSurface = true;
+
+        DisplayContentTests.performLayout(mDisplayContent);
+        // IME should cover half of the app's window
+        final var winFrame = win.getFrame();
+        imeSource.setFrame(winFrame.left, winFrame.bottom / 2, winFrame.right, winFrame.bottom);
+        imeSource.setVisibleFrame(imeSource.getFrame());
+        DisplayContentTests.performLayout(mDisplayContent);
+
+        assertTrue(mImeWindow.isVisible());
+        assertTrue(win.isVisible());
+
+        displayPolicy.beginPostLayoutPolicyLw();
+        displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null);
+        displayPolicy.finishPostLayoutPolicyLw();
+
+        final var imeInsetsShown = win.getInsetsState().calculateInsets(win.getFrame(), ime(),
+                true);
+        assertEquals(new Rect(0, 0, 0, winFrame.bottom / 2), imeInsetsShown.toRect());
+
+
+        // Now we're setting the excludedInsetsTypes for the IME. The IME is still showing, but
+        // in this case, InsetsPolicy#adjustVisibilityForIme will override and dispatch IME
+        // insets with zero height.
+        win.setExcludeInsetsTypes(ime());
+
+        displayPolicy.beginPostLayoutPolicyLw();
+        displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null);
+        displayPolicy.finishPostLayoutPolicyLw();
+
+        final var imeInsetsHidden = win.getInsetsState().calculateInsets(win.getFrame(), ime(),
+                true);
+        assertEquals(Insets.NONE, imeInsetsHidden);
+    }
+
+
     private WindowState addNavigationBar() {
         final Binder owner = new Binder();
         final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index c1edae9..c30f70e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -183,15 +183,32 @@
         DeviceConfig.setProperties(mInitialConstrainDisplayApisFlags);
     }
 
-    private void setUpApp(DisplayContent display) {
-        mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
-        mActivity = mTask.getTopNonFinishingActivity();
-        doReturn(false).when(mActivity).isImmersiveMode(any());
+    private ActivityRecord setUpApp(DisplayContent display) {
+        return setUpApp(display, null /* appBuilder */);
     }
 
-    private void setUpDisplaySizeWithApp(int dw, int dh) {
+    private ActivityRecord setUpApp(DisplayContent display, ActivityBuilder appBuilder) {
+        // Use the real package name (com.android.frameworks.wmtests) so that
+        // EnableCompatChanges/DisableCompatChanges can take effect.
+        // Otherwise the fake WindowTestsBase.DEFAULT_COMPONENT_PACKAGE_NAME will make
+        // PlatformCompat#isChangeEnabledByPackageName always return default value.
+        final ComponentName componentName = ComponentName.createRelative(
+                mContext, SizeCompatTests.class.getName());
+        mTask = new TaskBuilder(mSupervisor).setDisplay(display).setComponent(componentName)
+                .build();
+        final ActivityBuilder builder = appBuilder != null ? appBuilder : new ActivityBuilder(mAtm);
+        mActivity = builder.setTask(mTask).setComponent(componentName).build();
+        doReturn(false).when(mActivity).isImmersiveMode(any());
+        return mActivity;
+    }
+
+    private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh) {
+        return setUpDisplaySizeWithApp(dw, dh, null /* appBuilder */);
+    }
+
+    private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh, ActivityBuilder appBuilder) {
         final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm, dw, dh);
-        setUpApp(builder.build());
+        return setUpApp(builder.build(), appBuilder);
     }
 
     @Test
@@ -352,15 +369,13 @@
                 .setCanRotate(false)
                 .setNotch(cutoutHeight)
                 .build();
-        setUpApp(display);
         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         mWm.mAppCompatConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
         mWm.mAppCompatConfiguration.setIsVerticalReachabilityEnabled(true);
 
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
-                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
-                .build();
+                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
 
         doReturn(true).when(activity).isImmersiveMode(any());
         addWindowToActivity(activity);
@@ -424,17 +439,16 @@
     @Test
     @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() {
-        final int notchHeight = 100;
-        setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build());
-
-        final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
         final float aspectRatio = 1.2f;
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm)
                 .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
                 .setMinAspectRatio(aspectRatio)
                 .setMaxAspectRatio(aspectRatio)
-                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
-                .build();
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE);
+        final int notchHeight = 100;
+        final ActivityRecord activity = setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800)
+                .setNotch(notchHeight).build(), activityBuilder);
+        final Rect displayBounds = activity.mDisplayContent.getWindowConfiguration().getBounds();
         final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
 
         // The parent configuration doesn't change since the first resolved configuration, so the
@@ -1327,12 +1341,8 @@
     public void testOverrideMinAspectRatioSmall_overridden() {
         final int dh = 1200;
         final int dw = 1000;
-        setUpDisplaySizeWithApp(dw, dh);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         final Rect bounds = activity.getBounds();
         assertEquals(dh, bounds.height());
@@ -1346,13 +1356,9 @@
     public void testOverrideMinAspectRatioSmall_notOverridden() {
         final int dh = 1200;
         final int dw = 1000;
-        setUpDisplaySizeWithApp(dw, dh);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
 
         // Activity's requested aspect ratio is larger than OVERRIDE_MIN_ASPECT_RATIO_SMALL,
         // so OVERRIDE_MIN_ASPECT_RATIO_SMALL is ignored.
@@ -1366,12 +1372,8 @@
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioMedium() {
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1388,13 +1390,8 @@
         final int notchHeight = 200;
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh)
                 .setNotch(notchHeight).setSystemDecorations(true).build();
-        mTask = new TaskBuilder(mSupervisor).setDisplay(display).build();
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setMinAspectRatio(2f)
-                .build();
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).setMinAspectRatio(2f));
 
         // The per-package override should have no effect, because the manifest aspect ratio is
         // larger (2:1)
@@ -1411,13 +1408,9 @@
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatioLargerThanManifest() {
-        setUpDisplaySizeWithApp(1400, 1600);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                        .setMinAspectRatio(1.1f));
 
         // The per-package override should have no effect, because the manifest aspect ratio is
         // larger (2:1)
@@ -1430,12 +1423,8 @@
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatioLarge() {
-        setUpDisplaySizeWithApp(1500, 1600);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1500, 1600,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // The per-package override forces the activity into a 16:9 aspect ratio
         assertEquals(1600, activity.getBounds().height());
@@ -1449,13 +1438,8 @@
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatio_Both() {
         // If multiple override aspect ratios are set, we should use the largest one
-
-        setUpDisplaySizeWithApp(1400, 1600);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // The per-package override forces the activity into a 16:9 aspect ratio
         assertEquals(1600, activity.getBounds().height());
@@ -1470,11 +1454,7 @@
     public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
         // isn't applied.
-
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200);
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1497,13 +1477,8 @@
     public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
         // isn't applied.
-
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1524,12 +1499,8 @@
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1550,10 +1521,7 @@
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
     public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() {
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200);
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1568,13 +1536,8 @@
     public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() {
         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
         // isn't applied.
-
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
@@ -1588,12 +1551,8 @@
         // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
         // OVERRIDE_MIN_ASPECT_RATIO being also set.
 
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1604,12 +1563,8 @@
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatioLargeForResizableAppInSplitScreen() {
-        setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         final TestSplitOrganizer organizer =
                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
@@ -2462,10 +2417,8 @@
     public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() {
         final int displayWidth = 1400;
         final int displayHeight = 1600;
-        setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+                new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2483,10 +2436,8 @@
     public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() {
         final int displayWidth = 1600;
         final int displayHeight = 1400;
-        setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+                new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2505,10 +2456,8 @@
     public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() {
         final int displayWidth = 1400;
         final int displayHeight = 1600;
-        setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+                new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2527,10 +2476,8 @@
     public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() {
         final int displayWidth = 1600;
         final int displayHeight = 1400;
-        setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+                new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2549,8 +2496,7 @@
         mAtm.mDevEnableNonResizableMultiWindow = true;
         final int screenWidth = 1800;
         final int screenHeight = 1000;
-        setUpDisplaySizeWithApp(screenWidth, screenHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight);
 
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate real display with top insets.
@@ -2585,8 +2531,7 @@
         mAtm.mDevEnableNonResizableMultiWindow = true;
         final int screenWidth = 1000;
         final int screenHeight = 1800;
-        setUpDisplaySizeWithApp(screenWidth, screenHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight);
 
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate real display with top insets.
@@ -2616,18 +2561,18 @@
 
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
     public void testOverrideMinAspectRatioExcludePortraitFullscreen() {
-        setUpDisplaySizeWithApp(2600, 1600);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        resizeDisplay(mDisplayContent, 2600, 1600);
+        mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
 
         // Non-resizable portrait activity
-        prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        final ActivityRecord activity = setUpApp(mDisplayContent, new ActivityBuilder(mAtm)
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // At first, OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply, because the
         // display is in landscape
@@ -2645,16 +2590,16 @@
 
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
     public void testOverrideMinAspectRatioExcludePortraitFullscreenNotApplied() {
         // In this test, the activity is not in fullscreen, so the override is not applied
-        setUpDisplaySizeWithApp(2600, 1600);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
+        resizeDisplay(mDisplayContent, 2600, 1600);
+        mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
 
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpApp(mDisplayContent);
 
         final TestSplitOrganizer organizer =
                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
@@ -2983,6 +2928,7 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
     public void testDisplayIgnoreOrientationRequest_orientationChangedToUnspecified() {
         // Set up a display in landscape and ignoring orientation request.
         setUpDisplaySizeWithApp(2800, 1400);
@@ -3437,6 +3383,7 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
     public void testSupportsNonResizableInSplitScreen_letterboxForAspectRatioRestriction() {
         // Support non resizable in multi window
         mAtm.mDevEnableNonResizableMultiWindow = true;
@@ -4050,9 +3997,9 @@
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
     public void testPortraitCloseToSquareDisplayWithTaskbar_insetsOverridden_notLetterboxed() {
+        final DisplayContent display = mDisplayContent;
         // Set up portrait close to square display.
-        setUpDisplaySizeWithApp(2200, 2280);
-        final DisplayContent display = mActivity.mDisplayContent;
+        resizeDisplay(display, 2200, 2280);
         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape.
         final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
@@ -4067,9 +4014,8 @@
             display.sendNewConfiguration();
         }
 
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // Activity should not be letterboxed and should have portrait app bounds even though
         // orientation is not respected with insets as insets have been decoupled.
@@ -4085,9 +4031,9 @@
     @Test
     @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() {
+        final DisplayContent display = mDisplayContent;
         // Set up portrait close to square display
-        setUpDisplaySizeWithApp(2200, 2280);
-        final DisplayContent display = mActivity.mDisplayContent;
+        resizeDisplay(display, 2200, 2280);
         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
         final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
@@ -4102,9 +4048,8 @@
             display.sendNewConfiguration();
         }
 
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         final Rect bounds = activity.getBounds();
         // Activity should be letterboxed and should have portrait app bounds
@@ -4116,9 +4061,9 @@
     @Test
     @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testFixedAspectRatioAppInPortraitCloseToSquareDisplay_notInSizeCompat() {
-        setUpDisplaySizeWithApp(2200, 2280);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        final DisplayContent dc = mActivity.mDisplayContent;
+        final DisplayContent dc = mDisplayContent;
+        resizeDisplay(dc, 2200, 2280);
+        dc.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
         final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
                 "navbar");
@@ -4132,11 +4077,10 @@
             dc.sendNewConfiguration();
         }
 
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(dc, new ActivityBuilder(mAtm)
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
-                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
-                .build();
+                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
         // To force config to update again but with the same landscape orientation.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
 
@@ -4174,13 +4118,10 @@
                 .setCutout(0, 100, 0, 150)
                 .build();
 
-        setUpApp(display);
-
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setMaxAspectRatio(2.1f)
-                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
-                .build();
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
 
         // The activity height is 2100 and the display's app bounds height is 2050, so the activity
         // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
@@ -4196,13 +4137,10 @@
                 .setCutout(100, 0, 150, 0)
                 .build();
 
-        setUpApp(display);
-
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setMaxAspectRatio(2.1f)
-                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
-                .build();
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
         // The activity width is 2100 and the display's app bounds width is 2250, so the activity
         // can be aligned inside parentAppBounds
         assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000));
@@ -4216,12 +4154,10 @@
                 .setCutout(100, 0, 150, 0)
                 .build();
 
-        setUpApp(display);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setMaxAspectRatio(2.1f)
-                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
-                .build();
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
         // The activity width is 2100 and the display's app bounds width is 2050, so the activity
         // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
         assertEquals(activity.getBounds(), display.getBounds());
@@ -5032,17 +4968,13 @@
      */
     private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode,
             @ScreenOrientation int screenOrientation) {
-        return getActivityBuilderOnSameTask()
+        return getActivityBuilderWithoutTask().setTask(mTask)
                 .setResizeMode(resizeMode)
                 .setSupportsSizeChanges(supportsSizeChanges)
                 .setScreenOrientation(screenOrientation)
                 .build();
     }
 
-    private ActivityBuilder getActivityBuilderOnSameTask() {
-        return getActivityBuilderWithoutTask().setTask(mTask);
-    }
-
     private ActivityBuilder getActivityBuilderWithoutTask() {
         return new ActivityBuilder(mAtm)
                 .setComponent(ComponentName.createRelative(mContext,
diff --git a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
index 90dff47..a1e1655 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
@@ -24,17 +24,16 @@
 import android.platform.test.annotations.Presubmit
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
+import com.android.test.input.MockInputManagerRule
 import java.util.concurrent.Executor
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
-import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.doAnswer
@@ -61,9 +60,8 @@
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
 
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Before
     fun setUp() {
@@ -72,7 +70,6 @@
         executor = HandlerExecutor(Handler(testLooper.looper))
         registeredListener = null
         monitoredDevices.clear()
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
         `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                 .thenReturn(inputManager)
@@ -92,7 +89,7 @@
             monitoredDevices.add(deviceId)
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerBatteryListener(anyInt(), any())
+        }.`when`(inputManagerRule.mock).registerBatteryListener(anyInt(), any())
 
         // Handle battery listener being unregistered.
         doAnswer {
@@ -108,14 +105,7 @@
             if (monitoredDevices.isEmpty()) {
                 registeredListener = null
             }
-        }.`when`(iInputManagerMock).unregisterBatteryListener(anyInt(), any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterBatteryListener(anyInt(), any())
     }
 
     private fun notifyBatteryStateChanged(
diff --git a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 080186e..3fc9ce1 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -45,15 +45,14 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.test.input.MockInputManagerRule;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -73,23 +72,22 @@
     private static final int DEVICE_ID = 1000;
     private static final int PLAYER_ID = 3;
 
-    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+    @Rule
+    public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule();
 
     private InputManager mInputManager;
 
-    @Mock private IInputManager mIInputManagerMock;
     private InputManagerGlobal.TestSession mInputManagerGlobalSession;
 
     @Before
     public void setUp() throws Exception {
         final Context context = spy(
                 new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
-        when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+        when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
 
-        when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+        when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn(
                 createInputDevice(DEVICE_ID));
 
-        mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
         mInputManager = new InputManager(context);
         when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
 
@@ -102,7 +100,7 @@
                 lightStatesById.put(lightIds[i], lightStates[i]);
             }
             return null;
-        }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID),
+        }).when(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID),
                 any(int[].class), any(LightState[].class), any(IBinder.class));
 
         doAnswer(invocation -> {
@@ -111,7 +109,7 @@
                 return lightStatesById.get(lightId);
             }
             return new LightState(0);
-        }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt());
+        }).when(mInputManagerRule.getMock()).getLightState(eq(DEVICE_ID), anyInt());
     }
 
     @After
@@ -130,7 +128,7 @@
 
     private void mockLights(Light[] lights) throws Exception {
         // Mock the Lights returned form InputManagerService
-        when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn(
+        when(mInputManagerRule.getMock().getLights(eq(DEVICE_ID))).thenReturn(
                 new ArrayList(Arrays.asList(lights)));
     }
 
@@ -151,7 +149,7 @@
 
         LightsManager lightsManager = device.getLightsManager();
         List<Light> lights = lightsManager.getLights();
-        verify(mIInputManagerMock).getLights(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getLights(eq(DEVICE_ID));
         assertEquals(lights, Arrays.asList(mockedLights));
     }
 
@@ -185,9 +183,9 @@
                 .build());
         IBinder token = session.getToken();
 
-        verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+        verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID),
                 any(String.class), eq(token));
-        verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
+        verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
                 eq(states), eq(token));
 
         // Then all 3 should turn on.
@@ -204,7 +202,7 @@
 
         // close session
         session.close();
-        verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+        verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token));
     }
 
     @Test
@@ -232,9 +230,9 @@
                 .build());
         IBinder token = session.getToken();
 
-        verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+        verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID),
                 any(String.class), eq(token));
-        verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
+        verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
                 eq(states), eq(token));
 
         // Verify the light state
@@ -245,7 +243,7 @@
 
         // close session
         session.close();
-        verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+        verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token));
     }
 
     @Test
diff --git a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
index 0e3c200..3057f5d 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
+++ b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -41,16 +41,13 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.test.input.MockInputManagerRule;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
 
 import java.util.List;
 import java.util.concurrent.BlockingQueue;
@@ -70,43 +67,34 @@
 
     private static final int DEVICE_ID = 1000;
 
-    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+    @Rule
+    public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule();
 
     private InputManager mInputManager;
     private IInputSensorEventListener mIInputSensorEventListener;
     private final Object mLock = new Object();
 
-    @Mock private IInputManager mIInputManagerMock;
-    private InputManagerGlobal.TestSession mInputManagerGlobalSession;
-
     @Before
     public void setUp() throws Exception {
         final Context context = spy(
                 new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
-        mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
         mInputManager = new InputManager(context);
         when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
 
-        when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+        when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
 
-        when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+        when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn(
                 createInputDeviceWithSensor(DEVICE_ID));
 
-        when(mIInputManagerMock.getSensorList(eq(DEVICE_ID))).thenReturn(new InputSensorInfo[] {
-                createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER),
-                createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)});
+        when(mInputManagerRule.getMock().getSensorList(eq(DEVICE_ID))).thenReturn(
+                new InputSensorInfo[]{
+                        createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER),
+                        createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)});
 
-        when(mIInputManagerMock.enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt()))
+        when(mInputManagerRule.getMock().enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt()))
                 .thenReturn(true);
 
-        when(mIInputManagerMock.registerSensorListener(any())).thenReturn(true);
-    }
-
-    @After
-    public void tearDown() {
-        if (mInputManagerGlobalSession != null) {
-            mInputManagerGlobalSession.close();
-        }
+        when(mInputManagerRule.getMock().registerSensorListener(any())).thenReturn(true);
     }
 
     private class InputTestSensorEventListener implements SensorEventListener {
@@ -175,13 +163,13 @@
 
         SensorManager sensorManager = device.getSensorManager();
         List<Sensor> accelList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
-        verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
         assertEquals(1, accelList.size());
         assertEquals(DEVICE_ID, accelList.get(0).getId());
         assertEquals(Sensor.TYPE_ACCELEROMETER, accelList.get(0).getType());
 
         List<Sensor> gyroList = sensorManager.getSensorList(Sensor.TYPE_GYROSCOPE);
-        verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
         assertEquals(1, gyroList.size());
         assertEquals(DEVICE_ID, gyroList.get(0).getId());
         assertEquals(Sensor.TYPE_GYROSCOPE, gyroList.get(0).getType());
@@ -197,11 +185,11 @@
 
         List<Sensor> gameRotationList = sensorManager.getSensorList(
                 Sensor.TYPE_GAME_ROTATION_VECTOR);
-        verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
         assertEquals(0, gameRotationList.size());
 
         List<Sensor> gravityList = sensorManager.getSensorList(Sensor.TYPE_GRAVITY);
-        verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
         assertEquals(0, gravityList.size());
     }
 
@@ -218,13 +206,13 @@
             mIInputSensorEventListener = invocation.getArgument(0);
             assertNotNull(mIInputSensorEventListener);
             return true;
-        }).when(mIInputManagerMock).registerSensorListener(any());
+        }).when(mInputManagerRule.getMock()).registerSensorListener(any());
 
         InputTestSensorEventListener listener = new InputTestSensorEventListener();
         assertTrue(sensorManager.registerListener(listener, sensor,
                 SensorManager.SENSOR_DELAY_NORMAL));
-        verify(mIInputManagerMock).registerSensorListener(any());
-        verify(mIInputManagerMock).enableSensor(eq(DEVICE_ID), eq(sensor.getType()),
+        verify(mInputManagerRule.getMock()).registerSensorListener(any());
+        verify(mInputManagerRule.getMock()).enableSensor(eq(DEVICE_ID), eq(sensor.getType()),
                 anyInt(), anyInt());
 
         float[] values = new float[] {0.12f, 9.8f, 0.2f};
@@ -240,7 +228,7 @@
         }
 
         sensorManager.unregisterListener(listener);
-        verify(mIInputManagerMock).disableSensor(eq(DEVICE_ID), eq(sensor.getType()));
+        verify(mInputManagerRule.getMock()).disableSensor(eq(DEVICE_ID), eq(sensor.getType()));
     }
 
 }
diff --git a/tests/Input/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt
index 152dde9..4c6bb84 100644
--- a/tests/Input/src/android/hardware/input/InputManagerTest.kt
+++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt
@@ -23,18 +23,16 @@
 import android.view.DisplayInfo
 import android.view.InputDevice
 import androidx.test.core.app.ApplicationProvider
-import org.junit.After
-import org.junit.Assert.assertNotNull
+import com.android.test.input.MockInputManagerRule
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoJUnitRunner
 
 /**
@@ -54,35 +52,23 @@
     }
 
     @get:Rule
-    val rule = MockitoJUnit.rule()!!
+    val inputManagerRule = MockInputManagerRule()
 
     private lateinit var devicesChangedListener: IInputDevicesChangedListener
     private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         inputManager = InputManager(context)
         `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
-        `when`(iInputManager.inputDeviceIds).then {
+        `when`(inputManagerRule.mock.inputDeviceIds).then {
             deviceGenerationMap.keys.toIntArray()
         }
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     private fun notifyDeviceChanged(
         deviceId: Int,
         associatedDisplayId: Int,
@@ -92,7 +78,7 @@
             ?: throw IllegalArgumentException("Device $deviceId was never added!")
         deviceGenerationMap[deviceId] = generation
 
-        `when`(iInputManager.getInputDevice(deviceId))
+        `when`(inputManagerRule.mock.getInputDevice(deviceId))
             .thenReturn(createInputDevice(deviceId, associatedDisplayId, usiVersion, generation))
         val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
         if (::devicesChangedListener.isInitialized) {
@@ -125,7 +111,7 @@
     fun testUsiVersionFallBackToDisplayConfig() {
         addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null)
 
-        `when`(iInputManager.getHostUsiVersionFromDisplayConfig(eq(42)))
+        `when`(inputManagerRule.mock.getHostUsiVersionFromDisplayConfig(eq(42)))
             .thenReturn(HostUsiVersion(9, 8))
         val usiVersion = inputManager.getHostUsiVersion(createDisplay(42))
         assertEquals(HostUsiVersion(9, 8), usiVersion)
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
index 072341d..e99c814 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
@@ -18,20 +18,17 @@
 
 import android.content.Context
 import android.content.ContextWrapper
-import android.os.Handler
 import android.os.IBinder
-import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.platform.test.flag.junit.SetFlagsRule
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.`when`
@@ -69,20 +66,16 @@
 
     @get:Rule
     val rule = SetFlagsRule()
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
-    private val testLooper = TestLooper()
     private var registeredListener: IKeyGestureHandler? = null
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
 
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
         `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                 .thenReturn(inputManager)
@@ -97,7 +90,7 @@
             }
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerKeyGestureHandler(any())
+        }.`when`(inputManagerRule.mock).registerKeyGestureHandler(any())
 
         // Handle key gesture handler being unregistered.
         doAnswer {
@@ -108,14 +101,7 @@
             }
             registeredListener = null
             null
-        }.`when`(iInputManagerMock).unregisterKeyGestureHandler(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(any())
     }
 
     private fun handleKeyGestureEvent(event: KeyGestureEvent) {
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
index ca9de60..cf0bfcc 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
@@ -26,20 +26,19 @@
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnitRunner
+import com.android.test.input.MockInputManagerRule
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlin.test.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
 
 /**
  * Tests for [InputManager.KeyGestureEventListener].
@@ -63,21 +62,18 @@
 
     @get:Rule
     val rule = SetFlagsRule()
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     private val testLooper = TestLooper()
     private val executor = HandlerExecutor(Handler(testLooper.looper))
     private var registeredListener: IKeyGestureEventListener? = null
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
 
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
         `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                 .thenReturn(inputManager)
@@ -92,7 +88,7 @@
             }
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerKeyGestureEventListener(any())
+        }.`when`(inputManagerRule.mock).registerKeyGestureEventListener(any())
 
         // Handle key gesture event listener being unregistered.
         doAnswer {
@@ -103,14 +99,7 @@
             }
             registeredListener = null
             null
-        }.`when`(iInputManagerMock).unregisterKeyGestureEventListener(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterKeyGestureEventListener(any())
     }
 
     private fun notifyKeyGestureEvent(event: KeyGestureEvent) {
diff --git a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
index 23135b2..d25dee1 100644
--- a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
@@ -24,22 +24,20 @@
 import android.platform.test.annotations.Presubmit
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoJUnitRunner
+import com.android.test.input.MockInputManagerRule
 import java.util.concurrent.Executor
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlin.test.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
 
 /**
  * Tests for [InputManager.KeyboardBacklightListener].
@@ -50,23 +48,19 @@
 @Presubmit
 @RunWith(MockitoJUnitRunner::class)
 class KeyboardBacklightListenerTest {
+
     @get:Rule
-    val rule = MockitoJUnit.rule()!!
+    val inputManagerRule = MockInputManagerRule()
 
     private lateinit var testLooper: TestLooper
     private var registeredListener: IKeyboardBacklightListener? = null
     private lateinit var executor: Executor
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
 
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         testLooper = TestLooper()
         executor = HandlerExecutor(Handler(testLooper.looper))
         registeredListener = null
@@ -84,7 +78,7 @@
             }
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerKeyboardBacklightListener(any())
+        }.`when`(inputManagerRule.mock).registerKeyboardBacklightListener(any())
 
         // Handle keyboard backlight listener being unregistered.
         doAnswer {
@@ -95,14 +89,7 @@
             }
             registeredListener = null
             null
-        }.`when`(iInputManagerMock).unregisterKeyboardBacklightListener(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterKeyboardBacklightListener(any())
     }
 
     private fun notifyKeyboardBacklightChanged(
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index bcd56ad..1c2a053 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -27,21 +27,20 @@
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnitRunner
+import com.android.test.input.MockInputManagerRule
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
 
 /**
  * Tests for [InputManager.StickyModifierStateListener].
@@ -59,21 +58,18 @@
 
     @get:Rule
     val rule = SetFlagsRule()
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     private val testLooper = TestLooper()
     private val executor = HandlerExecutor(Handler(testLooper.looper))
     private var registeredListener: IStickyModifierStateListener? = null
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
 
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
         `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                 .thenReturn(inputManager)
@@ -88,7 +84,7 @@
             }
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerStickyModifierStateListener(any())
+        }.`when`(inputManagerRule.mock).registerStickyModifierStateListener(any())
 
         // Handle sticky modifier state listener being unregistered.
         doAnswer {
@@ -99,14 +95,7 @@
             }
             registeredListener = null
             null
-        }.`when`(iInputManagerMock).unregisterStickyModifierStateListener(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterStickyModifierStateListener(any())
     }
 
     private fun notifyStickyModifierStateChanged(modifierState: Int, lockedModifierState: Int) {
diff --git a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
index f2724e6..044f11d 100644
--- a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
@@ -27,7 +27,6 @@
 import android.hardware.input.IInputDeviceBatteryListener
 import android.hardware.input.IInputDeviceBatteryState
 import android.hardware.input.IInputDevicesChangedListener
-import android.hardware.input.IInputManager
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
 import android.os.Binder
@@ -42,13 +41,13 @@
 import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS
 import com.android.server.input.BatteryController.UEventBatteryListener
 import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS
+import com.android.test.input.MockInputManagerRule
 import org.hamcrest.Description
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.hamcrest.Matchers
 import org.hamcrest.TypeSafeMatcher
 import org.hamcrest.core.IsEqual.equalTo
-import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -184,12 +183,12 @@
 
     @get:Rule
     val rule = MockitoJUnit.rule()!!
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Mock
     private lateinit var native: NativeInputManagerService
     @Mock
-    private lateinit var iInputManager: IInputManager
-    @Mock
     private lateinit var uEventManager: UEventManager
     @Mock
     private lateinit var bluetoothBatteryManager: BluetoothBatteryManager
@@ -205,10 +204,9 @@
     fun setup() {
         context = TestableContext(ApplicationProvider.getApplicationContext())
         testLooper = TestLooper()
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         val inputManager = InputManager(context)
         context.addMockSystemService(InputManager::class.java, inputManager)
-        `when`(iInputManager.inputDeviceIds).then {
+        `when`(inputManagerRule.mock.inputDeviceIds).then {
             deviceGenerationMap.keys.toIntArray()
         }
         addInputDevice(DEVICE_ID)
@@ -218,18 +216,11 @@
             bluetoothBatteryManager)
         batteryController.systemRunning()
         val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java)
-        verify(iInputManager).registerInputDevicesChangedListener(listenerCaptor.capture())
+        verify(inputManagerRule.mock).registerInputDevicesChangedListener(listenerCaptor.capture())
         devicesChangedListener = listenerCaptor.value
         testLooper.dispatchAll()
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     private fun notifyDeviceChanged(
             deviceId: Int,
         hasBattery: Boolean = true,
@@ -239,7 +230,7 @@
             ?: throw IllegalArgumentException("Device $deviceId was never added!")
         deviceGenerationMap[deviceId] = generation
 
-        `when`(iInputManager.getInputDevice(deviceId))
+        `when`(inputManagerRule.mock.getInputDevice(deviceId))
             .thenReturn(createInputDevice(deviceId, hasBattery, supportsUsi, generation))
         val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
         if (::devicesChangedListener.isInitialized) {
@@ -657,9 +648,9 @@
 
     @Test
     fun testRegisterBluetoothListenerForMonitoredBluetoothDevices() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
-        `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
             .thenReturn("11:22:33:44:55:66")
         addInputDevice(BT_DEVICE_ID)
         testLooper.dispatchNext()
@@ -686,7 +677,7 @@
         batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID)
         verify(bluetoothBatteryManager, never()).removeBatteryListener(any())
 
-        `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
             .thenReturn(null)
         notifyDeviceChanged(SECOND_BT_DEVICE_ID)
         testLooper.dispatchNext()
@@ -695,7 +686,7 @@
 
     @Test
     fun testNotifiesBluetoothBatteryChanges() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
         addInputDevice(BT_DEVICE_ID)
@@ -716,7 +707,7 @@
     @Test
     fun testBluetoothBatteryIsPrioritized() {
         `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device")
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
         `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98)
@@ -745,7 +736,7 @@
     @Test
     fun testFallBackToNativeBatteryStateWhenBluetoothStateInvalid() {
         `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device")
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
         `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98)
@@ -776,9 +767,9 @@
 
     @Test
     fun testRegisterBluetoothMetadataListenerForMonitoredBluetoothDevices() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
-        `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
             .thenReturn("11:22:33:44:55:66")
         addInputDevice(BT_DEVICE_ID)
         testLooper.dispatchNext()
@@ -811,7 +802,7 @@
         verify(bluetoothBatteryManager)
             .removeMetadataListener("AA:BB:CC:DD:EE:FF", metadataListener1.value)
 
-        `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
             .thenReturn(null)
         notifyDeviceChanged(SECOND_BT_DEVICE_ID)
         testLooper.dispatchNext()
@@ -821,7 +812,7 @@
 
     @Test
     fun testNotifiesBluetoothMetadataBatteryChanges() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
                 BluetoothDevice.METADATA_MAIN_BATTERY))
@@ -861,7 +852,7 @@
 
     @Test
     fun testBluetoothMetadataBatteryIsPrioritized() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
         `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 351ec463..927958e 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -93,14 +93,12 @@
         )
     }
 
-    @JvmField
-    @Rule
+    @get:Rule
     val extendedMockitoRule =
         ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java)
             .mockStatic(PermissionChecker::class.java).build()!!
 
-    @JvmField
-    @Rule
+    @get:Rule
     val setFlagsRule = SetFlagsRule()
 
     @get:Rule
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 4ae06a4..7526737 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -46,6 +46,7 @@
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import junitparams.JUnitParamsRunner
 import junitparams.Parameters
+import org.junit.After
 import org.junit.Assert.assertArrayEquals
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
@@ -128,6 +129,13 @@
         currentPid = Process.myPid()
     }
 
+    @After
+    fun teardown() {
+        if (this::inputManagerGlobalSession.isInitialized) {
+            inputManagerGlobalSession.close()
+        }
+    }
+
     private fun setupBehaviors() {
         Mockito.`when`(
             resources.getBoolean(
diff --git a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
index f74fd72..4f4c97be 100644
--- a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
@@ -18,16 +18,18 @@
 
 import android.content.Context
 import android.content.ContextWrapper
-import android.hardware.input.IInputManager
 import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
 import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.provider.Settings
 import android.view.InputDevice
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Rule
@@ -35,10 +37,6 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.junit.MockitoJUnit
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
 
 private fun createKeyboard(deviceId: Int): InputDevice =
     InputDevice.Builder()
@@ -73,15 +71,15 @@
     @get:Rule
     val rule = MockitoJUnit.rule()!!
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
+
     @Mock
     private lateinit var native: NativeInputManagerService
     private lateinit var mKeyRemapper: KeyRemapper
     private lateinit var context: Context
     private lateinit var dataStore: PersistentDataStore
     private lateinit var testLooper: TestLooper
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
 
     @Before
     fun setup() {
@@ -104,25 +102,17 @@
             dataStore,
             testLooper.looper
         )
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         val inputManager = InputManager(context)
         Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
             .thenReturn(inputManager)
-        Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
     }
 
     @Test
     fun testKeyRemapping_whenRemappingEnabled() {
         ModifierRemappingFlag(true).use {
             val keyboard = createKeyboard(DEVICE_ID)
-            Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
+            Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
 
             for (i in REMAPPABLE_KEYS.indices) {
                 val fromKeyCode = REMAPPABLE_KEYS[i]
@@ -160,7 +150,7 @@
     fun testKeyRemapping_whenRemappingDisabled() {
         ModifierRemappingFlag(false).use {
             val keyboard = createKeyboard(DEVICE_ID)
-            Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
+            Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
 
             mKeyRemapper.remapKey(REMAPPABLE_KEYS[0], REMAPPABLE_KEYS[1])
             testLooper.dispatchAll()
diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
index 59aa96c..58fb4e1 100644
--- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
@@ -20,11 +20,9 @@
 import android.content.Context
 import android.content.ContextWrapper
 import android.graphics.Color
-import android.hardware.input.IInputManager
 import android.hardware.input.IKeyboardBacklightListener
 import android.hardware.input.IKeyboardBacklightState
 import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
 import android.hardware.lights.Light
 import android.os.UEventObserver
 import android.os.test.TestLooper
@@ -35,7 +33,11 @@
 import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL
 import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS
 import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotEquals
@@ -52,10 +54,6 @@
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
 
 private fun createKeyboard(deviceId: Int): InputDevice =
     InputDevice.Builder()
@@ -100,10 +98,10 @@
 
     @get:Rule
     val rule = MockitoJUnit.rule()!!
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Mock
-    private lateinit var iInputManager: IInputManager
-    @Mock
     private lateinit var native: NativeInputManagerService
     @Mock
     private lateinit var uEventManager: UEventManager
@@ -111,7 +109,6 @@
     private lateinit var context: Context
     private lateinit var dataStore: PersistentDataStore
     private lateinit var testLooper: TestLooper
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
     private var lightColorMap: HashMap<Int, Int> = HashMap()
     private var lastBacklightState: KeyboardBacklightState? = null
     private var sysfsNodeChanges = 0
@@ -134,10 +131,9 @@
         testLooper = TestLooper()
         keyboardBacklightController = KeyboardBacklightController(context, native, dataStore,
                 testLooper.looper, FakeAnimatorFactory(), uEventManager)
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         val inputManager = InputManager(context)
         `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
-        `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+        `when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
         `when`(native.setLightColor(anyInt(), anyInt(), anyInt())).then {
             val args = it.arguments
             lightColorMap.put(args[1] as Int, args[2] as Int)
@@ -152,13 +148,6 @@
         }
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     @Test
     fun testKeyboardBacklightIncrementDecrement() {
         KeyboardBacklightFlags(
@@ -168,8 +157,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -186,8 +176,9 @@
         ).use {
             val keyboardWithoutBacklight = createKeyboard(DEVICE_ID)
             val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithoutBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             incrementKeyboardBacklight(DEVICE_ID)
@@ -205,8 +196,9 @@
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
             val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(
                 listOf(
                     keyboardBacklight,
                     keyboardInputLight
@@ -230,8 +222,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
 
             for (level in 1 until DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size) {
                 dataStore.setKeyboardBacklightBrightness(
@@ -263,7 +256,8 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
             dataStore.setKeyboardBacklightBrightness(
                 keyboardWithBacklight.descriptor,
                 LIGHT_ID,
@@ -278,7 +272,7 @@
                 lightColorMap.isEmpty()
             )
 
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceChanged(DEVICE_ID)
             keyboardBacklightController.notifyUserActivity()
             testLooper.dispatchNext()
@@ -300,8 +294,9 @@
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
             val maxLevel = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size - 1
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             // Register backlight listener
@@ -352,8 +347,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             dataStore.setKeyboardBacklightBrightness(
                 keyboardWithBacklight.descriptor,
                 LIGHT_ID,
@@ -388,8 +384,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             dataStore.setKeyboardBacklightBrightness(
                 keyboardWithBacklight.descriptor,
                 LIGHT_ID,
@@ -482,8 +479,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             incrementKeyboardBacklight(DEVICE_ID)
@@ -511,8 +509,9 @@
             val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
                     suggestedLevels)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -531,8 +530,9 @@
             val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) }
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
                     suggestedLevels)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -551,8 +551,9 @@
             val suggestedLevels = intArrayOf(22, 63, 135, 196)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
                     suggestedLevels)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             // Framework will add the lowest and maximum levels if not provided via config
@@ -572,8 +573,10 @@
             val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
                     suggestedLevels)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID))
+                .thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             // Framework will drop out of bound levels in the config
@@ -591,8 +594,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
 
             dataStore.setKeyboardBacklightBrightness(
                 keyboardWithBacklight.descriptor,
@@ -619,8 +623,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
 
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
             incrementKeyboardBacklight(DEVICE_ID)
@@ -642,8 +647,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
             sendAmbientBacklightValue(1)
             assertEquals(
@@ -671,8 +677,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
             sendAmbientBacklightValue(254)
             assertEquals(
@@ -701,8 +708,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
             incrementKeyboardBacklight(DEVICE_ID)
             assertEquals(
diff --git a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
index c073c7a..ff8a9ba 100644
--- a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
@@ -23,9 +23,7 @@
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.content.pm.ServiceInfo
-import android.hardware.input.IInputManager
 import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyGlyphMap.KeyCombination
 import android.os.Bundle
 import android.os.test.TestLooper
@@ -36,8 +34,8 @@
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
 import com.android.hardware.input.Flags
+import com.android.test.input.MockInputManagerRule
 import com.android.test.input.R
-import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
@@ -65,30 +63,24 @@
         const val RECEIVER_NAME = "DummyReceiver"
     }
 
-    @JvmField
-    @Rule(order = 0)
+    @get:Rule
     val setFlagsRule = SetFlagsRule()
-
-    @JvmField
-    @Rule(order = 1)
+    @get:Rule
     val mockitoRule = MockitoJUnit.rule()!!
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Mock
     private lateinit var packageManager: PackageManager
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
-
     private lateinit var keyboardGlyphManager: KeyboardGlyphManager
     private lateinit var context: Context
     private lateinit var testLooper: TestLooper
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
     private lateinit var keyboardDevice: InputDevice
 
     @Before
     fun setup() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         testLooper = TestLooper()
         keyboardGlyphManager = KeyboardGlyphManager(context, testLooper.looper)
 
@@ -98,21 +90,14 @@
         testLooper.dispatchAll()
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     private fun setupInputDevices() {
         val inputManager = InputManager(context)
         Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
             .thenReturn(inputManager)
 
         keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "")
-        Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
-        Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+        Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
     }
 
     private fun setupBroadcastReceiver() {
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 301c0e6..d6654cc 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -24,11 +24,10 @@
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.content.pm.ServiceInfo
-import android.hardware.input.KeyboardLayoutSelectionResult
-import android.hardware.input.IInputManager
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyboardLayout
+import android.hardware.input.KeyboardLayoutSelectionResult
 import android.icu.util.ULocale
 import android.os.Bundle
 import android.os.test.TestLooper
@@ -42,8 +41,12 @@
 import com.android.internal.os.KeyboardConfiguredProto
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.test.input.MockInputManagerRule
 import com.android.test.input.R
-import org.junit.After
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
@@ -53,10 +56,6 @@
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
 
 fun createKeyboard(
     deviceId: Int,
@@ -120,8 +119,8 @@
     val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
             .mockStatic(FrameworkStatsLog::class.java).build()!!
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Mock
     private lateinit var native: NativeInputManagerService
@@ -148,7 +147,6 @@
     @Before
     fun setup() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
             override fun openRead(): InputStream? {
                 throw FileNotFoundException()
@@ -171,13 +169,6 @@
         setupIme()
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     private fun setupInputDevices() {
         val inputManager = InputManager(context)
         Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
@@ -191,19 +182,19 @@
                 DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "dvorak")
         englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID,
                 DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "qwerty")
-        Mockito.`when`(iInputManager.inputDeviceIds)
+        Mockito.`when`(inputManagerRule.mock.inputDeviceIds)
             .thenReturn(intArrayOf(
                 DEVICE_ID,
                 VENDOR_SPECIFIC_DEVICE_ID,
                 ENGLISH_DVORAK_DEVICE_ID,
                 ENGLISH_QWERTY_DEVICE_ID
             ))
-        Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
-        Mockito.`when`(iInputManager.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID))
             .thenReturn(vendorSpecificKeyboardDevice)
-        Mockito.`when`(iInputManager.getInputDevice(ENGLISH_DVORAK_DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_DVORAK_DEVICE_ID))
             .thenReturn(englishDvorakKeyboardDevice)
-        Mockito.`when`(iInputManager.getInputDevice(ENGLISH_QWERTY_DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_QWERTY_DEVICE_ID))
                 .thenReturn(englishQwertyKeyboardDevice)
     }
 
diff --git a/tests/Input/src/com/android/test/input/MockInputManagerRule.kt b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
new file mode 100644
index 0000000..cef9856
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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.test.input
+
+import android.hardware.input.IInputManager
+import android.hardware.input.InputManagerGlobal
+import org.junit.rules.ExternalResource
+import org.mockito.Mockito
+
+/**
+ * A test rule that temporarily replaces the [IInputManager] connection to the server with a mock
+ * to be used for testing.
+ */
+class MockInputManagerRule : ExternalResource() {
+
+    private lateinit var session: InputManagerGlobal.TestSession
+
+    val mock: IInputManager = Mockito.mock(IInputManager::class.java)
+
+    override fun before() {
+        session = InputManagerGlobal.createTestSession(mock)
+    }
+
+    override fun after() {
+        if (this::session.isInitialized) {
+            session.close()
+        }
+    }
+}
diff --git a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
index 1a29c0f..c61a250 100644
--- a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
+++ b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
@@ -21,7 +21,6 @@
 import android.graphics.PointF
 import android.hardware.input.InputManager
 import android.os.ParcelFileDescriptor
-import android.platform.test.annotations.FlakyTest
 import android.util.Log
 import android.util.Size
 import android.view.InputEvent
@@ -108,7 +107,6 @@
         parser = InputJsonParser(instrumentation.context)
     }
 
-    @FlakyTest(bugId = 366602644)
     @Test
     fun testEvemuRecording() {
         VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>(
diff --git a/tests/testables/src/android/testing/TestWithLooperRule.java b/tests/testables/src/android/testing/TestWithLooperRule.java
index 10df17f..6a8e142 100644
--- a/tests/testables/src/android/testing/TestWithLooperRule.java
+++ b/tests/testables/src/android/testing/TestWithLooperRule.java
@@ -100,6 +100,9 @@
                     case "ExpectException":
                         next = this.getNextStatement(next, "next");
                         break;
+                    case "UiThreadStatement":
+                        next = this.getNextStatement(next, "base");
+                        break;
                     default:
                         throw new Exception(
                                 String.format("Unexpected Statement received: [%s]",
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 37b1687..5983cf1 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -28,8 +28,19 @@
 namespace aapt {
 
 static constexpr ApiVersion sDevelopmentSdkLevel = 10000;
+
+// clang-format off
 static constexpr StringPiece sDevelopmentSdkCodeNames[] = {
-    "Q"sv, "R"sv, "S"sv, "Sv2"sv, "Tiramisu"sv, "UpsideDownCake"sv, "VanillaIceCream"sv};
+    "Q"sv,
+    "R"sv,
+    "S"sv,
+    "Sv2"sv,
+    "Tiramisu"sv,
+    "UpsideDownCake"sv,
+    "VanillaIceCream"sv,
+    "Baklava"sv,
+};
+// clang-format on
 
 static constexpr auto sPrivacySandboxSuffix = "PrivacySandbox"sv;
 
diff --git a/tools/hoststubgen/.gitignore b/tools/hoststubgen/.gitignore
deleted file mode 100644
index 6453bde..0000000
--- a/tools/hoststubgen/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-out/
-*-out/
-*.log
diff --git a/tools/hoststubgen/OWNERS b/tools/hoststubgen/OWNERS
deleted file mode 100644
index 3d8888d..0000000
--- a/tools/hoststubgen/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:platform/frameworks/base:/ravenwood/OWNERS
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
deleted file mode 100644
index 856e6ee..0000000
--- a/tools/hoststubgen/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "imports": [
-    {
-      "path": "frameworks/base/ravenwood"
-    }
-  ]
-}
diff --git a/tools/hoststubgen/hoststubgen/.gitignore b/tools/hoststubgen/hoststubgen/.gitignore
deleted file mode 100644
index 0f38407..0000000
--- a/tools/hoststubgen/hoststubgen/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-framework-all-stub-out
\ No newline at end of file
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index cba521e..196b5e7 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -22,8 +22,6 @@
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeSpec
-import java.util.HashMap
-import java.util.Map
 import javax.lang.model.element.Modifier
 
 /*
@@ -52,7 +50,7 @@
  *     public static boolean hasFeatureAutomotive(Context context);
  *     public static boolean hasFeatureLeanback(Context context);
  *     public static Boolean maybeHasFeature(String feature, int version);
- *     public static ArrayMap<String, FeatureInfo> getCompileTimeAvailableFeatures();
+ *     public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures();
  * }
  * </pre>
  */
@@ -63,6 +61,7 @@
     private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
     private val CONTEXT_CLASS = ClassName.get("android.content", "Context")
     private val FEATUREINFO_CLASS = ClassName.get("android.content.pm", "FeatureInfo")
+    private val ARRAYMAP_CLASS = ClassName.get("android.util", "ArrayMap")
     private val ASSUME_TRUE_CLASS =
         ClassName.get("com.android.aconfig.annotations", "AssumeTrueForR8")
     private val ASSUME_FALSE_CLASS =
@@ -291,19 +290,19 @@
         features: Collection<FeatureInfo>,
     ) {
         val methodBuilder =
-                MethodSpec.methodBuilder("getCompileTimeAvailableFeatures")
+                MethodSpec.methodBuilder("getReadOnlySystemEnabledFeatures")
                 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                 .addAnnotation(ClassName.get("android.annotation", "NonNull"))
                 .addJavadoc("Gets features marked as available at compile-time, keyed by name." +
                         "\n\n@hide")
                 .returns(ParameterizedTypeName.get(
-                        ClassName.get(Map::class.java),
+                        ARRAYMAP_CLASS,
                         ClassName.get(String::class.java),
                         FEATUREINFO_CLASS))
 
         val availableFeatures = features.filter { it.readonly && it.version != null }
-        methodBuilder.addStatement("Map<String, FeatureInfo> features = new \$T<>(\$L)",
-                HashMap::class.java, availableFeatures.size)
+        methodBuilder.addStatement("\$T<String, FeatureInfo> features = new \$T<>(\$L)",
+                ARRAYMAP_CLASS, ARRAYMAP_CLASS, availableFeatures.size)
         if (!availableFeatures.isEmpty()) {
             methodBuilder.addStatement("FeatureInfo fi = new FeatureInfo()")
         }
diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
index edbfc42..ee97b26 100644
--- a/tools/systemfeatures/tests/golden/RoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
@@ -13,10 +13,9 @@
 import android.content.Context;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import android.util.ArrayMap;
 import com.android.aconfig.annotations.AssumeFalseForR8;
 import com.android.aconfig.annotations.AssumeTrueForR8;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * @hide
@@ -94,8 +93,8 @@
      * @hide
      */
     @NonNull
-    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
-        Map<String, FeatureInfo> features = new HashMap<>(2);
+    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(2);
         FeatureInfo fi = new FeatureInfo();
         fi.name = PackageManager.FEATURE_WATCH;
         fi.version = 1;
diff --git a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
index bf7a006..40c7db7 100644
--- a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
@@ -9,8 +9,7 @@
 import android.content.Context;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
 
 /**
  * @hide
@@ -43,8 +42,8 @@
      * @hide
      */
     @NonNull
-    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
-        Map<String, FeatureInfo> features = new HashMap<>(0);
+    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
         return features;
     }
 }
diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
index b20b228..7bf8961 100644
--- a/tools/systemfeatures/tests/golden/RwFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
@@ -12,8 +12,7 @@
 import android.content.Context;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
 
 /**
  * @hide
@@ -73,8 +72,8 @@
      * @hide
      */
     @NonNull
-    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
-        Map<String, FeatureInfo> features = new HashMap<>(0);
+    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
         return features;
     }
 }
diff --git a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
index d91f5b6..eb7ec63 100644
--- a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
@@ -7,8 +7,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.FeatureInfo;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
 
 /**
  * @hide
@@ -32,8 +31,8 @@
      * @hide
      */
     @NonNull
-    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
-        Map<String, FeatureInfo> features = new HashMap<>(0);
+    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
         return features;
     }
 }
diff --git a/tools/systemfeatures/tests/src/ArrayMap.java b/tools/systemfeatures/tests/src/ArrayMap.java
new file mode 100644
index 0000000..a5ed9b0
--- /dev/null
+++ b/tools/systemfeatures/tests/src/ArrayMap.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.util;
+
+import java.util.HashMap;
+
+/** Stub for testing. */
+public final class ArrayMap<K, V> extends HashMap<K, V> {
+    public ArrayMap(int capacity) {
+        super(capacity);
+    }
+}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
index 39f8fc4..ed3f5c9 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
@@ -60,7 +60,7 @@
         assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RwNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
-        assertThat(RwNoFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+        assertThat(RwNoFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
     }
 
     @Test
@@ -72,7 +72,7 @@
         assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
-        assertThat(RoNoFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+        assertThat(RoNoFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
 
         // Also ensure we fall back to the PackageManager for feature APIs without an accompanying
         // versioned feature definition.
@@ -106,7 +106,7 @@
         assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RwFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
-        assertThat(RwFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+        assertThat(RwFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
     }
 
     @Test
@@ -163,7 +163,7 @@
         assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 100)).isNull();
         assertThat(RoFeatures.maybeHasFeature("", 0)).isNull();
 
-        Map<String, FeatureInfo> compiledFeatures = RoFeatures.getCompileTimeAvailableFeatures();
+        Map<String, FeatureInfo> compiledFeatures = RoFeatures.getReadOnlySystemEnabledFeatures();
         assertThat(compiledFeatures.keySet())
                 .containsExactly(PackageManager.FEATURE_WATCH, PackageManager.FEATURE_WIFI);
         assertThat(compiledFeatures.get(PackageManager.FEATURE_WATCH).version).isEqualTo(1);