Merge "Explicitly re-show the keyguard if we were hiding." into tm-qpr-dev
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 449729e..48df9e6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3933,6 +3933,9 @@
      * processes to reclaim memory; the system will take care of restarting
      * these processes in the future as needed.
      *
+     * <p class="note">Third party applications can only use this API to kill their own processes.
+     * </p>
+     *
      * @param packageName The name of the package whose processes are to
      * be killed.
      */
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d53ad17..e27af17 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -61,7 +61,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -181,9 +180,6 @@
     private final ArrayList<Engine> mActiveEngines
             = new ArrayList<Engine>();
 
-    private Handler mBackgroundHandler;
-    private HandlerThread mBackgroundThread;
-
     static final class WallpaperCommand {
         String action;
         int x;
@@ -202,6 +198,14 @@
      */
     public class Engine {
         IWallpaperEngineWrapper mIWallpaperEngine;
+        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
+        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
+
+        // 2D matrix [x][y] to represent a page of a portion of a window
+        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
+        Bitmap mLastScreenshot;
+        int mLastWindowPage = -1;
+        private boolean mResetWindowPages;
 
         // Copies from mIWallpaperEngine.
         HandlerCaller mCaller;
@@ -263,27 +267,11 @@
 
         final Object mLock = new Object();
         boolean mOffsetMessageEnqueued;
-
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         float mPendingXOffset;
         float mPendingYOffset;
         float mPendingXOffsetStep;
         float mPendingYOffsetStep;
-
-        /**
-         * local color extraction related fields
-         * to be used by the background thread only (except the atomic boolean)
-         */
-        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
-        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
-        private long mLastProcessLocalColorsTimestamp;
-        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
-        private int mPixelCopyCount = 0;
-        // 2D matrix [x][y] to represent a page of a portion of a window
-        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
-        Bitmap mLastScreenshot;
-        private boolean mResetWindowPages;
-
         boolean mPendingSync;
         MotionEvent mPendingMove;
         boolean mIsInAmbientMode;
@@ -292,8 +280,12 @@
         private long mLastColorInvalidation;
         private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
 
+        // used to throttle processLocalColors
+        private long mLastProcessLocalColorsTimestamp;
+        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
+
         private Display mDisplay;
         private Context mDisplayContext;
         private int mDisplayState;
@@ -833,7 +825,7 @@
                             + "was not established.");
                 }
                 mResetWindowPages = true;
-                processLocalColors();
+                processLocalColors(mPendingXOffset, mPendingXOffsetStep);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
             }
@@ -1372,7 +1364,7 @@
                             resetWindowPages();
                             mSession.finishDrawing(mWindow, null /* postDrawTransaction */,
                                                    Integer.MAX_VALUE);
-                            processLocalColors();
+                            processLocalColors(mPendingXOffset, mPendingXOffsetStep);
                         }
                         reposition();
                         reportEngineShown(shouldWaitForEngineShown());
@@ -1517,7 +1509,7 @@
             if (!mDestroyed) {
                 mVisible = visible;
                 reportVisibility();
-                if (mReportedVisible) processLocalColors();
+                if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
             } else {
                 AnimationHandler.requestAnimatorsEnabled(visible, this);
             }
@@ -1601,41 +1593,31 @@
             }
 
             // setup local color extraction data
-            processLocalColors();
+            processLocalColors(xOffset, xOffsetStep);
         }
 
         /**
          * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of
          * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls.
          */
-        private void processLocalColors() {
+        private void processLocalColors(float xOffset, float xOffsetStep) {
             if (mProcessLocalColorsPending.compareAndSet(false, true)) {
                 final long now = mClockFunction.get();
                 final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp;
                 final long timeToWait = Math.max(0,
                         PROCESS_LOCAL_COLORS_INTERVAL_MS - timeSinceLastColorProcess);
 
-                mBackgroundHandler.postDelayed(() -> {
+                mHandler.postDelayed(() -> {
                     mLastProcessLocalColorsTimestamp = now + timeToWait;
                     mProcessLocalColorsPending.set(false);
-                    processLocalColorsInternal();
+                    processLocalColorsInternal(xOffset, xOffsetStep);
                 }, timeToWait);
             }
         }
 
-        private void processLocalColorsInternal() {
+        private void processLocalColorsInternal(float xOffset, float xOffsetStep) {
             // implemented by the wallpaper
             if (supportsLocalColorExtraction()) return;
-            assertBackgroundThread();
-            float xOffset;
-            float xOffsetStep;
-            float wallpaperDimAmount;
-            synchronized (mLock) {
-                xOffset = mPendingXOffset;
-                xOffsetStep = mPendingXOffsetStep;
-                wallpaperDimAmount = mWallpaperDimAmount;
-            }
-
             if (DEBUG) {
                 Log.d(TAG, "processLocalColors " + xOffset + " of step "
                         + xOffsetStep);
@@ -1698,7 +1680,7 @@
                 xPage = mWindowPages.length - 1;
             }
             current = mWindowPages[xPage];
-            updatePage(current, xPage, xPages, wallpaperDimAmount);
+            updatePage(current, xPage, xPages, finalXOffsetStep);
             Trace.endSection();
         }
 
@@ -1718,23 +1700,16 @@
             }
         }
 
-        /**
-         * Must be called with the surface lock held.
-         * Must not be called if the surface is not valid.
-         * Will unlock the surface when done using it.
-         */
         void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
-                float wallpaperDimAmount) {
-
-            assertBackgroundThread();
-
+                float xOffsetStep) {
             // in case the clock is zero, we start with negative time
             long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION;
             long lapsed = current - currentPage.getLastUpdateTime();
             // Always update the page when the last update time is <= 0
             // This is important especially when the device first boots
-            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return;
-
+            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) {
+                return;
+            }
             Surface surface = mSurfaceHolder.getSurface();
             if (!surface.isValid()) return;
             boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
@@ -1750,42 +1725,33 @@
             Bitmap screenShot = Bitmap.createBitmap(width, height,
                     Bitmap.Config.ARGB_8888);
             final Bitmap finalScreenShot = screenShot;
-            final String pixelCopySectionName = "WallpaperService#pixelCopy";
-            final int pixelCopyCount = mPixelCopyCount++;
-            Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount);
-            try {
-                PixelCopy.request(surface, screenShot, (res) -> {
-                    Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount);
-                    if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
-                    if (res != PixelCopy.SUCCESS) {
-                        Bitmap lastBitmap = currentPage.getBitmap();
-                        // assign the last bitmap taken for now
-                        currentPage.setBitmap(mLastScreenshot);
-                        Bitmap lastScreenshot = mLastScreenshot;
-                        if (lastScreenshot != null && !lastScreenshot.isRecycled()
-                                && !Objects.equals(lastBitmap, lastScreenshot)) {
-                            updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
-                        }
-                    } else {
-                        mLastScreenshot = finalScreenShot;
-                        // going to hold this lock for a while
-                        currentPage.setBitmap(finalScreenShot);
-                        currentPage.setLastUpdateTime(current);
-                        updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
+            Trace.beginSection("WallpaperService#pixelCopy");
+            PixelCopy.request(surface, screenShot, (res) -> {
+                Trace.endSection();
+                if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
+                if (res != PixelCopy.SUCCESS) {
+                    Bitmap lastBitmap = currentPage.getBitmap();
+                    // assign the last bitmap taken for now
+                    currentPage.setBitmap(mLastScreenshot);
+                    Bitmap lastScreenshot = mLastScreenshot;
+                    if (lastScreenshot != null && !lastScreenshot.isRecycled()
+                            && !Objects.equals(lastBitmap, lastScreenshot)) {
+                        updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
                     }
-                }, mBackgroundHandler);
-            } catch (IllegalArgumentException e) {
-                // this can potentially happen if the surface is invalidated right between the
-                // surface.isValid() check and the PixelCopy operation.
-                // in this case, stop: we'll compute colors on the next processLocalColors call.
-                Log.i(TAG, "Cancelling processLocalColors: exception caught during PixelCopy");
-            }
+                } else {
+                    mLastScreenshot = finalScreenShot;
+                    // going to hold this lock for a while
+                    currentPage.setBitmap(finalScreenShot);
+                    currentPage.setLastUpdateTime(current);
+                    updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+                }
+            }, mHandler);
+
         }
         // locked by the passed page
-        private void updatePageColors(
-                EngineWindowPage page, int pageIndx, int numPages, float wallpaperDimAmount) {
+        private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages,
+                float xOffsetStep) {
             if (page.getBitmap() == null) return;
-            assertBackgroundThread();
             Trace.beginSection("WallpaperService#updatePageColors");
             if (DEBUG) {
                 Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas "
@@ -1807,7 +1773,7 @@
                     Log.e(TAG, "Error creating page local color bitmap", e);
                     continue;
                 }
-                WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount);
+                WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
                 target.recycle();
                 WallpaperColors currentColor = page.getColors(area);
 
@@ -1824,26 +1790,17 @@
                                 + " local color callback for area" + area + " for page " + pageIndx
                                 + " of " + numPages);
                     }
-                    mHandler.post(() -> {
-                        try {
-                            mConnection.onLocalWallpaperColorsChanged(area, color,
-                                    mDisplayContext.getDisplayId());
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
-                        }
-                    });
+                    try {
+                        mConnection.onLocalWallpaperColorsChanged(area, color,
+                                mDisplayContext.getDisplayId());
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+                    }
                 }
             }
             Trace.endSection();
         }
 
-        private void assertBackgroundThread() {
-            if (!mBackgroundHandler.getLooper().isCurrentThread()) {
-                throw new IllegalStateException(
-                        "ProcessLocalColors should be called from the background thread");
-            }
-        }
-
         private RectF generateSubRect(RectF in, int pageInx, int numPages) {
             float minLeft = (float) (pageInx) / (float) (numPages);
             float maxRight = (float) (pageInx + 1) / (float) (numPages);
@@ -1868,6 +1825,7 @@
             if (supportsLocalColorExtraction()) return;
             if (!mResetWindowPages) return;
             mResetWindowPages = false;
+            mLastWindowPage = -1;
             for (int i = 0; i < mWindowPages.length; i++) {
                 mWindowPages[i].setLastUpdateTime(0L);
             }
@@ -1893,10 +1851,12 @@
             if (DEBUG) {
                 Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
             }
-            mBackgroundHandler.post(() -> {
+            mHandler.post(() -> {
                 mLocalColorsToAdd.addAll(regions);
-                processLocalColors();
+                processLocalColors(mPendingXOffset, mPendingYOffset);
             });
+
+
         }
 
         /**
@@ -1906,7 +1866,7 @@
          */
         public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
             if (supportsLocalColorExtraction()) return;
-            mBackgroundHandler.post(() -> {
+            mHandler.post(() -> {
                 float step = mPendingXOffsetStep;
                 mLocalColorsToAdd.removeAll(regions);
                 mLocalColorAreas.removeAll(regions);
@@ -2537,9 +2497,6 @@
     @Override
     public void onCreate() {
         Trace.beginSection("WPMS.onCreate");
-        mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor");
-        mBackgroundThread.start();
-        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
         super.onCreate();
         Trace.endSection();
     }
@@ -2552,7 +2509,6 @@
             mActiveEngines.get(i).detach();
         }
         mActiveEngines.clear();
-        mBackgroundThread.quitSafely();
         Trace.endSection();
     }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 02027e4..293f9082 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -823,6 +823,11 @@
     /** @hide */
     public final void destroy() {
         mDestroyed = true;
+        onDestroy();
+    }
+
+    /** @hide */
+    protected void onDestroy() {
     }
 
     /** @hide */
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index d8afe50..e7217de 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -33,6 +33,7 @@
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
@@ -231,6 +232,7 @@
     public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66;
     public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67;
     public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68;
+    public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70;
 
     private static final int NO_STATSD_LOGGING = -1;
 
@@ -308,6 +310,8 @@
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME,
+            NO_STATSD_LOGGING,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION,
     };
 
     private static volatile InteractionJankMonitor sInstance;
@@ -396,7 +400,8 @@
             CUJ_RECENTS_SCROLLING,
             CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS,
             CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE,
-            CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME
+            CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME,
+            CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
@@ -917,6 +922,8 @@
                 return "LAUNCHER_CLOSE_ALL_APPS_SWIPE";
             case CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME:
                 return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME";
+            case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION:
+                return "LOCKSCREEN_CLOCK_MOVE_ANIMATION";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3a0e09d..6fed26c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -81,7 +81,6 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -202,7 +201,6 @@
     public static final int RESET_REASON_ADB_COMMAND = 2;
     public static final int RESET_REASON_FULL_CHARGE = 3;
     public static final int RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE = 4;
-    public static final int RESET_REASON_PLUGGED_IN_FOR_LONG_DURATION = 5;
 
     protected Clock mClock;
 
@@ -389,89 +387,6 @@
         }
     }
 
-    /** Provide BatteryStatsImpl configuration choices */
-    public static class BatteryStatsConfig {
-        static final int RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG = 1 << 0;
-        static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1;
-
-        private final int mFlags;
-
-        private BatteryStatsConfig(Builder builder) {
-            int flags = 0;
-            if (builder.mResetOnUnplugHighBatteryLevel) {
-                flags |= RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG;
-            }
-            if (builder.mResetOnUnplugAfterSignificantCharge) {
-                flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
-            }
-            mFlags = flags;
-        }
-
-        /**
-         * Returns whether a BatteryStats reset should occur on unplug when the battery level is
-         * high.
-         */
-        boolean shouldResetOnUnplugHighBatteryLevel() {
-            return (mFlags & RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG)
-                    == RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG;
-        }
-
-        /**
-         * Returns whether a BatteryStats reset should occur on unplug if the battery charge a
-         * significant amount since it has been plugged in.
-         */
-        boolean shouldResetOnUnplugAfterSignificantCharge() {
-            return (mFlags & RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG)
-                    == RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
-        }
-
-        /**
-         * Builder for BatteryStatsConfig
-         */
-        public static class Builder {
-            private boolean mResetOnUnplugHighBatteryLevel;
-            private boolean mResetOnUnplugAfterSignificantCharge;
-            public Builder() {
-                mResetOnUnplugHighBatteryLevel = true;
-                mResetOnUnplugAfterSignificantCharge = true;
-            }
-
-            /**
-             * Build the BatteryStatsConfig.
-             */
-            public BatteryStatsConfig build() {
-                return new BatteryStatsConfig(this);
-            }
-
-            /**
-             * Set whether a BatteryStats reset should occur on unplug when the battery level is
-             * high.
-             */
-            public Builder setResetOnUnplugHighBatteryLevel(boolean reset) {
-                mResetOnUnplugHighBatteryLevel = reset;
-                return this;
-            }
-
-            /**
-             * Set whether a BatteryStats reset should occur on unplug if the battery charge a
-             * significant amount since it has been plugged in.
-             */
-            public Builder setResetOnUnplugAfterSignificantCharge(boolean reset) {
-                mResetOnUnplugAfterSignificantCharge = reset;
-                return this;
-            }
-        }
-
-    }
-
-    /** Handles calls to AlarmManager */
-    public interface AlarmInterface {
-        /** Schedule an RTC alarm */
-        void schedule(long rtcTimeMs, long windowLengthMs);
-        /** Cancel the previously scheduled alarm */
-        void cancel();
-    }
-
     private final PlatformIdleStateCallback mPlatformIdleStateCallback;
 
     private final Runnable mDeferSetCharging = new Runnable() {
@@ -802,7 +717,6 @@
     protected boolean mHaveBatteryLevel = false;
     protected boolean mRecordingHistory = false;
     int mNumHistoryItems;
-    private long mBatteryPluggedInRealTimeMs = 0;
 
     private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
     private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024;
@@ -1545,13 +1459,6 @@
     @GuardedBy("this")
     protected final Constants mConstants;
 
-    @VisibleForTesting
-    @GuardedBy("this")
-    protected BatteryStatsConfig mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
-
-    @VisibleForTesting
-    protected AlarmInterface mLongPlugInAlarmInterface = null;
-
     /*
      * Holds a SamplingTimer associated with each Resource Power Manager state and voter,
      * recording their times when on-battery (regardless of screen state).
@@ -1718,13 +1625,12 @@
     public BatteryStatsImpl(Clock clock, File historyDirectory) {
         init(clock);
         mStartClockTimeMs = clock.currentTimeMillis();
+        mCheckinFile = null;
         mDailyFile = null;
         if (historyDirectory == null) {
-            mCheckinFile = null;
             mStatsFile = null;
             mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
         } else {
-            mCheckinFile = new AtomicFile(new File(historyDirectory, "batterystats-checkin.bin"));
             mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
             mBatteryStatsHistory = new BatteryStatsHistory(this, historyDirectory, mHistoryBuffer);
         }
@@ -12642,27 +12548,6 @@
     }
 
     /**
-     * Injects BatteryStatsConfig
-     */
-    public void setBatteryStatsConfig(BatteryStatsConfig config) {
-        synchronized (this) {
-            mBatteryStatsConfig = config;
-        }
-    }
-
-    /**
-     * Injects a LongPlugInAlarmHandler
-     */
-    public void setLongPlugInAlarmInterface(AlarmInterface longPlugInAlarmInterface) {
-        synchronized (this) {
-            mLongPlugInAlarmInterface = longPlugInAlarmInterface;
-            if (!mOnBattery) {
-                scheduleNextResetWhilePluggedInCheck();
-            }
-        }
-    }
-
-    /**
      * Starts tracking CPU time-in-state for threads of the system server process,
      * keeping a separate account of threads receiving incoming binder calls.
      */
@@ -13134,12 +13019,12 @@
     }
 
     @GuardedBy("this")
-    public void resetAllStatsAndHistoryLocked(int reason) {
+    public void resetAllStatsCmdLocked() {
         final long mSecUptime = mClock.uptimeMillis();
         long uptimeUs = mSecUptime * 1000;
         long mSecRealtime = mClock.elapsedRealtime();
         long realtimeUs = mSecRealtime * 1000;
-        resetAllStatsLocked(mSecUptime, mSecRealtime, reason);
+        resetAllStatsLocked(mSecUptime, mSecRealtime, RESET_REASON_ADB_COMMAND);
         mDischargeStartLevel = mHistoryCur.batteryLevel;
         pullPendingStateUpdatesLocked();
         addHistoryRecordLocked(mSecRealtime, mSecUptime);
@@ -15613,73 +15498,6 @@
     }
 
     /**
-     * Might reset battery stats if conditions are met. Assumed the device is currently plugged in.
-     */
-    @GuardedBy("this")
-    public void maybeResetWhilePluggedInLocked() {
-        final long elapsedRealtimeMs = mClock.elapsedRealtime();
-        if (shouldResetWhilePluggedInLocked(elapsedRealtimeMs)) {
-            Slog.i(TAG,
-                    "Resetting due to long plug in duration. elapsed time = " + elapsedRealtimeMs
-                            + " ms, last plug in time = " + mBatteryPluggedInRealTimeMs
-                            + " ms, last reset time = " + mRealtimeStartUs / 1000);
-            resetAllStatsAndHistoryLocked(RESET_REASON_PLUGGED_IN_FOR_LONG_DURATION);
-        }
-
-        scheduleNextResetWhilePluggedInCheck();
-    }
-
-    @GuardedBy("this")
-    private void scheduleNextResetWhilePluggedInCheck() {
-        if (mLongPlugInAlarmInterface != null) {
-            final long timeoutMs = mClock.currentTimeMillis()
-                    + mConstants.RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS
-                    * DateUtils.HOUR_IN_MILLIS;
-            Calendar nextAlarm = Calendar.getInstance();
-            nextAlarm.setTimeInMillis(timeoutMs);
-
-            // Find the 2 AM the same day as the end of the minimum duration.
-            // This logic does not handle a Daylight Savings transition, or a timezone change
-            // while the alarm has been set. The need to reset after a long period while plugged
-            // in is not strict enough to warrant a well architected out solution.
-            nextAlarm.set(Calendar.MILLISECOND, 0);
-            nextAlarm.set(Calendar.SECOND, 0);
-            nextAlarm.set(Calendar.MINUTE, 0);
-            nextAlarm.set(Calendar.HOUR_OF_DAY, 2);
-            long nextTimeMs = nextAlarm.getTimeInMillis();
-            if (nextTimeMs < timeoutMs) {
-                // The 2AM on the day of the timeout, move on the next day.
-                nextTimeMs += DateUtils.DAY_IN_MILLIS;
-            }
-            mLongPlugInAlarmInterface.schedule(nextTimeMs, DateUtils.HOUR_IN_MILLIS);
-        }
-    }
-
-
-    @GuardedBy("this")
-    private boolean shouldResetWhilePluggedInLocked(long elapsedRealtimeMs) {
-        if (mNoAutoReset) return false;
-        if (!mSystemReady) return false;
-
-        final long pluggedInThresholdMs = mBatteryPluggedInRealTimeMs
-                + mConstants.RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS
-                * DateUtils.HOUR_IN_MILLIS;
-        if (elapsedRealtimeMs >= pluggedInThresholdMs) {
-            // The device has been plugged in for a long time.
-            final long resetThresholdMs = mRealtimeStartUs / 1000
-                    + mConstants.RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS
-                    * DateUtils.HOUR_IN_MILLIS;
-            if (elapsedRealtimeMs >= resetThresholdMs) {
-                // And it has been a long time since the last reset.
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
-    /**
      * Notifies BatteryStatsImpl that the system server is ready.
      */
     public void onSystemReady() {
@@ -15687,32 +15505,6 @@
     }
 
     @GuardedBy("this")
-    private boolean shouldResetOnUnplugLocked(int batteryStatus, int batteryLevel) {
-        if (mNoAutoReset) return false;
-        if (!mSystemReady) return false;
-        if (mBatteryStatsConfig.shouldResetOnUnplugHighBatteryLevel()) {
-            // Allow resetting due to currently being at high battery level
-            if (batteryStatus == BatteryManager.BATTERY_STATUS_FULL) return true;
-            if (batteryLevel >= 90) return true;
-        }
-        if (mBatteryStatsConfig.shouldResetOnUnplugAfterSignificantCharge()) {
-            // Allow resetting after a significant charge (from a very low level to a now very
-            // high level).
-            if (mDischargePlugLevel < 20 && batteryLevel >= 80) return true;
-        }
-        if (getHighDischargeAmountSinceCharge() >= 200) {
-            // Reset the stats if battery got partially charged and discharged repeatedly without
-            // ever reaching the full charge.
-            // This reset is done in order to prevent stats sessions from going on forever.
-            // Exceedingly long battery sessions would lead to an overflow of
-            // data structures such as mWakeupReasonStats.
-            return true;
-        }
-
-        return false;
-    }
-
-    @GuardedBy("this")
     protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
             final boolean onBattery, final int oldStatus, final int level, final int chargeUah) {
         boolean doWrite = false;
@@ -15724,10 +15516,23 @@
         final long realtimeUs = mSecRealtime * 1000;
         final int screenState = mScreenState;
         if (onBattery) {
+            // We will reset our status if we are unplugging after the
+            // battery was last full, or the level is at 100, or
+            // we have gone through a significant charge (from a very low
+            // level to a now very high level).
+            // Also, we will reset the stats if battery got partially charged
+            // and discharged repeatedly without ever reaching the full charge.
+            // This reset is done in order to prevent stats sessions from going on forever.
+            // Exceedingly long battery sessions would lead to an overflow of
+            // data structures such as mWakeupReasonStats.
             boolean reset = false;
-            if (shouldResetOnUnplugLocked(oldStatus, level)) {
+            if (!mNoAutoReset && mSystemReady
+                    && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
+                    || level >= 90
+                    || (mDischargeCurrentLevel < 20 && level >= 80)
+                    || getHighDischargeAmountSinceCharge() >= 200)) {
                 Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
-                        + " dischargeLevel=" + mDischargePlugLevel
+                        + " dischargeLevel=" + mDischargeCurrentLevel
                         + " lowAmount=" + getLowDischargeAmountSinceCharge()
                         + " highAmount=" + getHighDischargeAmountSinceCharge());
                 // Before we write, collect a snapshot of the final aggregated
@@ -15784,9 +15589,6 @@
             mInitStepMode = mCurStepMode;
             mModStepMode = 0;
             pullPendingStateUpdatesLocked();
-            if (mLongPlugInAlarmInterface != null) {
-                mLongPlugInAlarmInterface.cancel();
-            }
             mHistoryCur.batteryLevel = (byte)level;
             mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
@@ -15818,7 +15620,6 @@
             mLastChargingStateLevel = level;
             mOnBattery = mOnBatteryInternal = false;
             pullPendingStateUpdatesLocked();
-            mBatteryPluggedInRealTimeMs = mSecRealtime;
             mHistoryCur.batteryLevel = (byte)level;
             mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
@@ -15836,7 +15637,6 @@
             mMaxChargeStepLevel = level;
             mInitStepMode = mCurStepMode;
             mModStepMode = 0;
-            scheduleNextResetWhilePluggedInCheck();
         }
         if (doWrite || (mLastWriteTimeMs + (60 * 1000)) < mSecRealtime) {
             if (mStatsFile != null && mBatteryStatsHistory.getActiveFile() != null) {
@@ -16851,8 +16651,6 @@
         public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
         public static final String KEY_BATTERY_CHARGED_DELAY_MS =
                 "battery_charged_delay_ms";
-        public static final String KEY_RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS =
-                "reset_while_plugged_in_minimum_duration_hours";
 
         private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
         private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 1_000;
@@ -16865,8 +16663,6 @@
         private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
         private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
         private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
-        // Little less than 2 days
-        private static final int DEFAULT_RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS = 47;
 
         public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
         /* Do not set default value for KERNEL_UID_READERS_THROTTLE_TIME. Need to trigger an
@@ -16882,8 +16678,6 @@
         public int MAX_HISTORY_FILES;
         public int MAX_HISTORY_BUFFER; /*Bytes*/
         public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
-        public int RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS =
-                DEFAULT_RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -16960,11 +16754,6 @@
                                 DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
                                 : DEFAULT_MAX_HISTORY_BUFFER_KB)
                         * 1024;
-
-                RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS = mParser.getInt(
-                        KEY_RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS,
-                        DEFAULT_RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS);
-
                 updateBatteryChargedDelayMsLocked();
             }
         }
@@ -17019,8 +16808,6 @@
             pw.println(MAX_HISTORY_BUFFER/1024);
             pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("=");
             pw.println(BATTERY_CHARGED_DELAY_MS);
-            pw.print(KEY_RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS); pw.print("=");
-            pw.println(RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS);
         }
     }
 
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index bb69192f..e603e2ed 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -295,6 +295,7 @@
     private boolean mClosingActionMenu;
 
     private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
+    private int mAudioMode = AudioManager.MODE_NORMAL;
     private MediaController mMediaController;
 
     private AudioManager mAudioManager;
@@ -317,6 +318,8 @@
         }
     };
 
+    private AudioManager.OnModeChangedListener mOnModeChangedListener;
+
     private Transition mEnterTransition = null;
     private Transition mReturnTransition = USE_DEFAULT_TRANSITION;
     private Transition mExitTransition = null;
@@ -1950,9 +1953,9 @@
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                // If we have a session send it the volume command, otherwise
-                // use the suggested stream.
-                if (mMediaController != null) {
+                // If we have a session and no active phone call send it the volume command,
+                // otherwise use the suggested stream.
+                if (mMediaController != null && !isActivePhoneCallOngoing()) {
                     getMediaSessionManager().dispatchVolumeKeyEventToSessionAsSystemService(event,
                             mMediaController.getSessionToken());
                 } else {
@@ -2003,6 +2006,11 @@
         return false;
     }
 
+    private boolean isActivePhoneCallOngoing() {
+        return mAudioMode == AudioManager.MODE_IN_CALL
+                || mAudioMode == AudioManager.MODE_IN_COMMUNICATION;
+    }
+
     private KeyguardManager getKeyguardManager() {
         if (mKeyguardManager == null) {
             mKeyguardManager = (KeyguardManager) getContext().getSystemService(
@@ -2326,6 +2334,14 @@
         }
     }
 
+    @Override
+    protected void onDestroy() {
+        if (mOnModeChangedListener != null) {
+            getAudioManager().removeOnModeChangedListener(mOnModeChangedListener);
+            mOnModeChangedListener = null;
+        }
+    }
+
     private class PanelMenuPresenterCallback implements MenuPresenter.Callback {
         @Override
         public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
@@ -3208,6 +3224,15 @@
     @Override
     public void setMediaController(MediaController controller) {
         mMediaController = controller;
+        if (controller != null && mOnModeChangedListener == null) {
+            mAudioMode = getAudioManager().getMode();
+            mOnModeChangedListener = mode -> mAudioMode = mode;
+            getAudioManager().addOnModeChangedListener(getContext().getMainExecutor(),
+                    mOnModeChangedListener);
+        } else if (mOnModeChangedListener != null) {
+            getAudioManager().removeOnModeChangedListener(mOnModeChangedListener);
+            mOnModeChangedListener = null;
+        }
     }
 
     @Override
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 31903e2..0e95e30 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3154,7 +3154,11 @@
         android:protectionLevel="normal" />
 
     <!-- Allows an application to call
-        {@link android.app.ActivityManager#killBackgroundProcesses}.
+         {@link android.app.ActivityManager#killBackgroundProcesses}.
+
+         <p class="note">Third party applications can only use this API to kill their own
+         processes.</p>
+
          <p>Protection level: normal
     -->
     <permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 58c8b29..dafa0ad 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6092,9 +6092,4 @@
 
     <!-- Whether to show weather on the lock screen by default. -->
     <bool name="config_lockscreenWeatherEnabledByDefault">false</bool>
-
-    <!-- Whether to reset Battery Stats on unplug when the battery level is high. -->
-    <bool name="config_batteryStatsResetOnUnplugHighBatteryLevel">true</bool>
-    <!-- Whether to reset Battery Stats on unplug if the battery was significantly charged -->
-    <bool name="config_batteryStatsResetOnUnplugAfterSignificantCharge">true</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8addca2..591ba5f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4903,7 +4903,4 @@
 
   <!-- Whether to show weather on the lockscreen by default. -->
   <java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
-
-  <java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
-  <java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
 </resources>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsResetTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsResetTest.java
deleted file mode 100644
index 9c2d332..0000000
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsResetTest.java
+++ /dev/null
@@ -1,362 +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.internal.os;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.os.BatteryManager;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BatteryStatsResetTest {
-
-    private static final int BATTERY_NOMINAL_VOLTAGE_MV = 3700;
-    private static final int BATTERY_CAPACITY_UAH = 4_000_000;
-    private static final int BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL = 100;
-
-    private MockClock mMockClock;
-    private MockBatteryStatsImpl mBatteryStatsImpl;
-
-
-    /**
-     * Battery status. Must be one of the following:
-     * {@link BatteryManager#BATTERY_STATUS_UNKNOWN}
-     * {@link BatteryManager#BATTERY_STATUS_CHARGING}
-     * {@link BatteryManager#BATTERY_STATUS_DISCHARGING}
-     * {@link BatteryManager#BATTERY_STATUS_NOT_CHARGING}
-     * {@link BatteryManager#BATTERY_STATUS_FULL}
-     */
-    private int mBatteryStatus;
-    /**
-     * Battery health. Must be one of the following:
-     * {@link BatteryManager#BATTERY_HEALTH_UNKNOWN}
-     * {@link BatteryManager#BATTERY_HEALTH_GOOD}
-     * {@link BatteryManager#BATTERY_HEALTH_OVERHEAT}
-     * {@link BatteryManager#BATTERY_HEALTH_DEAD}
-     * {@link BatteryManager#BATTERY_HEALTH_OVER_VOLTAGE}
-     * {@link BatteryManager#BATTERY_HEALTH_UNSPECIFIED_FAILURE}
-     * {@link BatteryManager#BATTERY_HEALTH_COLD}
-     */
-    private int mBatteryHealth;
-    /**
-     * Battery plug type. Can be the union of any number of the following flags:
-     * {@link BatteryManager#BATTERY_PLUGGED_AC}
-     * {@link BatteryManager#BATTERY_PLUGGED_USB}
-     * {@link BatteryManager#BATTERY_PLUGGED_WIRELESS}
-     * {@link BatteryManager#BATTERY_PLUGGED_DOCK}
-     *
-     * Zero means the device is unplugged.
-     */
-    private int mBatteryPlugType;
-    private int mBatteryLevel;
-    private int mBatteryTemp;
-    private int mBatteryVoltageMv;
-    private int mBatteryChargeUah;
-    private int mBatteryChargeFullUah;
-    private long mBatteryChargeTimeToFullSeconds;
-
-    @Before
-    public void setUp() {
-        final Context context = InstrumentationRegistry.getContext();
-
-        mMockClock = new MockClock();
-        mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, context.getFilesDir());
-        mBatteryStatsImpl.onSystemReady();
-
-
-        // Set up the battery state. Start off with a fully charged plugged in battery.
-        mBatteryStatus = BatteryManager.BATTERY_STATUS_FULL;
-        mBatteryHealth = BatteryManager.BATTERY_HEALTH_GOOD;
-        mBatteryPlugType = BatteryManager.BATTERY_PLUGGED_USB;
-        mBatteryLevel = 100;
-        mBatteryTemp = 70; // Arbitrary reasonable temperature.
-        mBatteryVoltageMv = BATTERY_NOMINAL_VOLTAGE_MV;
-        mBatteryChargeUah = BATTERY_CAPACITY_UAH;
-        mBatteryChargeFullUah = BATTERY_CAPACITY_UAH;
-        mBatteryChargeTimeToFullSeconds = 0;
-    }
-
-    @Test
-    public void testResetOnUnplug_highBatteryLevel() {
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugHighBatteryLevel(true)
-                        .build());
-
-        long expectedResetTimeUs = 0;
-
-        unplugBattery();
-        dischargeToLevel(60);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(80);
-        unplugBattery();
-        // Reset should not occur until battery level above 90.
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(95);
-        // Reset should not occur until unplug.
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        unplugBattery();
-        // Reset should occur on unplug now that battery level is high (>=90)
-        expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // disable high battery level reset on unplug.
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugHighBatteryLevel(false)
-                        .build());
-
-        dischargeToLevel(60);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(95);
-        unplugBattery();
-        // Reset should not occur since the high battery level logic has been disabled.
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-    }
-
-    @Test
-    public void testResetOnUnplug_significantCharge() {
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugAfterSignificantCharge(true)
-                        .build());
-        long expectedResetTimeUs = 0;
-
-        unplugBattery();
-        // Battery level dropped below 20%.
-        dischargeToLevel(15);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(50);
-        unplugBattery();
-        // Reset should not occur until battery level above 80
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(85);
-        unplugBattery();
-        // Reset should not occur because the charge session did not go from 20% to 80%
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Battery level dropped below 20%.
-        dischargeToLevel(15);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(85);
-        unplugBattery();
-        // Reset should occur after significant charge amount.
-        expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // disable reset on unplug after significant charge.
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugAfterSignificantCharge(false)
-                        .build());
-
-        // Battery level dropped below 20%.
-        dischargeToLevel(15);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(85);
-        unplugBattery();
-        // Reset should not occur after significant charge amount.
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-    }
-
-    @Test
-    public void testResetOnUnplug_manyPartialCharges() {
-        long expectedResetTimeUs = 0;
-
-        unplugBattery();
-        // Cumulative battery discharged: 60%.
-        dischargeToLevel(40);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(80);
-        unplugBattery();
-        // Reset should not occur
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Cumulative battery discharged: 100%.
-        dischargeToLevel(40);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(80);
-        unplugBattery();
-        // Reset should not occur
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Cumulative battery discharged: 140%.
-        dischargeToLevel(40);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(80);
-        unplugBattery();
-        // Reset should not occur
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Cumulative battery discharged: 180%.
-        dischargeToLevel(40);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(80);
-        unplugBattery();
-        // Reset should not occur
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Cumulative battery discharged: 220%.
-        dischargeToLevel(40);
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        chargeToLevel(80);
-        unplugBattery();
-        // Should reset after >200% of cumulative battery discharge
-        expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-    }
-
-    @Test
-    public void testResetWhilePluggedIn_longPlugIn() {
-        // disable high battery level reset on unplug.
-        mBatteryStatsImpl.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugHighBatteryLevel(false)
-                        .setResetOnUnplugAfterSignificantCharge(false)
-                        .build());
-        long expectedResetTimeUs = 0;
-
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-        mBatteryStatsImpl.maybeResetWhilePluggedInLocked();
-        // Reset should not occur
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Increment time a day
-        incTimeMs(24L * 60L * 60L * 1000L);
-        mBatteryStatsImpl.maybeResetWhilePluggedInLocked();
-        // Reset should still not occur
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Increment time a day
-        incTimeMs(24L * 60L * 60L * 1000L);
-        mBatteryStatsImpl.maybeResetWhilePluggedInLocked();
-        // Reset 47 hour threshold crossed, reset should occur.
-        expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Increment time a day
-        incTimeMs(24L * 60L * 60L * 1000L);
-        mBatteryStatsImpl.maybeResetWhilePluggedInLocked();
-        // Reset should not occur
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Increment time a day
-        incTimeMs(24L * 60L * 60L * 1000L);
-        mBatteryStatsImpl.maybeResetWhilePluggedInLocked();
-        // Reset another 47 hour threshold crossed, reset should occur.
-        expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Increment time a day
-        incTimeMs(24L * 60L * 60L * 1000L);
-        mBatteryStatsImpl.maybeResetWhilePluggedInLocked();
-        // Reset should not occur
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        unplugBattery();
-        plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
-
-        // Increment time a day
-        incTimeMs(24L * 60L * 60L * 1000L);
-        mBatteryStatsImpl.maybeResetWhilePluggedInLocked();
-        // Reset should not occur, since unplug occurred recently.
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-
-        // Increment time a day
-        incTimeMs(24L * 60L * 60L * 1000L);
-        mBatteryStatsImpl.maybeResetWhilePluggedInLocked();
-        // Reset another 47 hour threshold crossed, reset should occur.
-        expectedResetTimeUs = mMockClock.elapsedRealtime() * 1000;
-        assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
-    }
-
-    private void dischargeToLevel(int targetLevel) {
-        mBatteryStatus = BatteryManager.BATTERY_STATUS_DISCHARGING;
-        for (int level = mBatteryLevel - 1; level >= targetLevel; level--) {
-            prepareBatteryLevel(level);
-            incTimeMs(5000); // Arbitrary discharge rate.
-            updateBatteryState();
-        }
-    }
-
-    private void chargeToLevel(int targetLevel) {
-        mBatteryStatus = BatteryManager.BATTERY_STATUS_CHARGING;
-        for (int level = mBatteryLevel + 1; level <= targetLevel; level++) {
-            if (level >= 100) mBatteryStatus = BatteryManager.BATTERY_STATUS_FULL;
-            prepareBatteryLevel(level);
-            incTimeMs(BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL * 1000);
-            updateBatteryState();
-        }
-    }
-
-    private void unplugBattery() {
-        mBatteryPlugType = 0;
-        updateBatteryState();
-    }
-
-    private void plugBattery(int type) {
-        mBatteryPlugType |= type;
-        updateBatteryState();
-    }
-
-    private void prepareBatteryLevel(int level) {
-        mBatteryLevel = level;
-        mBatteryChargeUah = mBatteryLevel * mBatteryChargeFullUah / 100;
-        mBatteryChargeTimeToFullSeconds =
-                (100 - mBatteryLevel) * BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL;
-    }
-
-    private void incTimeMs(long milliseconds) {
-        mMockClock.realtime += milliseconds;
-        mMockClock.uptime += milliseconds / 2; // Arbitrary slower uptime accumulation
-        mMockClock.currentTime += milliseconds;
-    }
-
-    private void updateBatteryState() {
-        mBatteryStatsImpl.setBatteryStateLocked(mBatteryStatus, mBatteryHealth, mBatteryPlugType,
-                mBatteryLevel, mBatteryTemp, mBatteryVoltageMv, mBatteryChargeUah,
-                mBatteryChargeFullUah, mBatteryChargeTimeToFullSeconds,
-                mMockClock.elapsedRealtime(), mMockClock.uptimeMillis(),
-                mMockClock.currentTimeMillis());
-    }
-}
-
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index ae2d1af..2742861 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -342,7 +342,7 @@
         Context context = InstrumentationRegistry.getContext();
         BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
         mStatsRule.setCurrentTime(5 * MINUTE_IN_MS);
-        batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+        batteryStats.resetAllStatsCmdLocked();
 
         BatteryUsageStatsStore batteryUsageStatsStore = new BatteryUsageStatsStore(context,
                 batteryStats, new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"),
@@ -357,14 +357,14 @@
         batteryStats.noteFlashlightOffLocked(APP_UID,
                 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
         mStatsRule.setCurrentTime(25 * MINUTE_IN_MS);
-        batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+        batteryStats.resetAllStatsCmdLocked();
 
         batteryStats.noteFlashlightOnLocked(APP_UID,
                 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
         batteryStats.noteFlashlightOffLocked(APP_UID,
                 50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
         mStatsRule.setCurrentTime(55 * MINUTE_IN_MS);
-        batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+        batteryStats.resetAllStatsCmdLocked();
 
         // This section should be ignored because the timestamp is out or range
         batteryStats.noteFlashlightOnLocked(APP_UID,
@@ -372,7 +372,7 @@
         batteryStats.noteFlashlightOffLocked(APP_UID,
                 70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS);
         mStatsRule.setCurrentTime(75 * MINUTE_IN_MS);
-        batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+        batteryStats.resetAllStatsCmdLocked();
 
         // This section should be ignored because it represents the current stats session
         batteryStats.noteFlashlightOnLocked(APP_UID,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
index 11b9047..c9729fa 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
@@ -84,7 +84,7 @@
 
         mMockClock.realtime = 1_000_000;
         mMockClock.uptime = 1_000_000;
-        mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+        mBatteryStats.resetAllStatsCmdLocked();
 
         final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
         assertThat(timestamps).hasLength(1);
@@ -114,7 +114,7 @@
         final int numberOfSnapshots =
                 (int) (MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES / snapshotFileSize);
         for (int i = 0; i < numberOfSnapshots + 2; i++) {
-            mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+            mBatteryStats.resetAllStatsCmdLocked();
 
             mMockClock.realtime += 10_000_000;
             mMockClock.uptime += 10_000_000;
@@ -141,7 +141,7 @@
             mMockClock.currentTime += 10_000_000;
             prepareBatteryStats();
 
-            mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+            mBatteryStats.resetAllStatsCmdLocked();
         }
 
         assertThat(getDirectorySize(mStoreDirectory)).isNotEqualTo(0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a673384..7d3e7ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -204,7 +204,7 @@
     // and exit, since exit itself can trigger a number of changes that update the stages.
     private boolean mShouldUpdateRecents;
     private boolean mExitSplitScreenOnHide;
-    private boolean mIsSplitEntering;
+    private boolean mIsDividerRemoteAnimating;
     private boolean mIsDropEntering;
     private boolean mIsExiting;
 
@@ -881,7 +881,7 @@
 
         // Set false to avoid record new bounds with old task still on top;
         mShouldUpdateRecents = false;
-        mIsSplitEntering = true;
+        mIsDividerRemoteAnimating = true;
         if (mSplitRequest == null) {
             mSplitRequest = new SplitRequest(mainTaskId,
                     mainPendingIntent != null ? mainPendingIntent.getIntent() : null,
@@ -974,7 +974,7 @@
     }
 
     private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
-        mIsSplitEntering = false;
+        mIsDividerRemoteAnimating = false;
         mShouldUpdateRecents = true;
         mSplitRequest = null;
         // If any stage has no child after animation finished, it means that split will display
@@ -1240,7 +1240,7 @@
             }
         });
         mShouldUpdateRecents = false;
-        mIsSplitEntering = false;
+        mIsDividerRemoteAnimating = false;
 
         mSplitLayout.getInvisibleBounds(mTempRect1);
         if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
@@ -1573,7 +1573,7 @@
                 && !ENABLE_SHELL_TRANSITIONS) {
             // Clear the divider remote animating flag as the divider will be re-rendered to apply
             // the new rotation config.
-            mIsSplitEntering = false;
+            mIsDividerRemoteAnimating = false;
             mSplitLayout.update(null /* t */);
             onLayoutSizeChanged(mSplitLayout);
         }
@@ -1623,9 +1623,9 @@
     }
 
     void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
+        // Handle entering split screen while there is a split pair running in the background.
         if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
-                && !mIsSplitEntering) {
-            // Handle entring split case here if split already running background.
+                && mSplitRequest == null) {
             if (mIsDropEntering) {
                 mSplitLayout.resetDividerPosition();
             } else {
@@ -1717,7 +1717,7 @@
         mDividerVisible = visible;
         sendSplitVisibilityChanged();
 
-        if (mIsSplitEntering) {
+        if (mIsDividerRemoteAnimating) {
             ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                     "   Skip animating divider bar due to it's remote animating.");
             return;
@@ -1737,7 +1737,7 @@
                     "   Skip animating divider bar due to divider leash not ready.");
             return;
         }
-        if (mIsSplitEntering) {
+        if (mIsDividerRemoteAnimating) {
             ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                     "   Skip animating divider bar due to it's remote animating.");
             return;
@@ -1805,7 +1805,8 @@
                 mSplitLayout.flingDividerToDismiss(
                         mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT,
                         EXIT_REASON_APP_FINISHED);
-            } else if (!isSplitScreenVisible() && !mIsSplitEntering) {
+            } else if (!isSplitScreenVisible() && mSplitRequest == null) {
+                // Dismiss split screen in the background once any sides of the split become empty.
                 exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
             }
         } else if (isSideStage && hasChildren && !mMainStage.isActive()) {
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt
new file mode 100644
index 0000000..f64ea45
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UElement
+
+class DemotingTestWithoutBugDetector : Detector(), SourceCodeScanner {
+    override fun getApplicableUastTypes(): List<Class<out UElement>> {
+        return listOf(UAnnotation::class.java)
+    }
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return object : UElementHandler() {
+            override fun visitAnnotation(node: UAnnotation) {
+                if (node.qualifiedName !in DEMOTING_ANNOTATION) {
+                    return
+                }
+                val bugId = node.findAttributeValue("bugId")!!.evaluate() as Int
+                if (bugId <= 0) {
+                    val location = context.getLocation(node)
+                    val message = "Please attach a bug id to track demoted test"
+                    context.report(ISSUE, node, location, message)
+                }
+            }
+        }
+    }
+
+    companion object {
+        val DEMOTING_ANNOTATION =
+            listOf("androidx.test.filters.FlakyTest", "android.platform.test.annotations.FlakyTest")
+
+        @JvmField
+        val ISSUE: Issue =
+            Issue.create(
+                id = "DemotingTestWithoutBug",
+                briefDescription = "Demoting a test without attaching a bug.",
+                explanation =
+                    """
+                    Annotations (`@FlakyTest`) demote tests to an unmonitored \
+                    test suite. Please set the `bugId` field in such annotations to track \
+                    the test status.
+                    """,
+                category = Category.TESTING,
+                priority = 8,
+                severity = Severity.WARNING,
+                implementation =
+                    Implementation(
+                        DemotingTestWithoutBugDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                    )
+            )
+    }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 84f7050..387b67d 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -39,7 +39,8 @@
                 RegisterReceiverViaContextDetector.ISSUE,
                 SoftwareBitmapDetector.ISSUE,
                 NonInjectedServiceDetector.ISSUE,
-                StaticSettingsProviderDetector.ISSUE
+                StaticSettingsProviderDetector.ISSUE,
+                DemotingTestWithoutBugDetector.ISSUE
             )
 
     override val api: Int
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt
new file mode 100644
index 0000000..557c300
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class DemotingTestWithoutBugDetectorTest : SystemUILintDetectorTest() {
+
+    override fun getDetector(): Detector = DemotingTestWithoutBugDetector()
+    override fun getIssues(): List<Issue> = listOf(DemotingTestWithoutBugDetector.ISSUE)
+
+    @Test
+    fun testMarkFlaky_withBugId() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import androidx.test.filters.FlakyTest;
+
+                        @FlakyTest(bugId = 123)
+                        public class TestClass {
+                            public void testCase() {}
+                        }
+                    """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(DemotingTestWithoutBugDetector.ISSUE)
+            .run()
+            .expectClean()
+
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import android.platform.test.annotations.FlakyTest;
+
+                        @FlakyTest(bugId = 123)
+                        public class TestClass {
+                            public void testCase() {}
+                        }
+                    """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(DemotingTestWithoutBugDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun testMarkFlaky_withoutBugId() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import androidx.test.filters.FlakyTest;
+
+                        @FlakyTest
+                        public class TestClass {
+                            public void testCase() {}
+                        }
+                    """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(DemotingTestWithoutBugDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                src/test/pkg/TestClass.java:4: Warning: Please attach a bug id to track demoted test [DemotingTestWithoutBug]
+                @FlakyTest
+                ~~~~~~~~~~
+                0 errors, 1 warnings
+                """
+            )
+
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import android.platform.test.annotations.FlakyTest;
+
+                        @FlakyTest
+                        public class TestClass {
+                            public void testCase() {}
+                        }
+                    """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(DemotingTestWithoutBugDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                src/test/pkg/TestClass.java:4: Warning: Please attach a bug id to track demoted test [DemotingTestWithoutBug]
+                @FlakyTest
+                ~~~~~~~~~~
+                0 errors, 1 warnings
+                """
+            )
+    }
+
+    private val filtersFlakyTestStub: TestFile =
+        java(
+            """
+        package androidx.test.filters;
+
+        public @interface FlakyTest {
+            int bugId() default -1;
+        }
+        """
+        )
+    private val annotationsFlakyTestStub: TestFile =
+        java(
+            """
+        package android.platform.test.annotations;
+
+        public @interface FlakyTest {
+            int bugId() default -1;
+        }
+        """
+        )
+    private val stubs = arrayOf(filtersFlakyTestStub, annotationsFlakyTestStub)
+}
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 992d143..6cc5b9d 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -78,7 +78,7 @@
     <!-- The vertical margin between the date and the owner info. -->
 
     <!-- The translation for disappearing security views after having solved them. -->
-    <dimen name="disappear_y_translation">-50dp</dimen>
+    <dimen name="disappear_y_translation">-32dp</dimen>
 
     <!-- Dimens for animation for the Bouncer PIN view -->
     <dimen name="pin_view_trans_y_entry">120dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 6bfaf5e..b2add4f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.app.ActivityTaskManager.getService;
 
 import android.annotation.NonNull;
@@ -45,6 +44,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.Display;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationTarget;
@@ -112,6 +112,13 @@
     }
 
     /**
+     * @see #getRunningTasks(boolean , int)
+     */
+    public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) {
+        return getRunningTasks(filterOnlyVisibleRecents, Display.INVALID_DISPLAY);
+    }
+
+    /**
      * We ask for {@link #NUM_RECENT_ACTIVITIES_REQUEST} activities because when in split screen,
      * we'll get back 2 activities for each split app and one for launcher. Launcher might be more
      * "recently" used than one of the split apps so if we only request 2 tasks, then we might miss
@@ -120,10 +127,12 @@
      * @return an array of up to {@link #NUM_RECENT_ACTIVITIES_REQUEST} running tasks
      *         filtering only for tasks that can be visible in the recent tasks list.
      */
-    public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) {
+    public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents,
+            int displayId) {
         // Note: The set of running tasks from the system is ordered by recency
         List<ActivityManager.RunningTaskInfo> tasks =
-                mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents);
+                mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST,
+                        filterOnlyVisibleRecents, /* keepInExtras= */ false, displayId);
         return tasks.toArray(new RunningTaskInfo[tasks.size()]);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index c1fae9e..33bea02 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -69,6 +69,7 @@
 
     private Interpolator mLinearOutSlowInInterpolator;
     private Interpolator mFastOutLinearInInterpolator;
+    private DisappearAnimationListener mDisappearAnimationListener;
 
     public KeyguardPasswordView(Context context) {
         this(context, null);
@@ -186,9 +187,13 @@
                                 return;
                             }
                             Insets shownInsets = controller.getShownStateInsets();
-                            Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
-                                    (int) (-shownInsets.bottom / 4
-                                            * anim.getAnimatedFraction())));
+                            int dist = (int) (-shownInsets.bottom / 4
+                                    * anim.getAnimatedFraction());
+                            Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0, dist));
+                            if (mDisappearAnimationListener != null) {
+                                mDisappearAnimationListener.setTranslationY(-dist);
+                            }
+
                             controller.setInsetsAndAlpha(insets,
                                     (float) animation.getAnimatedValue(),
                                     anim.getAnimatedFraction());
@@ -209,6 +214,7 @@
                                     controller.finish(false);
                                     runOnFinishImeAnimationRunnable();
                                     finishRunnable.run();
+                                    mDisappearAnimationListener = null;
                                     Trace.endSection();
                                 });
                             }
@@ -286,4 +292,19 @@
             }
         });
     }
+
+    /**
+     * Listens to the progress of the disappear animation and handles it.
+     */
+    interface DisappearAnimationListener {
+        void setTranslationY(int transY);
+    }
+
+    /**
+     * Set an instance of the disappear animation listener to this class. This will be
+     * removed when the animation completes.
+     */
+    public void setDisappearAnimationListener(DisappearAnimationListener listener) {
+        mDisappearAnimationListener = listener;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c6f0eee..2949616 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -39,6 +39,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.Activity;
@@ -173,6 +174,17 @@
     private @Mode int mCurrentMode = MODE_UNINITIALIZED;
     private int mWidth = -1;
 
+    /**
+     * This callback is used to animate KeyguardSecurityContainer and its child views based on
+     * the interaction with the ime. After
+     * {@link WindowInsetsAnimation.Callback#onPrepare(WindowInsetsAnimation)},
+     * {@link #onApplyWindowInsets} is called where we
+     * set the bottom padding to be the height of the keyboard. We use this padding to determine
+     * the delta of vertical distance for y-translation animations.
+     * Note that bottom padding is not set when the disappear animation is started because
+     * we are deferring the y translation logic to the animator in
+     * {@link KeyguardPasswordView#startDisappearAnimation(Runnable)}
+     */
     private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
             new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
 
@@ -213,7 +225,6 @@
                             continue;
                         }
                         interpolatedFraction = animation.getInterpolatedFraction();
-
                         final int paddingBottom = (int) MathUtils.lerp(
                                 start, end,
                                 interpolatedFraction);
@@ -568,13 +579,21 @@
      */
     public void startDisappearAnimation(SecurityMode securitySelection) {
         mDisappearAnimRunning = true;
-        mViewMode.startDisappearAnimation(securitySelection);
+        if (securitySelection == SecurityMode.Password
+                && mSecurityViewFlipper.getSecurityView() instanceof KeyguardPasswordView) {
+            ((KeyguardPasswordView) mSecurityViewFlipper.getSecurityView())
+                    .setDisappearAnimationListener(this::setTranslationY);
+        } else {
+            mViewMode.startDisappearAnimation(securitySelection);
+        }
     }
 
     /**
      * This will run when the bouncer shows in all cases except when the user drags the bouncer up.
      */
     public void startAppearAnimation(SecurityMode securityMode) {
+        setTranslationY(0f);
+        setAlpha(1f);
         updateChildren(0 /* translationY */, 1f /* alpha */);
         mViewMode.startAppearAnimation(securityMode);
     }
@@ -623,7 +642,13 @@
         int inset = max(bottomInset, imeInset);
         int paddingBottom = max(inset, getContext().getResources()
                 .getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin));
-        setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom);
+        // If security mode is password, we rely on the animation value of defined in
+        // KeyguardPasswordView to determine the y translation animation.
+        // This means that we will prevent the WindowInsetsAnimationCallback from setting any y
+        // translation values by preventing the setting of the padding here.
+        if (!mDisappearAnimRunning) {
+            setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom);
+        }
         return insets.inset(0, 0, 0, inset);
     }
 
@@ -1043,10 +1068,13 @@
 
             int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
 
+            AnimatorSet anims = new AnimatorSet();
             ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
-            yAnim.setInterpolator(Interpolators.STANDARD_ACCELERATE);
-            yAnim.setDuration(500);
-            yAnim.start();
+            ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f);
+
+            anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+            anims.playTogether(alphaAnim, yAnim);
+            anims.start();
         }
 
         private void setupUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index c32b853..b8bb260 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -636,17 +636,12 @@
 
     public void startAppearAnimation() {
         if (mCurrentSecurityMode != SecurityMode.None) {
-            setAlpha(1f);
+            mView.setAlpha(1f);
             mView.startAppearAnimation(mCurrentSecurityMode);
             getCurrentSecurityController().startAppearAnimation();
         }
     }
 
-    /** Set the alpha of the security container view */
-    public void setAlpha(float alpha) {
-        mView.setAlpha(alpha);
-    }
-
     public boolean startDisappearAnimation(Runnable onFinishRunnable) {
         boolean didRunAnimation = false;
 
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index c214f53..2501be9 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -263,10 +263,11 @@
     @Override // ClipboardListener.ClipboardOverlay
     public void setClipData(ClipData data, String source) {
         ClipboardModel model = ClipboardModel.fromClipData(mContext, mClipboardUtils, data, source);
-        if (mExitAnimator != null && mExitAnimator.isRunning()) {
+        boolean wasExiting = (mExitAnimator != null && mExitAnimator.isRunning());
+        if (wasExiting) {
             mExitAnimator.cancel();
         }
-        boolean shouldAnimate = !model.dataMatches(mClipboardModel);
+        boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting;
         mClipboardModel = model;
         mClipboardLogger.setClipSource(mClipboardModel.getSource());
         if (shouldAnimate) {
@@ -313,15 +314,19 @@
                 mOnPreviewTapped = this::editText;
                 break;
             case IMAGE:
-                if (model.isSensitive() || model.loadThumbnail(mContext) != null) {
-                    mView.showImagePreview(
-                            model.isSensitive() ? null : model.loadThumbnail(mContext));
-                    mView.setEditAccessibilityAction(true);
-                    mOnPreviewTapped = () -> editImage(model.getUri());
-                } else {
-                    // image loading failed
-                    mView.showDefaultTextPreview();
-                }
+                mBgExecutor.execute(() -> {
+                    if (model.isSensitive() || model.loadThumbnail(mContext) != null) {
+                        mView.post(() -> {
+                            mView.showImagePreview(
+                                    model.isSensitive() ? null : model.loadThumbnail(mContext));
+                            mView.setEditAccessibilityAction(true);
+                        });
+                        mOnPreviewTapped = () -> editImage(model.getUri());
+                    } else {
+                        // image loading failed
+                        mView.post(mView::showDefaultTextPreview);
+                    }
+                });
                 break;
             case URI:
             case OTHER:
@@ -363,15 +368,15 @@
 
     private void classifyText(ClipboardModel model) {
         mBgExecutor.execute(() -> {
-            Optional<RemoteAction> remoteAction = mClipboardUtils.getAction(
-                            model.getText(), model.getTextLinks(), model.getSource());
+            Optional<RemoteAction> remoteAction =
+                    mClipboardUtils.getAction(model.getTextLinks(), model.getSource());
             if (model.equals(mClipboardModel)) {
                 remoteAction.ifPresent(action -> {
                     mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_ACTION_SHOWN);
-                    mView.setActionChip(action, () -> {
+                    mView.post(() -> mView.setActionChip(action, () -> {
                         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
                         animateOut();
-                    });
+                    }));
                 });
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
index a85f8b9..25caaea 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
@@ -39,6 +39,9 @@
 
 class ClipboardOverlayUtils {
 
+    // minimum proportion of entire text an entity must take up, to be considered for smart actions
+    private static final float MINIMUM_ENTITY_PROPORTION = .8f;
+
     private final TextClassifier mTextClassifier;
 
     @Inject
@@ -65,19 +68,23 @@
         return false;
     }
 
-    public Optional<RemoteAction> getAction(CharSequence text, TextLinks textLinks, String source) {
-        return getActions(text, textLinks).stream().filter(remoteAction -> {
+    public Optional<RemoteAction> getAction(TextLinks textLinks, String source) {
+        return getActions(textLinks).stream().filter(remoteAction -> {
             ComponentName component = remoteAction.getActionIntent().getIntent().getComponent();
             return component != null && !TextUtils.equals(source, component.getPackageName());
         }).findFirst();
     }
 
-    private ArrayList<RemoteAction> getActions(CharSequence text, TextLinks textLinks) {
+    private ArrayList<RemoteAction> getActions(TextLinks textLinks) {
         ArrayList<RemoteAction> actions = new ArrayList<>();
         for (TextLinks.TextLink link : textLinks.getLinks()) {
-            TextClassification classification = mTextClassifier.classifyText(
-                    text, link.getStart(), link.getEnd(), null);
-            actions.addAll(classification.getActions());
+            // skip classification for incidental entities
+            if (link.getEnd() - link.getStart()
+                    >= textLinks.getText().length() * MINIMUM_ENTITY_PROPORTION) {
+                TextClassification classification = mTextClassifier.classifyText(
+                        textLinks.getText(), link.getStart(), link.getEnd(), null);
+                actions.addAll(classification.getActions());
+            }
         }
         return actions;
     }
@@ -92,9 +99,13 @@
     private ArrayList<RemoteAction> getActions(ClipData.Item item) {
         ArrayList<RemoteAction> actions = new ArrayList<>();
         for (TextLinks.TextLink link : item.getTextLinks().getLinks()) {
-            TextClassification classification = mTextClassifier.classifyText(
-                    item.getText(), link.getStart(), link.getEnd(), null);
-            actions.addAll(classification.getActions());
+            // skip classification for incidental entities
+            if (link.getEnd() - link.getStart()
+                    >= item.getText().length() * MINIMUM_ENTITY_PROPORTION) {
+                TextClassification classification = mTextClassifier.classifyText(
+                        item.getText(), link.getStart(), link.getEnd(), null);
+                actions.addAll(classification.getActions());
+            }
         }
         return actions;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index fb01691..cb2d673 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -29,6 +29,7 @@
 import com.android.systemui.settings.brightness.BrightnessDialog;
 import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
 import com.android.systemui.tuner.TunerActivity;
+import com.android.systemui.usb.UsbAccessoryUriActivity;
 import com.android.systemui.usb.UsbConfirmActivity;
 import com.android.systemui.usb.UsbDebuggingActivity;
 import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
@@ -94,6 +95,12 @@
     @ClassKey(UsbConfirmActivity.class)
     public abstract Activity bindUsbConfirmActivity(UsbConfirmActivity activity);
 
+    /** Inject into UsbAccessoryUriActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(UsbAccessoryUriActivity.class)
+    public abstract Activity bindUsbAccessoryUriActivity(UsbAccessoryUriActivity activity);
+
     /** Inject into CreateUserActivity. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f0ee443..cedc226a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -75,6 +75,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.connectivity.ConnectivityModule;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -256,9 +257,7 @@
     abstract FingerprintInteractiveToAuthProvider optionalFingerprintInteractiveToAuthProvider();
 
     @BindsOptionalOf
-    //TODO(b/269430792 remove full qualifier. Full qualifier is used to avoid merge conflict.)
-    abstract com.android.systemui.statusbar.events.SystemStatusAnimationScheduler
-    optionalSystemStatusAnimationScheduler();
+    abstract SystemStatusAnimationScheduler optionalSystemStatusAnimationScheduler();
 
     @SysUISingleton
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 73c2289..a7b3bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -254,7 +254,10 @@
         mCurrentScrimController = mScrimManager.getCurrentController();
 
         session.registerCallback(() -> {
-            mVelocityTracker.recycle();
+            if (mVelocityTracker != null) {
+                mVelocityTracker.recycle();
+                mVelocityTracker = null;
+            }
             mScrimManager.removeCallback(mScrimManagerCallback);
             mCapture = null;
             mNotificationShadeWindowController.setForcePluginOpen(false, this);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 5ae86b1..9b36b92 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -352,7 +352,7 @@
     val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE = releasedFlag(910, "media_ttt_receiver_success_ripple")
 
     // TODO(b/263512203): Tracking Bug
-    val MEDIA_EXPLICIT_INDICATOR = unreleasedFlag(911, "media_explicit_indicator", teamfood = true)
+    val MEDIA_EXPLICIT_INDICATOR = unreleasedFlag(911, "media_explicit_indicator")
 
     // TODO(b/265813373): Tracking Bug
     val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE = releasedFlag(912, "media_ttt_dismiss_gesture")
@@ -385,6 +385,9 @@
     // TODO(b/265045965): Tracking Bug
     val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot")
 
+    @JvmField
+    val ENABLE_LOW_LIGHT_CLOCK_UNDOCKED = unreleasedFlag(1004, "enable_low_light_clock_undocked")
+
     // 1100 - windowing
     @Keep
     @JvmField
@@ -461,7 +464,7 @@
     @Keep
     @JvmField
     val ENABLE_PIP_SIZE_LARGE_SCREEN =
-        sysPropBooleanFlag(1114, "persist.wm.debug.enable_pip_size_large_screen", default = false)
+        sysPropBooleanFlag(1114, "persist.wm.debug.enable_pip_size_large_screen", default = true)
 
     // TODO(b/265998256): Tracking bug
     @Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
index b0f9c4e..d078688 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyboard.backlight.ui.KeyboardBacklightDialogCoordinator
 import javax.inject.Inject
 
 /** A [CoreStartable] that launches components interested in physical keyboard interaction. */
@@ -28,11 +29,12 @@
 class PhysicalKeyboardCoreStartable
 @Inject
 constructor(
+    private val keyboardBacklightDialogCoordinator: KeyboardBacklightDialogCoordinator,
     private val featureFlags: FeatureFlags,
 ) : CoreStartable {
     override fun start() {
         if (featureFlags.isEnabled(Flags.KEYBOARD_BACKLIGHT_INDICATOR)) {
-            // TODO(b/268645743) start listening for keyboard backlight brightness
+            keyboardBacklightDialogCoordinator.startListening()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
new file mode 100644
index 0000000..65e70b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.keyboard.backlight.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+
+/** Allows listening to changes to keyboard backlight level */
+@SysUISingleton
+class KeyboardBacklightInteractor
+@Inject
+constructor(
+    private val keyboardRepository: KeyboardRepository,
+) {
+
+    /** Emits current backlight level as [BacklightModel] or null if keyboard is not connected */
+    val backlight: Flow<BacklightModel?> =
+        keyboardRepository.keyboardConnected.flatMapLatest { connected ->
+            if (connected) keyboardRepository.backlight else flowOf(null)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
new file mode 100644
index 0000000..85d0379
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.keyboard.backlight.ui
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Based on the state produced from [BacklightDialogViewModel] shows or hides keyboard backlight
+ * indicator
+ */
+@SysUISingleton
+class KeyboardBacklightDialogCoordinator
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val context: Context,
+    private val viewModel: BacklightDialogViewModel,
+) {
+
+    var dialog: KeyboardBacklightDialog? = null
+
+    fun startListening() {
+        applicationScope.launch {
+            viewModel.dialogContent.collect { dialogViewModel ->
+                if (dialogViewModel != null) {
+                    if (dialog == null) {
+                        dialog = KeyboardBacklightDialog(context, dialogViewModel)
+                        // pass viewModel and show
+                    }
+                } else {
+                    dialog?.dismiss()
+                    dialog = null
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
new file mode 100644
index 0000000..b68a2a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.keyboard.backlight.ui.view
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogContentViewModel
+
+class KeyboardBacklightDialog(context: Context, val viewModel: BacklightDialogContentViewModel) :
+    Dialog(context) {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        // TODO(b/268650355) Implement the dialog
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
similarity index 68%
copy from packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
copy to packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
index ea15a9f..3ef0ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
@@ -15,10 +15,6 @@
  *
  */
 
-package com.android.systemui.keyboard.data.model
+package com.android.systemui.keyboard.backlight.ui.viewmodel
 
-/**
- * Model for current state of keyboard backlight brightness. [level] indicates current level of
- * backlight brightness and [maxLevel] its max possible value.
- */
-data class BacklightModel(val level: Int, val maxLevel: Int)
+data class BacklightDialogContentViewModel(val currentValue: Int, val maxValue: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt
new file mode 100644
index 0000000..86abca5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.keyboard.backlight.ui.viewmodel
+
+import android.view.accessibility.AccessibilityManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import javax.inject.Inject
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Responsible for dialog visibility and content - emits [BacklightDialogContentViewModel] if dialog
+ * should be shown and hidden otherwise
+ */
+@SysUISingleton
+class BacklightDialogViewModel
+@Inject
+constructor(
+    interactor: KeyboardBacklightInteractor,
+    private val accessibilityManagerWrapper: AccessibilityManagerWrapper,
+) {
+
+    private val timeoutMillis: Long
+        get() =
+            accessibilityManagerWrapper
+                .getRecommendedTimeoutMillis(
+                    DEFAULT_DIALOG_TIMEOUT_MILLIS,
+                    AccessibilityManager.FLAG_CONTENT_ICONS
+                )
+                .toLong()
+
+    val dialogContent: Flow<BacklightDialogContentViewModel?> =
+        interactor.backlight
+            .filterNotNull()
+            .map { BacklightDialogContentViewModel(it.level, it.maxLevel) }
+            .timeout(timeoutMillis, emitAfterTimeout = null)
+
+    private fun <T> Flow<T>.timeout(timeoutMillis: Long, emitAfterTimeout: T): Flow<T> {
+        return flatMapLatest {
+            flow {
+                emit(it)
+                delay(timeoutMillis)
+                emit(emitAfterTimeout)
+            }
+        }
+    }
+
+    private companion object {
+        const val DEFAULT_DIALOG_TIMEOUT_MILLIS = 3000
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
index 70faf40..9449ece 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.data.model.BacklightModel
+import com.android.systemui.keyboard.shared.model.BacklightModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
rename to packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
index ea15a9f..4a32f79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
@@ -15,7 +15,7 @@
  *
  */
 
-package com.android.systemui.keyboard.data.model
+package com.android.systemui.keyboard.shared.model
 
 /**
  * Model for current state of keyboard backlight brightness. [level] indicates current level of
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 76f20d25..a3b3d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -80,9 +80,6 @@
      */
     val isKeyguardShowing: Flow<Boolean>
 
-    /** Is the keyguard in a unlocked state? */
-    val isKeyguardUnlocked: Flow<Boolean>
-
     /** Is an activity showing over the keyguard? */
     val isKeyguardOccluded: Flow<Boolean>
 
@@ -281,31 +278,6 @@
             }
             .distinctUntilChanged()
 
-    override val isKeyguardUnlocked: Flow<Boolean> =
-        conflatedCallbackFlow {
-                val callback =
-                    object : KeyguardStateController.Callback {
-                        override fun onUnlockedChanged() {
-                            trySendWithFailureLogging(
-                                keyguardStateController.isUnlocked,
-                                TAG,
-                                "updated isKeyguardUnlocked"
-                            )
-                        }
-                    }
-
-                keyguardStateController.addCallback(callback)
-                // Adding the callback does not send an initial update.
-                trySendWithFailureLogging(
-                    keyguardStateController.isUnlocked,
-                    TAG,
-                    "initial isKeyguardUnlocked"
-                )
-
-                awaitClose { keyguardStateController.removeCallback(callback) }
-            }
-            .distinctUntilChanged()
-
     override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
         val callback =
             object : KeyguardStateController.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 100bc59..0c4bca6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -68,11 +68,8 @@
     /**
      * Begin a transition from one state to another. Transitions are interruptible, and will issue a
      * [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one.
-     *
-     * When canceled, there are two options: to continue from the current position of the prior
-     * transition, or to reset the position. When [resetIfCanceled] == true, it will do the latter.
      */
-    fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean = false): UUID?
+    fun startTransition(info: TransitionInfo): UUID?
 
     /**
      * Allows manual control of a transition. When calling [startTransition], the consumer must pass
@@ -133,10 +130,7 @@
         )
     }
 
-    override fun startTransition(
-        info: TransitionInfo,
-        resetIfCanceled: Boolean,
-    ): UUID? {
+    override fun startTransition(info: TransitionInfo): UUID? {
         if (lastStep.from == info.from && lastStep.to == info.to) {
             Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
             return null
@@ -144,11 +138,7 @@
         val startingValue =
             if (lastStep.transitionState != TransitionState.FINISHED) {
                 Log.i(TAG, "Transition still active: $lastStep, canceling")
-                if (resetIfCanceled) {
-                    0f
-                } else {
-                    lastStep.value
-                }
+                lastStep.value
             } else {
                 0f
             }
@@ -237,7 +227,10 @@
     }
 
     private fun trace(step: TransitionStep, isManual: Boolean) {
-        if (step.transitionState == TransitionState.RUNNING) {
+        if (
+            step.transitionState != TransitionState.STARTED &&
+                step.transitionState != TransitionState.FINISHED
+        ) {
             return
         }
         val traceName =
@@ -250,10 +243,7 @@
         val traceCookie = traceName.hashCode()
         if (step.transitionState == TransitionState.STARTED) {
             Trace.beginAsyncSection(traceName, traceCookie)
-        } else if (
-            step.transitionState == TransitionState.FINISHED ||
-                step.transitionState == TransitionState.CANCELED
-        ) {
+        } else if (step.transitionState == TransitionState.FINISHED) {
             Trace.endAsyncSection(traceName, traceCookie)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 3beac0b..8715d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -34,6 +34,7 @@
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
@@ -56,7 +57,14 @@
 
     private fun listenForDreamingToLockscreen() {
         scope.launch {
-            keyguardInteractor.isAbleToDream
+            // Dependending on the dream, either dream state or occluded change will change first,
+            // so listen for both
+            combine(keyguardInteractor.isAbleToDream, keyguardInteractor.isKeyguardOccluded) {
+                    isAbleToDream,
+                    isKeyguardOccluded ->
+                    isAbleToDream && isKeyguardOccluded
+                }
+                .distinctUntilChanged()
                 .sample(
                     combine(
                         keyguardInteractor.dozeTransitionModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 911861d..d01f489 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -130,59 +130,55 @@
             shadeRepository.shadeModel
                 .sample(
                     combine(
-                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        keyguardTransitionInteractor.finishedKeyguardState,
                         keyguardInteractor.statusBarState,
-                        keyguardInteractor.isKeyguardUnlocked,
-                        ::toTriple
+                        ::Pair
                     ),
-                    ::toQuad
+                    ::toTriple
                 )
-                .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) ->
+                .collect { (shadeModel, keyguardState, statusBarState) ->
                     val id = transitionId
                     if (id != null) {
-                        if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
-                            // An existing `id` means a transition is started, and calls to
-                            // `updateTransition` will control it until FINISHED or CANCELED
-                            var nextState =
-                                if (shadeModel.expansionAmount == 0f) {
-                                    TransitionState.FINISHED
-                                } else if (shadeModel.expansionAmount == 1f) {
-                                    TransitionState.CANCELED
-                                } else {
-                                    TransitionState.RUNNING
-                                }
-                            keyguardTransitionRepository.updateTransition(
-                                id,
-                                1f - shadeModel.expansionAmount,
-                                nextState,
-                            )
-
-                            if (
-                                nextState == TransitionState.CANCELED ||
-                                    nextState == TransitionState.FINISHED
-                            ) {
-                                transitionId = null
+                        // An existing `id` means a transition is started, and calls to
+                        // `updateTransition` will control it until FINISHED or CANCELED
+                        var nextState =
+                            if (shadeModel.expansionAmount == 0f) {
+                                TransitionState.FINISHED
+                            } else if (shadeModel.expansionAmount == 1f) {
+                                TransitionState.CANCELED
+                            } else {
+                                TransitionState.RUNNING
                             }
+                        keyguardTransitionRepository.updateTransition(
+                            id,
+                            1f - shadeModel.expansionAmount,
+                            nextState,
+                        )
 
-                            // If canceled, just put the state back
-                            if (nextState == TransitionState.CANCELED) {
-                                keyguardTransitionRepository.startTransition(
-                                    TransitionInfo(
-                                        ownerName = name,
-                                        from = KeyguardState.PRIMARY_BOUNCER,
-                                        to = KeyguardState.LOCKSCREEN,
-                                        animator = getAnimator(0.milliseconds)
-                                    )
+                        if (
+                            nextState == TransitionState.CANCELED ||
+                                nextState == TransitionState.FINISHED
+                        ) {
+                            transitionId = null
+                        }
+
+                        // If canceled, just put the state back
+                        if (nextState == TransitionState.CANCELED) {
+                            keyguardTransitionRepository.startTransition(
+                                TransitionInfo(
+                                    ownerName = name,
+                                    from = KeyguardState.PRIMARY_BOUNCER,
+                                    to = KeyguardState.LOCKSCREEN,
+                                    animator = getAnimator(0.milliseconds)
                                 )
-                            }
+                            )
                         }
                     } else {
                         // TODO (b/251849525): Remove statusbarstate check when that state is
                         // integrated into KeyguardTransitionRepository
                         if (
-                            keyguardState.to == KeyguardState.LOCKSCREEN &&
+                            keyguardState == KeyguardState.LOCKSCREEN &&
                                 shadeModel.isUserDragging &&
-                                !isKeyguardUnlocked &&
                                 statusBarState == KEYGUARD
                         ) {
                             transitionId =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 94961cb..b59b413 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -17,9 +17,6 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.animation.ValueAnimator
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
-import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -29,8 +26,6 @@
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -42,8 +37,7 @@
     @Application private val scope: CoroutineScope,
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardTransitionRepository: KeyguardTransitionRepository,
-    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-    private val keyguardSecurityModel: KeyguardSecurityModel,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor
 ) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) {
 
     override fun start() {
@@ -99,47 +93,31 @@
     private fun listenForPrimaryBouncerToGone() {
         scope.launch {
             keyguardInteractor.isKeyguardGoingAway
-                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { (isKeyguardGoingAway, lastStartedTransitionStep) ->
-                    if (
-                        isKeyguardGoingAway &&
-                            lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
-                    ) {
-                        val securityMode =
-                            keyguardSecurityModel.getSecurityMode(
-                                KeyguardUpdateMonitor.getCurrentUser()
-                            )
-                        // IME for password requires a slightly faster animation
-                        val duration =
-                            if (securityMode == Password) {
-                                TO_GONE_SHORT_DURATION
-                            } else {
-                                TO_GONE_DURATION
-                            }
+                .sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) }
+                .collect { pair ->
+                    val (isKeyguardGoingAway, keyguardState) = pair
+                    if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 ownerName = name,
                                 from = KeyguardState.PRIMARY_BOUNCER,
                                 to = KeyguardState.GONE,
-                                animator = getAnimator(duration),
-                            ),
-                            resetIfCanceled = true,
+                                animator = getAnimator(),
+                            )
                         )
                     }
                 }
         }
     }
 
-    private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
+    private fun getAnimator(): ValueAnimator {
         return ValueAnimator().apply {
             setInterpolator(Interpolators.LINEAR)
-            setDuration(duration.inWholeMilliseconds)
+            setDuration(TRANSITION_DURATION_MS)
         }
     }
 
     companion object {
-        private val DEFAULT_DURATION = 300.milliseconds
-        val TO_GONE_DURATION = 250.milliseconds
-        val TO_GONE_SHORT_DURATION = 200.milliseconds
+        private const val TRANSITION_DURATION_MS = 300L
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index ec99049..d25aff0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -33,9 +33,7 @@
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
-import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
 import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.delay
@@ -97,9 +95,6 @@
         awaitClose { commandQueue.removeCallback(callback) }
     }
 
-    /** The device wake/sleep state */
-    val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
-
     /**
      * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
      * that doze mode is not running and DREAMING is ok to commence.
@@ -114,12 +109,6 @@
                     isDreaming && isDozeOff(dozeTransitionModel.to)
                 }
             )
-            .sample(
-                wakefulnessModel,
-                { isAbleToDream, wakefulnessModel ->
-                    isAbleToDream && isWakingOrStartingToWake(wakefulnessModel)
-                }
-            )
             .flatMapLatest { isAbleToDream ->
                 flow {
                     delay(50)
@@ -130,8 +119,6 @@
 
     /** Whether the keyguard is showing or not. */
     val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
-    /** Whether the keyguard is unlocked or not. */
-    val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
     /** Whether the keyguard is occluded (covered by an activity). */
     val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
     /** Whether the keyguard is going away. */
@@ -140,6 +127,8 @@
     val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible
     /** Whether the alternate bouncer is showing or not. */
     val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
+    /** The device wake/sleep state */
+    val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
     /** Observable for the [StatusBarState] */
     val statusBarState: Flow<StatusBarState> = repository.statusBarState
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index e650b9f..51b0277 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -61,15 +61,7 @@
         }
 
         scope.launch {
-            keyguardInteractor.isAbleToDream.collect {
-                logger.log(TAG, VERBOSE, "isAbleToDream", it)
-            }
-        }
-
-        scope.launch {
-            keyguardInteractor.isKeyguardOccluded.collect {
-                logger.log(TAG, VERBOSE, "isOccluded", it)
-            }
+            keyguardInteractor.isDreaming.collect { logger.log(TAG, VERBOSE, "isDreaming", it) }
         }
 
         scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 3c0ec35..1b7da5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -78,10 +78,6 @@
     val occludedToLockscreenTransition: Flow<TransitionStep> =
         repository.transition(OCCLUDED, LOCKSCREEN)
 
-    /** PRIMARY_BOUNCER->GONE transition information. */
-    val primaryBouncerToGoneTransition: Flow<TransitionStep> =
-        repository.transition(PRIMARY_BOUNCER, GONE)
-
     /**
      * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
      * Lockscreen (0f).
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 2337ffc..7db567b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.keyguard.data.BouncerViewDelegate
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.ActivityStarter
 import kotlinx.coroutines.awaitCancellation
@@ -45,7 +44,6 @@
     fun bind(
         view: ViewGroup,
         viewModel: KeyguardBouncerViewModel,
-        primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
         componentFactory: KeyguardBouncerComponent.Factory
     ) {
         // Builds the KeyguardSecurityContainerController from bouncer view group.
@@ -147,12 +145,6 @@
                     }
 
                     launch {
-                        primaryBouncerToGoneTransitionViewModel.bouncerAlpha.collect { alpha ->
-                            securityContainerController.setAlpha(alpha)
-                        }
-                    }
-
-                    launch {
                         viewModel.bouncerExpansionAmount
                             .filter { it == EXPANSION_VISIBLE }
                             .collect {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
deleted file mode 100644
index 0890791..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
- * consume.
- */
-@SysUISingleton
-class PrimaryBouncerToGoneTransitionViewModel
-@Inject
-constructor(
-    private val interactor: KeyguardTransitionInteractor,
-) {
-    private val transitionAnimation =
-        KeyguardTransitionAnimationFlow(
-            transitionDuration = TO_GONE_DURATION,
-            transitionFlow = interactor.primaryBouncerToGoneTransition,
-        )
-
-    /** Bouncer container alpha */
-    val bouncerAlpha: Flow<Float> =
-        transitionAnimation.createFlow(
-            duration = 200.milliseconds,
-            onStep = { 1f - it },
-        )
-
-    /** Scrim alpha */
-    val scrimAlpha: Flow<Float> =
-        transitionAnimation.createFlow(
-            duration = TO_GONE_DURATION,
-            interpolator = EMPHASIZED_ACCELERATE,
-            onStep = { 1f - it },
-        )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 25ff308b..019ca52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -631,7 +631,9 @@
         final NavigationBarView navBarView =
                 mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
         final NotificationPanelViewController panelController =
-                mCentralSurfacesOptionalLazy.get().get().getNotificationPanelViewController();
+                mCentralSurfacesOptionalLazy.get()
+                        .map(CentralSurfaces::getNotificationPanelViewController)
+                        .orElse(null);
         if (SysUiState.DEBUG) {
             Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
                     + " navBarView=" + navBarView + " panelController=" + panelController);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e53eea9..3be2417 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -23,6 +23,7 @@
 import static androidx.constraintlayout.widget.ConstraintSet.END;
 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
 
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION;
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
@@ -71,6 +72,7 @@
 import android.provider.Settings;
 import android.transition.ChangeBounds;
 import android.transition.Transition;
+import android.transition.TransitionListenerAdapter;
 import android.transition.TransitionManager;
 import android.transition.TransitionSet;
 import android.transition.TransitionValues;
@@ -98,6 +100,7 @@
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.policy.SystemBarUtils;
@@ -353,6 +356,7 @@
     private final NotificationGutsManager mGutsManager;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
     private final QuickSettingsController mQsController;
+    private final InteractionJankMonitor mInteractionJankMonitor;
 
     private long mDownTime;
     private boolean mTouchSlopExceededBeforeDown;
@@ -642,6 +646,19 @@
                     step.getTransitionState() == TransitionState.RUNNING;
             };
 
+    private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
+            new TransitionListenerAdapter() {
+                @Override
+                public void onTransitionCancel(Transition transition) {
+                    mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
+                }
+
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    mInteractionJankMonitor.end(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
+                }
+            };
+
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
             @Main Handler handler,
@@ -706,6 +723,7 @@
             NotificationStackSizeCalculator notificationStackSizeCalculator,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             ShadeTransitionController shadeTransitionController,
+            InteractionJankMonitor interactionJankMonitor,
             SystemClock systemClock,
             KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
             KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
@@ -720,6 +738,7 @@
             DumpManager dumpManager,
             KeyguardLongPressViewModel keyguardLongPressViewModel,
             KeyguardInteractor keyguardInteractor) {
+        mInteractionJankMonitor = interactionJankMonitor;
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
             public void onKeyguardFadingAwayChanged() {
@@ -1540,6 +1559,7 @@
             int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline;
             constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END);
             if (animate) {
+                mInteractionJankMonitor.begin(mView, CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
                 ChangeBounds transition = new ChangeBounds();
                 if (mSplitShadeEnabled) {
                     // Excluding media from the transition on split-shade, as it doesn't transition
@@ -1563,6 +1583,7 @@
                     // The clock container can sometimes be null. If it is, just fall back to the
                     // old animation rather than setting up the custom animations.
                     if (clockContainerView == null || clockContainerView.getChildCount() == 0) {
+                        transition.addListener(mKeyguardStatusAlignmentTransitionListener);
                         TransitionManager.beginDelayedTransition(
                                 mNotificationContainerParent, transition);
                     } else {
@@ -1581,10 +1602,11 @@
                         adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION);
                         adapter.addTarget(clockView);
                         set.addTransition(adapter);
-
+                        set.addListener(mKeyguardStatusAlignmentTransitionListener);
                         TransitionManager.beginDelayedTransition(mNotificationContainerParent, set);
                     }
                 } else {
+                    transition.addListener(mKeyguardStatusAlignmentTransitionListener);
                     TransitionManager.beginDelayedTransition(
                             mNotificationContainerParent, transition);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index c130b39..87350b46 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -45,7 +45,6 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationInsetsController;
@@ -134,8 +133,7 @@
             KeyguardBouncerViewModel keyguardBouncerViewModel,
             KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
             AlternateBouncerInteractor alternateBouncerInteractor,
-            KeyguardTransitionInteractor keyguardTransitionInteractor,
-            PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
+            KeyguardTransitionInteractor keyguardTransitionInteractor
     ) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
@@ -162,7 +160,6 @@
         KeyguardBouncerViewBinder.bind(
                 mView.findViewById(R.id.keyguard_bouncer_container),
                 keyguardBouncerViewModel,
-                primaryBouncerToGoneTransitionViewModel,
                 keyguardBouncerComponentFactory);
 
         collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 39e4000..4522e41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -443,6 +443,7 @@
         CancellationSignal cancellationSignal = new CancellationSignal();
         cancellationSignal.setOnCancelListener(
                 () -> runningInflations.values().forEach(CancellationSignal::cancel));
+
         return cancellationSignal;
     }
 
@@ -783,6 +784,7 @@
     public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
             implements InflationCallback, InflationTask {
 
+        private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L;
         private final NotificationEntry mEntry;
         private final Context mContext;
         private final boolean mInflateSynchronously;
@@ -876,7 +878,7 @@
                         recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight,
                         mUsesIncreasedHeadsUpHeight, packageContext);
                 InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState();
-                return inflateSmartReplyViews(
+                InflationProgress result = inflateSmartReplyViews(
                         inflationProgress,
                         mReInflateFlags,
                         mEntry,
@@ -884,6 +886,11 @@
                         packageContext,
                         previousSmartReplyState,
                         mSmartRepliesInflater);
+
+                // wait for image resolver to finish preloading
+                mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS);
+
+                return result;
             } catch (Exception e) {
                 mError = e;
                 return null;
@@ -918,6 +925,9 @@
                 mCallback.handleInflationException(mRow.getEntry(),
                         new InflationException("Couldn't inflate contentViews" + e));
             }
+
+            // Cancel any image loading tasks, not useful any more
+            mRow.getImageResolver().cancelRunningTasks();
         }
 
         @Override
@@ -944,6 +954,9 @@
             // Notify the resolver that the inflation task has finished,
             // try to purge unnecessary cached entries.
             mRow.getImageResolver().purgeCache();
+
+            // Cancel any image loading tasks that have not completed at this point
+            mRow.getImageResolver().cancelRunningTasks();
         }
 
         private static class RtlEnabledContext extends ContextWrapper {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index 41eeada0..fe0b312 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -22,8 +22,11 @@
 import android.util.Log;
 
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * A cache for inline images of image messages.
@@ -56,12 +59,13 @@
     }
 
     @Override
-    public Drawable get(Uri uri) {
+    public Drawable get(Uri uri, long timeoutMs) {
         Drawable result = null;
         try {
-            result = mCache.get(uri).get();
-        } catch (InterruptedException | ExecutionException ex) {
-            Log.d(TAG, "get: Failed get image from " + uri);
+            result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException
+                | TimeoutException | CancellationException ex) {
+            Log.d(TAG, "get: Failed get image from " + uri + " " + ex);
         }
         return result;
     }
@@ -72,6 +76,15 @@
         mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey()));
     }
 
+    @Override
+    public void cancelRunningTasks() {
+        mCache.forEach((key, value) -> {
+            if (value.getStatus() != AsyncTask.Status.FINISHED) {
+                value.cancel(true);
+            }
+        });
+    }
+
     private static class PreloadImageTask extends AsyncTask<Uri, Void, Drawable> {
         private final NotificationInlineImageResolver mResolver;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index b05e64ab..c620f44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -23,6 +23,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.util.Log;
 
 import com.android.internal.R;
@@ -45,6 +46,9 @@
 public class NotificationInlineImageResolver implements ImageResolver {
     private static final String TAG = NotificationInlineImageResolver.class.getSimpleName();
 
+    // Timeout for loading images from ImageCache when calling from UI thread
+    private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L;
+
     private final Context mContext;
     private final ImageCache mImageCache;
     private Set<Uri> mWantedUriSet;
@@ -123,17 +127,25 @@
         return null;
     }
 
+    /**
+     * Loads an image from the Uri.
+     * This method is synchronous and is usually called from the Main thread.
+     * It will time-out after MAX_UI_THREAD_TIMEOUT_MS.
+     *
+     * @param uri Uri of the target image.
+     * @return drawable of the image, null if loading failed/timeout
+     */
     @Override
     public Drawable loadImage(Uri uri) {
-        return hasCache() ? loadImageFromCache(uri) : resolveImage(uri);
+        return hasCache() ? loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS) : resolveImage(uri);
     }
 
-    private Drawable loadImageFromCache(Uri uri) {
+    private Drawable loadImageFromCache(Uri uri, long timeoutMs) {
         // if the uri isn't currently cached, try caching it first
         if (!mImageCache.hasEntry(uri)) {
             mImageCache.preload((uri));
         }
-        return mImageCache.get(uri);
+        return mImageCache.get(uri, timeoutMs);
     }
 
     /**
@@ -208,6 +220,30 @@
     }
 
     /**
+     * Wait for a maximum timeout for images to finish preloading
+     * @param timeoutMs total timeout time
+     */
+    void waitForPreloadedImages(long timeoutMs) {
+        if (!hasCache()) {
+            return;
+        }
+        Set<Uri> preloadedUris = getWantedUriSet();
+        if (preloadedUris != null) {
+            // Decrement remaining timeout after each image check
+            long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs;
+            preloadedUris.forEach(
+                    uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime()));
+        }
+    }
+
+    void cancelRunningTasks() {
+        if (!hasCache()) {
+            return;
+        }
+        mImageCache.cancelRunningTasks();
+    }
+
+    /**
      * A interface for internal cache implementation of this resolver.
      */
     interface ImageCache {
@@ -216,7 +252,7 @@
          * @param uri The uri of the image.
          * @return Drawable of the image.
          */
-        Drawable get(Uri uri);
+        Drawable get(Uri uri, long timeoutMs);
 
         /**
          * Set the image resolver that actually resolves image from specified uri.
@@ -241,6 +277,11 @@
          * Purge unnecessary entries in the cache.
          */
         void purge();
+
+        /**
+         * Cancel all unfinished image loading tasks
+         */
+        void cancelRunningTasks();
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index b5d51ce..be791f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3695,12 +3695,6 @@
     @Override
     public void notifyBiometricAuthModeChanged() {
         mDozeServiceHost.updateDozing();
-        if (mBiometricUnlockController.getMode()
-                == BiometricUnlockController.MODE_DISMISS_BOUNCER) {
-            // Don't update the scrim controller at this time, in favor of the transition repository
-            // updating the scrim
-            return;
-        }
         updateScrimController();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 753032c..3268032 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -59,7 +59,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.fragment.StatusBarIconBlocklistKt;
-import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventAnimator;
+import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventDefaultAnimator;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -75,6 +75,8 @@
 
 import javax.inject.Inject;
 
+import kotlin.Unit;
+
 /** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
 public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
     private static final String TAG = "KeyguardStatusBarViewController";
@@ -123,7 +125,8 @@
                 public void onDensityOrFontScaleChanged() {
                     mView.loadDimens();
                     // The animator is dependent on resources for offsets
-                    mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, getResources());
+                    mSystemEventAnimator =
+                            getSystemEventAnimator(mSystemEventAnimator.isAnimationRunning());
                 }
 
                 @Override
@@ -248,7 +251,8 @@
     private int mStatusBarState;
     private boolean mDozing;
     private boolean mShowingKeyguardHeadsUp;
-    private StatusBarSystemEventAnimator mSystemEventAnimator;
+    private StatusBarSystemEventDefaultAnimator mSystemEventAnimator;
+    private float mSystemEventAnimatorAlpha = 1;
 
     /**
      * The alpha value to be set on the View. If -1, this value is to be ignored.
@@ -324,7 +328,7 @@
 
         mView.setKeyguardUserAvatarEnabled(
                 !mStatusBarUserChipViewModel.getChipEnabled());
-        mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r);
+        mSystemEventAnimator = getSystemEventAnimator(/* isAnimationRunning */ false);
 
         mDisableStateTracker = new DisableStateTracker(
                 /* mask1= */ DISABLE_SYSTEM_INFO,
@@ -480,6 +484,10 @@
                     * (1.0f - mKeyguardHeadsUpShowingAmount);
         }
 
+        if (mSystemEventAnimator.isAnimationRunning()) {
+            newAlpha = Math.min(newAlpha, mSystemEventAnimatorAlpha);
+        }
+
         boolean hideForBypass =
                 mFirstBypassAttempt && mKeyguardUpdateMonitor.shouldListenForFace()
                         || mDelayShowingKeyguardStatusBar;
@@ -488,7 +496,7 @@
                         && !mDozing
                         && !hideForBypass
                         && !mDisableStateTracker.isDisabled()
-                ? View.VISIBLE : View.INVISIBLE;
+                        ? View.VISIBLE : View.INVISIBLE;
 
         updateViewState(newAlpha, newVisibility);
     }
@@ -614,4 +622,15 @@
             updateBlockedIcons();
         }
     };
+
+    private StatusBarSystemEventDefaultAnimator getSystemEventAnimator(boolean isAnimationRunning) {
+        return new StatusBarSystemEventDefaultAnimator(getResources(), (alpha) -> {
+            mSystemEventAnimatorAlpha = alpha;
+            updateViewState();
+            return Unit.INSTANCE;
+        }, (translationX) -> {
+            mView.setTranslationX(translationX);
+            return Unit.INSTANCE;
+        }, isAnimationRunning);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index c01137a..8e0ec284 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
-
 import static java.lang.Float.isNaN;
 
 import android.animation.Animator;
@@ -55,11 +53,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
-import com.android.systemui.keyguard.shared.model.TransitionState;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -77,8 +71,6 @@
 
 import javax.inject.Inject;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-
 /**
  * Controls both the scrim behind the notifications and in front of the notifications (when a
  * security method gets shown).
@@ -259,28 +251,6 @@
     private boolean mWakeLockHeld;
     private boolean mKeyguardOccluded;
 
-    private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
-    private CoroutineDispatcher mMainDispatcher;
-    private boolean mIsBouncerToGoneTransitionStarted = false;
-    private boolean mIsBouncerToGoneTransitionRunning = false;
-    private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
-    private final Consumer<Float> mScrimAlphaConsumer =
-            (Float alpha) -> {
-                mScrimInFront.setViewAlpha(0f);
-                mNotificationsScrim.setViewAlpha(0f);
-                mScrimBehind.setViewAlpha(alpha);
-            };
-    final Consumer<TransitionStep> mPrimaryBouncerToGoneTransition =
-            (TransitionStep step) -> {
-                mIsBouncerToGoneTransitionRunning =
-                    step.getTransitionState() == TransitionState.RUNNING;
-                mIsBouncerToGoneTransitionStarted =
-                    step.getTransitionState() == TransitionState.STARTED;
-                if (mIsBouncerToGoneTransitionStarted) {
-                    transitionTo(ScrimState.UNLOCKED);
-                }
-            };
-
     @Inject
     public ScrimController(
             LightBarController lightBarController,
@@ -295,10 +265,7 @@
             @Main Executor mainExecutor,
             ScreenOffAnimationController screenOffAnimationController,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
-            KeyguardTransitionInteractor keyguardTransitionInteractor,
-            @Main CoroutineDispatcher mainDispatcher) {
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
         mScrimStateListener = lightBarController::setScrimState;
         mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
 
@@ -337,9 +304,6 @@
             }
         });
         mColors = new GradientColors();
-        mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
-        mKeyguardTransitionInteractor = keyguardTransitionInteractor;
-        mMainDispatcher = mainDispatcher;
     }
 
     /**
@@ -379,11 +343,6 @@
         for (ScrimState state : ScrimState.values()) {
             state.prepare(state);
         }
-
-        collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
-                mPrimaryBouncerToGoneTransition, mMainDispatcher);
-        collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
-                mScrimAlphaConsumer, mMainDispatcher);
     }
 
     /**
@@ -406,11 +365,6 @@
     }
 
     public void transitionTo(ScrimState state, Callback callback) {
-        if (mIsBouncerToGoneTransitionRunning) {
-            Log.i(TAG, "Skipping transition to: " + state
-                    + " while mIsBouncerToGoneTransitionRunning");
-            return;
-        }
         if (state == mState) {
             // Call the callback anyway, unless it's already enqueued
             if (callback != null && mCallback != callback) {
@@ -830,11 +784,10 @@
                         mBehindAlpha = 0;
                         mNotificationsAlpha = 0;
                     } else {
+                        // Behind scrim will finish fading in at 30% expansion.
                         float behindFraction = MathUtils
                                 .constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction);
-                        if (!mIsBouncerToGoneTransitionStarted) {
-                            mBehindAlpha = behindFraction * mDefaultScrimAlpha;
-                        }
+                        mBehindAlpha = behindFraction * mDefaultScrimAlpha;
                         // Delay fade-in of notification scrim a bit further, to coincide with the
                         // behind scrim finishing fading in.
                         // Also to coincide with the view starting to fade in, otherwise the empty
@@ -1172,9 +1125,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint",
                     Color.alpha(tint));
             scrimView.setTint(tint);
-            if (!mIsBouncerToGoneTransitionRunning) {
-                scrimView.setViewAlpha(alpha);
-            }
+            scrimView.setViewAlpha(alpha);
         } else {
             scrim.setAlpha(alpha);
         }
@@ -1522,9 +1473,6 @@
     }
 
     public void setKeyguardOccluded(boolean keyguardOccluded) {
-        if (mKeyguardOccluded == keyguardOccluded) {
-            return;
-        }
         mKeyguardOccluded = keyguardOccluded;
         updateScrims();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
index c04ea36..5903fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
@@ -26,19 +26,39 @@
 import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_OUT
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback
 import com.android.systemui.util.animation.AnimationUtil.Companion.frames
+import com.android.systemui.util.doOnCancel
+import com.android.systemui.util.doOnEnd
+
+/**
+ * An implementation of [StatusBarSystemEventDefaultAnimator], applying the onAlphaChanged and
+ * onTranslationXChanged callbacks directly to the provided animatedView.
+ */
+class StatusBarSystemEventAnimator @JvmOverloads constructor(
+        val animatedView: View,
+        resources: Resources,
+        isAnimationRunning: Boolean = false
+) : StatusBarSystemEventDefaultAnimator(
+        resources = resources,
+        onAlphaChanged = animatedView::setAlpha,
+        onTranslationXChanged = animatedView::setTranslationX,
+        isAnimationRunning = isAnimationRunning
+)
 
 /**
  * Tied directly to [SystemStatusAnimationScheduler]. Any StatusBar-like thing (keyguard, collapsed
- * status bar fragment), can just feed this an animatable view to get the default system status
- * animation.
+ * status bar fragment), can use this Animator to get the default system status animation. It simply
+ * needs to implement the onAlphaChanged and onTranslationXChanged callbacks.
  *
  * This animator relies on resources, and should be recreated whenever resources are updated. While
  * this class could be used directly as the animation callback, it's probably best to forward calls
  * to it so that it can be recreated at any moment without needing to remove/add callback.
  */
-class StatusBarSystemEventAnimator(
-    val animatedView: View,
-    resources: Resources
+
+open class StatusBarSystemEventDefaultAnimator @JvmOverloads constructor(
+        resources: Resources,
+        private val onAlphaChanged: (Float) -> Unit,
+        private val onTranslationXChanged: (Float) -> Unit,
+        var isAnimationRunning: Boolean = false
 ) : SystemStatusAnimationCallback {
     private val translationXIn: Int = resources.getDimensionPixelSize(
             R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x)
@@ -46,18 +66,19 @@
             R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x)
 
     override fun onSystemEventAnimationBegin(): Animator {
+        isAnimationRunning = true
         val moveOut = ValueAnimator.ofFloat(0f, 1f).apply {
             duration = 23.frames
             interpolator = STATUS_BAR_X_MOVE_OUT
             addUpdateListener {
-                animatedView.translationX = -(translationXIn * animatedValue as Float)
+                onTranslationXChanged(-(translationXIn * animatedValue as Float))
             }
         }
         val alphaOut = ValueAnimator.ofFloat(1f, 0f).apply {
             duration = 8.frames
             interpolator = null
             addUpdateListener {
-                animatedView.alpha = animatedValue as Float
+                onAlphaChanged(animatedValue as Float)
             }
         }
 
@@ -67,13 +88,13 @@
     }
 
     override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
-        animatedView.translationX = translationXOut.toFloat()
+        onTranslationXChanged(translationXOut.toFloat())
         val moveIn = ValueAnimator.ofFloat(1f, 0f).apply {
             duration = 23.frames
             startDelay = 7.frames
             interpolator = STATUS_BAR_X_MOVE_IN
             addUpdateListener {
-                animatedView.translationX = translationXOut * animatedValue as Float
+                onTranslationXChanged(translationXOut * animatedValue as Float)
             }
         }
         val alphaIn = ValueAnimator.ofFloat(0f, 1f).apply {
@@ -81,13 +102,14 @@
             startDelay = 11.frames
             interpolator = null
             addUpdateListener {
-                animatedView.alpha = animatedValue as Float
+                onAlphaChanged(animatedValue as Float)
             }
         }
 
         val animatorSet = AnimatorSet()
         animatorSet.playTogether(moveIn, alphaIn)
-
+        animatorSet.doOnEnd { isAnimationRunning = false }
+        animatorSet.doOnCancel { isAnimationRunning = false }
         return animatorSet
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
index d5d3efd..3a7ac9c 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
@@ -31,6 +31,9 @@
 import com.android.internal.app.AlertActivity;
 import com.android.internal.app.AlertController;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import javax.inject.Inject;
 
 /**
  * If the attached USB accessory has a URL associated with it, and that URL is valid,
@@ -46,13 +49,27 @@
     private UsbAccessory mAccessory;
     private Uri mUri;
 
+    private final DeviceProvisionedController mDeviceProvisionedController;
+
+    @Inject
+    UsbAccessoryUriActivity(DeviceProvisionedController deviceProvisionedController) {
+        mDeviceProvisionedController = deviceProvisionedController;
+    }
+
     @Override
     public void onCreate(Bundle icicle) {
-       getWindow().addSystemFlags(
+        getWindow().addSystemFlags(
                 WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-       super.onCreate(icicle);
+        super.onCreate(icicle);
 
-       Intent intent = getIntent();
+        // Don't show this dialog during Setup Wizard
+        if (!mDeviceProvisionedController.isDeviceProvisioned()) {
+            Log.e(TAG, "device not provisioned");
+            finish();
+            return;
+        }
+
+        Intent intent = getIntent();
         mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
         String uriString = intent.getStringExtra("uri");
         mUri = (uriString == null ? null : Uri.parse(uriString));
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 81ae6e8..c72853e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -115,6 +115,17 @@
     }
 
     /**
+     * Provide a Long running Executor.
+     */
+    @Provides
+    @SysUISingleton
+    @LongRunning
+    public static DelayableExecutor provideLongRunningDelayableExecutor(
+            @LongRunning Looper looper) {
+        return new ExecutorImpl(looper);
+    }
+
+    /**
      * Provide a Background-Thread Executor.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 8b925b7..b962148 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -35,7 +35,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.LongRunning;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -61,17 +61,16 @@
     private final UserTracker mUserTracker;
 
     // used for most tasks (call canvas.drawBitmap, load/unload the bitmap)
-    @Background
-    private final DelayableExecutor mBackgroundExecutor;
+    @LongRunning
+    private final DelayableExecutor mLongExecutor;
 
     // wait at least this duration before unloading the bitmap
     private static final int DELAY_UNLOAD_BITMAP = 2000;
 
     @Inject
-    public ImageWallpaper(@Background DelayableExecutor backgroundExecutor,
-            UserTracker userTracker) {
+    public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker) {
         super();
-        mBackgroundExecutor = backgroundExecutor;
+        mLongExecutor = longExecutor;
         mUserTracker = userTracker;
     }
 
@@ -105,7 +104,7 @@
             setFixedSizeAllowed(true);
             setShowForAllUsers(true);
             mWallpaperLocalColorExtractor = new WallpaperLocalColorExtractor(
-                    mBackgroundExecutor,
+                    mLongExecutor,
                     new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
                         @Override
                         public void onColorsProcessed(List<RectF> regions,
@@ -202,7 +201,7 @@
         }
 
         private void drawFrame() {
-            mBackgroundExecutor.execute(this::drawFrameSynchronized);
+            mLongExecutor.execute(this::drawFrameSynchronized);
         }
 
         private void drawFrameSynchronized() {
@@ -257,7 +256,7 @@
         }
 
         private void unloadBitmapIfNotUsed() {
-            mBackgroundExecutor.execute(this::unloadBitmapIfNotUsedSynchronized);
+            mLongExecutor.execute(this::unloadBitmapIfNotUsedSynchronized);
         }
 
         private void unloadBitmapIfNotUsedSynchronized() {
@@ -341,7 +340,7 @@
                  *   - the mini bitmap from color extractor is recomputed
                  *   - the DELAY_UNLOAD_BITMAP has passed
                  */
-                mBackgroundExecutor.executeDelayed(
+                mLongExecutor.executeDelayed(
                         this::unloadBitmapIfNotUsedSynchronized, DELAY_UNLOAD_BITMAP);
             }
             // even if the bitmap cannot be loaded, call reportEngineShown
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
index 988fd71..1e8446f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
@@ -29,7 +29,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.LongRunning;
 import com.android.systemui.util.Assert;
 
 import java.io.FileDescriptor;
@@ -66,8 +66,8 @@
     private final List<RectF> mPendingRegions = new ArrayList<>();
     private final Set<RectF> mProcessedRegions = new ArraySet<>();
 
-    @Background
-    private final Executor mBackgroundExecutor;
+    @LongRunning
+    private final Executor mLongExecutor;
 
     private final WallpaperLocalColorExtractorCallback mWallpaperLocalColorExtractorCallback;
 
@@ -101,13 +101,13 @@
 
     /**
      * Creates a new color extractor.
-     * @param backgroundExecutor the executor on which the color extraction will be performed
+     * @param longExecutor the executor on which the color extraction will be performed
      * @param wallpaperLocalColorExtractorCallback an interface to handle the callbacks from
      *                                        the color extractor.
      */
-    public WallpaperLocalColorExtractor(@Background Executor backgroundExecutor,
+    public WallpaperLocalColorExtractor(@LongRunning Executor longExecutor,
             WallpaperLocalColorExtractorCallback wallpaperLocalColorExtractorCallback) {
-        mBackgroundExecutor = backgroundExecutor;
+        mLongExecutor = longExecutor;
         mWallpaperLocalColorExtractorCallback = wallpaperLocalColorExtractorCallback;
     }
 
@@ -117,7 +117,7 @@
      * not recomputed.
      */
     public void setDisplayDimensions(int displayWidth, int displayHeight) {
-        mBackgroundExecutor.execute(() ->
+        mLongExecutor.execute(() ->
                 setDisplayDimensionsSynchronized(displayWidth, displayHeight));
     }
 
@@ -144,7 +144,7 @@
      * @param bitmap the new wallpaper
      */
     public void onBitmapChanged(@NonNull Bitmap bitmap) {
-        mBackgroundExecutor.execute(() -> onBitmapChangedSynchronized(bitmap));
+        mLongExecutor.execute(() -> onBitmapChangedSynchronized(bitmap));
     }
 
     private void onBitmapChangedSynchronized(@NonNull Bitmap bitmap) {
@@ -167,7 +167,7 @@
      * @param pages the total number of pages of the launcher
      */
     public void onPageChanged(int pages) {
-        mBackgroundExecutor.execute(() -> onPageChangedSynchronized(pages));
+        mLongExecutor.execute(() -> onPageChangedSynchronized(pages));
     }
 
     private void onPageChangedSynchronized(int pages) {
@@ -194,7 +194,7 @@
      */
     public void addLocalColorsAreas(@NonNull List<RectF> regions) {
         if (regions.size() > 0) {
-            mBackgroundExecutor.execute(() -> addLocalColorsAreasSynchronized(regions));
+            mLongExecutor.execute(() -> addLocalColorsAreasSynchronized(regions));
         } else {
             Log.w(TAG, "Attempt to add colors with an empty list");
         }
@@ -218,7 +218,7 @@
      * @param regions The areas of interest in our wallpaper (in screen pixel coordinates)
      */
     public void removeLocalColorAreas(@NonNull List<RectF> regions) {
-        mBackgroundExecutor.execute(() -> removeLocalColorAreasSynchronized(regions));
+        mLongExecutor.execute(() -> removeLocalColorAreasSynchronized(regions));
     }
 
     private void removeLocalColorAreasSynchronized(@NonNull List<RectF> regions) {
@@ -236,7 +236,7 @@
      * Clean up the memory (in particular, the mini bitmap) used by this class.
      */
     public void cleanUp() {
-        mBackgroundExecutor.execute(this::cleanUpSynchronized);
+        mLongExecutor.execute(this::cleanUpSynchronized);
     }
 
     private void cleanUpSynchronized() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 1bbc199..531006d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -37,6 +37,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -160,6 +161,29 @@
     }
 
     @Test
+    public void testOnApplyWindowInsets_disappearAnimation_paddingNotSet() {
+        int paddingBottom = getContext().getResources()
+                .getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
+        int imeInsetAmount = paddingBottom + 1;
+        int systemBarInsetAmount = 0;
+        initMode(MODE_DEFAULT);
+
+        Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
+        Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
+
+        WindowInsets insets = new WindowInsets.Builder()
+                .setInsets(ime(), imeInset)
+                .setInsetsIgnoringVisibility(systemBars(), systemBarInset)
+                .build();
+
+        ensureViewFlipperIsMocked();
+        mKeyguardSecurityContainer.startDisappearAnimation(
+                KeyguardSecurityModel.SecurityMode.Password);
+        mKeyguardSecurityContainer.onApplyWindowInsets(insets);
+        assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isNotEqualTo(imeInsetAmount);
+    }
+
+    @Test
     public void testDefaultViewMode() {
         initMode(MODE_ONE_HANDED);
         initMode(MODE_DEFAULT);
@@ -376,6 +400,17 @@
         assertThat(mKeyguardSecurityContainer.getScaleY()).isEqualTo(1);
     }
 
+    @Test
+    public void testDisappearAnimationPassword() {
+        ensureViewFlipperIsMocked();
+        KeyguardPasswordView keyguardPasswordView = mock(KeyguardPasswordView.class);
+        when(mSecurityViewFlipper.getSecurityView()).thenReturn(keyguardPasswordView);
+
+        mKeyguardSecurityContainer
+                .startDisappearAnimation(KeyguardSecurityModel.SecurityMode.Password);
+        verify(keyguardPasswordView).setDisappearAnimationListener(any());
+    }
+
     private BackEvent createBackEvent(float touchX, float progress) {
         return new BackEvent(0, 0, progress, BackEvent.EDGE_LEFT);
     }
@@ -446,4 +481,12 @@
                 mUserSwitcherController, () -> {
                 }, mFalsingA11yDelegate);
     }
+
+    private void ensureViewFlipperIsMocked() {
+        mSecurityViewFlipper = mock(KeyguardSecurityViewFlipper.class);
+        KeyguardPasswordView keyguardPasswordView = mock(KeyguardPasswordView.class);
+        when(mSecurityViewFlipper.getSecurityView()).thenReturn(keyguardPasswordView);
+        mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 2099281..c2fb904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -446,7 +446,7 @@
         mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
         when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString()))
                 .thenReturn(true);
-        when(mClipboardUtils.getAction(any(CharSequence.class), any(TextLinks.class), anyString()))
+        when(mClipboardUtils.getAction(any(TextLinks.class), anyString()))
                 .thenReturn(Optional.of(Mockito.mock(RemoteAction.class)));
         when(mClipboardOverlayView.post(any(Runnable.class))).thenAnswer(new Answer<Object>() {
             @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
index aea6be3..3d8f04e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.when;
 
@@ -77,6 +78,74 @@
 
     @Test
     public void test_getAction_noLinks_returnsEmptyOptional() {
+        Optional<RemoteAction> action =
+                mClipboardUtils.getAction(Mockito.mock(TextLinks.class), "abc");
+
+        assertTrue(action.isEmpty());
+    }
+
+    @Test
+    public void test_getAction_returnsFirstLink() {
+        TextLinks links = getFakeTextLinksBuilder().build();
+        RemoteAction actionA = constructRemoteAction("abc");
+        RemoteAction actionB = constructRemoteAction("def");
+        TextClassification classificationA = Mockito.mock(TextClassification.class);
+        when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA));
+        TextClassification classificationB = Mockito.mock(TextClassification.class);
+        when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB));
+        when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn(
+                classificationA, classificationB);
+
+        RemoteAction result = mClipboardUtils.getAction(links, "test").orElse(null);
+
+        assertEquals(actionA, result);
+    }
+
+    @Test
+    public void test_getAction_skipsMatchingComponent() {
+        TextLinks links = getFakeTextLinksBuilder().build();
+        RemoteAction actionA = constructRemoteAction("abc");
+        RemoteAction actionB = constructRemoteAction("def");
+        TextClassification classificationA = Mockito.mock(TextClassification.class);
+        when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA));
+        TextClassification classificationB = Mockito.mock(TextClassification.class);
+        when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB));
+        when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn(
+                classificationA, classificationB);
+
+        RemoteAction result = mClipboardUtils.getAction(links, "abc").orElse(null);
+
+        assertEquals(actionB, result);
+    }
+
+    @Test
+    public void test_getAction_skipsShortEntity() {
+        TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22");
+        final Map<String, Float> scores = new ArrayMap<>();
+        scores.put(TextClassifier.TYPE_EMAIL, 1f);
+        textLinks.addLink(20, 22, scores);
+        textLinks.addLink(0, 22, scores);
+
+        RemoteAction actionA = constructRemoteAction("abc");
+        RemoteAction actionB = constructRemoteAction("def");
+        TextClassification classificationA = Mockito.mock(TextClassification.class);
+        when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA));
+        TextClassification classificationB = Mockito.mock(TextClassification.class);
+        when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB));
+        when(mTextClassifier.classifyText(anyString(), eq(20), eq(22), isNull())).thenReturn(
+                classificationA);
+        when(mTextClassifier.classifyText(anyString(), eq(0), eq(22), isNull())).thenReturn(
+                classificationB);
+
+        RemoteAction result = mClipboardUtils.getAction(textLinks.build(), "test").orElse(null);
+
+        assertEquals(actionB, result);
+    }
+
+    // TODO(b/267162944): Next four tests (marked "legacy") are obsolete once
+    //  CLIPBOARD_MINIMIZED_LAYOUT flag is released and removed
+    @Test
+    public void test_getAction_noLinks_returnsEmptyOptional_legacy() {
         ClipData.Item item = new ClipData.Item("no text links");
         item.setTextLinks(Mockito.mock(TextLinks.class));
 
@@ -86,8 +155,8 @@
     }
 
     @Test
-    public void test_getAction_returnsFirstLink() {
-        when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinks());
+    public void test_getAction_returnsFirstLink_legacy() {
+        when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinksBuilder().build());
         when(mClipDataItem.getText()).thenReturn("");
         RemoteAction actionA = constructRemoteAction("abc");
         RemoteAction actionB = constructRemoteAction("def");
@@ -98,14 +167,14 @@
         when(mTextClassifier.classifyText(anyString(), anyInt(), anyInt(), isNull())).thenReturn(
                 classificationA, classificationB);
 
-        RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "def").orElse(null);
+        RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "test").orElse(null);
 
         assertEquals(actionA, result);
     }
 
     @Test
-    public void test_getAction_skipsMatchingComponent() {
-        when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinks());
+    public void test_getAction_skipsMatchingComponent_legacy() {
+        when(mClipDataItem.getTextLinks()).thenReturn(getFakeTextLinksBuilder().build());
         when(mClipDataItem.getText()).thenReturn("");
         RemoteAction actionA = constructRemoteAction("abc");
         RemoteAction actionB = constructRemoteAction("def");
@@ -122,6 +191,33 @@
     }
 
     @Test
+    public void test_getAction_skipsShortEntity_legacy() {
+        TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22");
+        final Map<String, Float> scores = new ArrayMap<>();
+        scores.put(TextClassifier.TYPE_EMAIL, 1f);
+        textLinks.addLink(20, 22, scores);
+        textLinks.addLink(0, 22, scores);
+
+        when(mClipDataItem.getTextLinks()).thenReturn(textLinks.build());
+        when(mClipDataItem.getText()).thenReturn(textLinks.build().getText());
+
+        RemoteAction actionA = constructRemoteAction("abc");
+        RemoteAction actionB = constructRemoteAction("def");
+        TextClassification classificationA = Mockito.mock(TextClassification.class);
+        when(classificationA.getActions()).thenReturn(Lists.newArrayList(actionA));
+        TextClassification classificationB = Mockito.mock(TextClassification.class);
+        when(classificationB.getActions()).thenReturn(Lists.newArrayList(actionB));
+        when(mTextClassifier.classifyText(anyString(), eq(20), eq(22), isNull())).thenReturn(
+                classificationA);
+        when(mTextClassifier.classifyText(anyString(), eq(0), eq(22), isNull())).thenReturn(
+                classificationB);
+
+        RemoteAction result = mClipboardUtils.getAction(mClipDataItem, "test").orElse(null);
+
+        assertEquals(actionB, result);
+    }
+
+    @Test
     public void test_extra_withPackage_returnsTrue() {
         PersistableBundle b = new PersistableBundle();
         b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true);
@@ -184,12 +280,12 @@
         return action;
     }
 
-    private static TextLinks getFakeTextLinks() {
-        TextLinks.Builder textLinks = new TextLinks.Builder("test");
+    private static TextLinks.Builder getFakeTextLinksBuilder() {
+        TextLinks.Builder textLinks = new TextLinks.Builder("test text of length 22");
         final Map<String, Float> scores = new ArrayMap<>();
         scores.put(TextClassifier.TYPE_EMAIL, 1f);
-        textLinks.addLink(0, 0, scores);
-        textLinks.addLink(0, 0, scores);
-        return textLinks.build();
+        textLinks.addLink(0, 22, scores);
+        textLinks.addLink(0, 22, scores);
+        return textLinks;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 3a168d4..d6dbd73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -450,6 +450,15 @@
         swipeToPosition(0f, Direction.DOWN, 0);
     }
 
+    @Test
+    public void testTouchSessionOnRemovedCalledTwice() {
+        mTouchHandler.onSessionStart(mTouchSession);
+        ArgumentCaptor<DreamTouchHandler.TouchSession.Callback> onRemovedCallbackCaptor =
+                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.Callback.class);
+        verify(mTouchSession).registerCallback(onRemovedCallbackCaptor.capture());
+        onRemovedCallbackCaptor.getValue().onRemoved();
+        onRemovedCallbackCaptor.getValue().onRemoved();
+    }
 
     private void swipeToPosition(float percent, Direction direction, float velocityY) {
         Mockito.clearInvocations(mTouchSession);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
new file mode 100644
index 0000000..ec94cde
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.keyboard.backlight.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyboardBacklightInteractorTest : SysuiTestCase() {
+
+    private val keyboardRepository = FakeKeyboardRepository()
+    private lateinit var underTest: KeyboardBacklightInteractor
+
+    @Before
+    fun setUp() {
+        underTest = KeyboardBacklightInteractor(keyboardRepository)
+    }
+
+    @Test
+    fun emitsNull_whenKeyboardJustConnected() = runTest {
+        val latest by collectLastValue(underTest.backlight)
+        keyboardRepository.setKeyboardConnected(true)
+
+        assertThat(latest).isNull()
+    }
+
+    @Test
+    fun emitsBacklight_whenKeyboardConnectedAndBacklightChanged() = runTest {
+        keyboardRepository.setKeyboardConnected(true)
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+        assertThat(underTest.backlight.first()).isEqualTo(BacklightModel(1, 5))
+    }
+
+    @Test
+    fun emitsNull_afterKeyboardDisconnecting() = runTest {
+        val latest by collectLastValue(underTest.backlight)
+        keyboardRepository.setKeyboardConnected(true)
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+        keyboardRepository.setKeyboardConnected(false)
+
+        assertThat(latest).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
new file mode 100644
index 0000000..ec05d10
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.keyboard.backlight.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class BacklightDialogViewModelTest : SysuiTestCase() {
+
+    private val keyboardRepository = FakeKeyboardRepository()
+    private lateinit var underTest: BacklightDialogViewModel
+    @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
+    private val timeoutMillis = 3000L
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any()))
+            .thenReturn(timeoutMillis.toInt())
+        underTest =
+            BacklightDialogViewModel(
+                KeyboardBacklightInteractor(keyboardRepository),
+                accessibilityManagerWrapper
+            )
+        keyboardRepository.setKeyboardConnected(true)
+    }
+
+    @Test
+    fun emitsViewModel_whenBacklightChanged() = runTest {
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+        assertThat(underTest.dialogContent.first()).isEqualTo(BacklightDialogContentViewModel(1, 5))
+    }
+
+    @Test
+    fun emitsNull_afterTimeout() = runTest {
+        val latest by collectLastValue(underTest.dialogContent)
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
+        advanceTimeBy(timeoutMillis + 1)
+        assertThat(latest).isNull()
+    }
+
+    @Test
+    fun emitsNull_after5secDelay_fromLastBacklightChange() = runTest {
+        val latest by collectLastValue(underTest.dialogContent)
+        keyboardRepository.setKeyboardConnected(true)
+
+        keyboardRepository.setBacklight(BacklightModel(1, 5))
+        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
+
+        advanceTimeBy(timeoutMillis * 2 / 3)
+        // timeout yet to pass, no new emission
+        keyboardRepository.setBacklight(BacklightModel(2, 5))
+        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
+
+        advanceTimeBy(timeoutMillis * 2 / 3)
+        // timeout refreshed because of last `setBacklight`, still content present
+        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
+
+        advanceTimeBy(timeoutMillis * 2 / 3)
+        // finally timeout reached and null emitted
+        assertThat(latest).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 0e6f8d4..0469e77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -219,29 +219,6 @@
         }
 
     @Test
-    fun isKeyguardUnlocked() =
-        runTest(UnconfinedTestDispatcher()) {
-            whenever(keyguardStateController.isUnlocked).thenReturn(false)
-            var latest: Boolean? = null
-            val job = underTest.isKeyguardUnlocked.onEach { latest = it }.launchIn(this)
-
-            assertThat(latest).isFalse()
-
-            val captor = argumentCaptor<KeyguardStateController.Callback>()
-            verify(keyguardStateController).addCallback(captor.capture())
-
-            whenever(keyguardStateController.isUnlocked).thenReturn(true)
-            captor.value.onUnlockedChanged()
-            assertThat(latest).isTrue()
-
-            whenever(keyguardStateController.isUnlocked).thenReturn(false)
-            captor.value.onUnlockedChanged()
-            assertThat(latest).isFalse()
-
-            job.cancel()
-        }
-
-    @Test
     fun isDozing() =
         runTest(UnconfinedTestDispatcher()) {
             var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index fe9098f..ae7a928 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -19,8 +19,6 @@
 import android.animation.ValueAnimator
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.flags.FakeFeatureFlags
@@ -42,7 +40,6 @@
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.cancelChildren
@@ -54,8 +51,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
@@ -82,7 +77,6 @@
     // Used to verify transition requests for test output
     @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository
     @Mock private lateinit var commandQueue: CommandQueue
-    @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
 
     private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
     private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
@@ -108,8 +102,6 @@
         transitionRepository = KeyguardTransitionRepositoryImpl()
         runner = KeyguardTransitionRunner(transitionRepository)
 
-        whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
-
         val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
         fromLockscreenTransitionInteractor =
             FromLockscreenTransitionInteractor(
@@ -181,17 +173,16 @@
                 keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
-                keyguardSecurityModel = keyguardSecurityModel,
             )
         fromPrimaryBouncerTransitionInteractor.start()
     }
 
     @Test
-    fun `DREAMING to LOCKSCREEN`() =
+    fun `DREAMING to LOCKSCREEN - dreaming state changes first`() =
         testScope.runTest {
-            // GIVEN a device is dreaming
+            // GIVEN a device is dreaming and occluded
             keyguardRepository.setDreamingWithOverlay(true)
-            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setKeyguardOccluded(true)
             runCurrent()
 
             // GIVEN a prior transition has run to DREAMING
@@ -224,7 +215,56 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
+                }
+            // THEN a transition to BOUNCER should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `DREAMING to LOCKSCREEN - occluded state changes first`() =
+        testScope.runTest {
+            // GIVEN a device is dreaming and occluded
+            keyguardRepository.setDreamingWithOverlay(true)
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.DREAMING,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN doze is complete
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            // AND occluded has stopped
+            keyguardRepository.setKeyguardOccluded(false)
+            advanceUntilIdle()
+            // AND then dreaming has stopped
+            keyguardRepository.setDreamingWithOverlay(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to BOUNCER should occur
             assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
@@ -264,7 +304,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to PRIMARY_BOUNCER should occur
             assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -305,7 +345,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -346,7 +386,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -387,7 +427,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -428,7 +468,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -465,7 +505,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -502,7 +542,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -543,7 +583,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -584,7 +624,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to AOD should occur
             assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -621,7 +661,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to AOD should occur
             assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -637,7 +677,6 @@
         testScope.runTest {
             // GIVEN a device that is not dreaming or dozing
             keyguardRepository.setDreamingWithOverlay(false)
-            keyguardRepository.setWakefulnessModel(startingToWake())
             keyguardRepository.setDozeTransitionModel(
                 DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
             )
@@ -665,7 +704,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DREAMING should occur
             assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -702,7 +741,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to PRIMARY_BOUNCER should occur
             assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -745,7 +784,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to AOD should occur
             assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -789,7 +828,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -831,7 +870,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to LOCKSCREEN should occur
             assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -873,7 +912,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to AOD should occur
             assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -915,7 +954,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -956,7 +995,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                    verify(mockTransitionRepository).startTransition(capture())
                 }
             // THEN a transition to LOCKSCREEN should occur
             assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 52b0b6a..4f469f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -551,6 +551,7 @@
                 mNotificationStackSizeCalculator,
                 mUnlockedScreenOffAnimationController,
                 mShadeTransitionController,
+                mInteractionJankMonitor,
                 systemClock,
                 mKeyguardBottomAreaViewModel,
                 mKeyguardBottomAreaInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 82a5743..0a401b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.NotificationInsetsController
@@ -66,32 +65,48 @@
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper(setAsMainLooper = true)
 class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
-    @Mock private lateinit var view: NotificationShadeWindowView
-    @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
-    @Mock private lateinit var centralSurfaces: CentralSurfaces
-    @Mock private lateinit var dockManager: DockManager
-    @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
-    @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
-    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
-    @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
-    @Mock private lateinit var ambientState: AmbientState
-    @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
-    @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
-    @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
-    @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
+    @Mock
+    private lateinit var view: NotificationShadeWindowView
+    @Mock
+    private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
+    @Mock
+    private lateinit var centralSurfaces: CentralSurfaces
+    @Mock
+    private lateinit var dockManager: DockManager
+    @Mock
+    private lateinit var notificationPanelViewController: NotificationPanelViewController
+    @Mock
+    private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+    @Mock
+    private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock
+    private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+    @Mock
+    private lateinit var ambientState: AmbientState
+    @Mock
+    private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
+    @Mock
+    private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
+    @Mock
+    private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+    @Mock
+    private lateinit var statusBarWindowStateController: StatusBarWindowStateController
     @Mock
     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
-    @Mock private lateinit var lockIconViewController: LockIconViewController
-    @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
-    @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
-    @Mock private lateinit var notificationInsetsController: NotificationInsetsController
-    @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+    @Mock
+    private lateinit var lockIconViewController: LockIconViewController
+    @Mock
+    private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
+    @Mock
+    private lateinit var pulsingGestureListener: PulsingGestureListener
+    @Mock
+    private lateinit var notificationInsetsController: NotificationInsetsController
+    @Mock
+    private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
     @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
     @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
     @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
-    @Mock
-    lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
 
     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
     private lateinit var interactionEventHandler: InteractionEventHandler
@@ -103,44 +118,43 @@
         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.lockscreenToDreamingTransition)
-            .thenReturn(emptyFlow<TransitionStep>())
-        underTest =
-            NotificationShadeWindowViewController(
-                lockscreenShadeTransitionController,
-                FalsingCollectorFake(),
-                sysuiStatusBarStateController,
-                dockManager,
-                notificationShadeDepthController,
-                view,
-                notificationPanelViewController,
-                ShadeExpansionStateManager(),
-                stackScrollLayoutController,
-                statusBarKeyguardViewManager,
-                statusBarWindowStateController,
-                lockIconViewController,
-                centralSurfaces,
-                notificationShadeWindowController,
-                keyguardUnlockAnimationController,
-                notificationInsetsController,
-                ambientState,
-                pulsingGestureListener,
-                keyguardBouncerViewModel,
-                keyguardBouncerComponentFactory,
-                alternateBouncerInteractor,
-                keyguardTransitionInteractor,
-                primaryBouncerToGoneTransitionViewModel,
-            )
+                .thenReturn(emptyFlow<TransitionStep>())
+        underTest = NotificationShadeWindowViewController(
+            lockscreenShadeTransitionController,
+            FalsingCollectorFake(),
+            sysuiStatusBarStateController,
+            dockManager,
+            notificationShadeDepthController,
+            view,
+            notificationPanelViewController,
+            ShadeExpansionStateManager(),
+            stackScrollLayoutController,
+            statusBarKeyguardViewManager,
+            statusBarWindowStateController,
+            lockIconViewController,
+            centralSurfaces,
+            notificationShadeWindowController,
+            keyguardUnlockAnimationController,
+            notificationInsetsController,
+            ambientState,
+            pulsingGestureListener,
+            keyguardBouncerViewModel,
+            keyguardBouncerComponentFactory,
+            alternateBouncerInteractor,
+            keyguardTransitionInteractor,
+        )
         underTest.setupExpandedStatusBar()
 
-        interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
+        interactionEventHandlerCaptor =
+            ArgumentCaptor.forClass(InteractionEventHandler::class.java)
         verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
-        interactionEventHandler = interactionEventHandlerCaptor.value
+            interactionEventHandler = interactionEventHandlerCaptor.value
     }
 
     // Note: So far, these tests only cover interactions with the status bar view controller. More
@@ -170,11 +184,14 @@
     @Test
     fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
         underTest.setStatusBarViewController(phoneStatusBarViewController)
-        val downEvBelow =
-            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
+        val downEvBelow = MotionEvent.obtain(
+            0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0
+        )
         interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
 
-        val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
+        val nextEvent = MotionEvent.obtain(
+            0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
+        )
         whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
 
         val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index faa6221..5d71979 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -46,7 +46,6 @@
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationInsetsController;
@@ -102,7 +101,6 @@
     @Mock private NotificationInsetsController mNotificationInsetsController;
     @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
     @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
-    @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
 
     @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
             mInteractionEventHandlerCaptor;
@@ -152,8 +150,7 @@
                 mKeyguardBouncerViewModel,
                 mKeyguardBouncerComponentFactory,
                 mAlternateBouncerInteractor,
-                mKeyguardTransitionInteractor,
-                mPrimaryBouncerToGoneTransitionViewModel
+                mKeyguardTransitionInteractor
         );
         mController.setupExpandedStatusBar();
         mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 180d9f8..dc5a047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -24,8 +24,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -60,12 +58,7 @@
 import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
-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.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.statusbar.policy.FakeConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -92,10 +85,8 @@
 import java.util.HashSet;
 import java.util.Map;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
 @SmallTest
 public class ScrimControllerTest extends SysuiTestCase {
 
@@ -124,10 +115,6 @@
     @Mock private DockManager mDockManager;
     @Mock private ScreenOffAnimationController mScreenOffAnimationController;
     @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
-    @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
-    @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
-    @Mock private CoroutineDispatcher mMainDispatcher;
-
     // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
     //   event-dispatch-on-registration pattern caused some of these unit tests to fail.)
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -238,20 +225,13 @@
         when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
         when(mDockManager.isDocked()).thenReturn(false);
 
-        when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
-                .thenReturn(emptyFlow());
-        when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha()).thenReturn(emptyFlow());
-
         mScrimController = new ScrimController(mLightBarController,
                 mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
                 new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
                 mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
                 mScreenOffAnimationController,
                 mKeyguardUnlockAnimationController,
-                mStatusBarKeyguardViewManager,
-                mPrimaryBouncerToGoneTransitionViewModel,
-                mKeyguardTransitionInteractor,
-                mMainDispatcher);
+                mStatusBarKeyguardViewManager);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
@@ -881,10 +861,7 @@
                 mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
                 mScreenOffAnimationController,
                 mKeyguardUnlockAnimationController,
-                mStatusBarKeyguardViewManager,
-                mPrimaryBouncerToGoneTransitionViewModel,
-                mKeyguardTransitionInteractor,
-                mMainDispatcher);
+                mStatusBarKeyguardViewManager);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
@@ -1652,18 +1629,6 @@
         assertScrimAlpha(mScrimBehind, 0);
     }
 
-    @Test
-    public void ignoreTransitionRequestWhileKeyguardTransitionRunning() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
-        mScrimController.mPrimaryBouncerToGoneTransition.accept(
-                new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
-                        TransitionState.RUNNING, "ScrimControllerTest"));
-
-        // This request should not happen
-        mScrimController.transitionTo(ScrimState.BOUNCER);
-        assertThat(mScrimController.getState()).isEqualTo(ScrimState.UNLOCKED);
-    }
-
     private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
         mScrimController.setRawPanelExpansionFraction(expansion);
         finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
index 85cfef7..fd368eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
@@ -16,22 +16,24 @@
 
 package com.android.systemui.unfold.updates
 
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.os.Looper
 import android.testing.AndroidTestingRunner
-import android.view.IRotationWatcher
-import android.view.IWindowManager
+import android.view.Display
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.os.FakeHandler
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
@@ -42,19 +44,23 @@
 
     private lateinit var rotationChangeProvider: RotationChangeProvider
 
-    @Mock lateinit var windowManagerInterface: IWindowManager
+    @Mock lateinit var displayManager: DisplayManager
     @Mock lateinit var listener: RotationListener
-    @Captor lateinit var rotationWatcher: ArgumentCaptor<IRotationWatcher>
-    private val fakeExecutor = FakeExecutor(FakeSystemClock())
+    @Mock lateinit var display: Display
+    @Captor lateinit var displayListener: ArgumentCaptor<DisplayManager.DisplayListener>
+    private val fakeHandler = FakeHandler(Looper.getMainLooper())
+
+    private lateinit var spyContext: Context
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        rotationChangeProvider =
-            RotationChangeProvider(windowManagerInterface, context, fakeExecutor)
+        spyContext = spy(context)
+        whenever(spyContext.display).thenReturn(display)
+        rotationChangeProvider = RotationChangeProvider(displayManager, spyContext, fakeHandler)
         rotationChangeProvider.addCallback(listener)
-        fakeExecutor.runAllReady()
-        verify(windowManagerInterface).watchRotation(rotationWatcher.capture(), anyInt())
+        fakeHandler.dispatchQueuedMessages()
+        verify(displayManager).registerDisplayListener(displayListener.capture(), any())
     }
 
     @Test
@@ -70,15 +76,16 @@
         verify(listener).onRotationChanged(42)
 
         rotationChangeProvider.removeCallback(listener)
-        fakeExecutor.runAllReady()
+        fakeHandler.dispatchQueuedMessages()
         sendRotationUpdate(43)
 
-        verify(windowManagerInterface).removeRotationWatcher(any())
+        verify(displayManager).unregisterDisplayListener(any())
         verifyNoMoreInteractions(listener)
     }
 
     private fun sendRotationUpdate(newRotation: Int) {
-        rotationWatcher.value.onRotationChanged(newRotation)
-        fakeExecutor.runAllReady()
+        whenever(display.rotation).thenReturn(newRotation)
+        displayListener.allValues.forEach { it.onDisplayChanged(display.displayId) }
+        fakeHandler.dispatchQueuedMessages()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 31cce4f..468c5a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -88,7 +88,7 @@
     @Mock
     private Bitmap mWallpaperBitmap;
     FakeSystemClock mFakeSystemClock = new FakeSystemClock();
-    FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
+    FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
 
     @Before
     public void setUp() throws Exception {
@@ -125,7 +125,7 @@
 
     @Test
     public void testBitmapWallpaper_normal() {
-        // Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
+        // Will use an image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
         // Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
         int bitmapSide = DISPLAY_WIDTH;
         testSurfaceHelper(
@@ -137,7 +137,7 @@
 
     @Test
     public void testBitmapWallpaper_low_resolution() {
-        // Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
+        // Will use an image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
         // Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
         testSurfaceHelper(LOW_BMP_WIDTH /* bitmapWidth */,
                 LOW_BMP_HEIGHT /* bitmapHeight */,
@@ -161,13 +161,13 @@
         ImageWallpaper.CanvasEngine spyEngine = getSpyEngine();
         spyEngine.onCreate(mSurfaceHolder);
         spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder);
-        assertThat(mFakeBackgroundExecutor.numPending()).isAtLeast(1);
+        assertThat(mFakeExecutor.numPending()).isAtLeast(1);
 
         int n = 0;
-        while (mFakeBackgroundExecutor.numPending() >= 1) {
+        while (mFakeExecutor.numPending() >= 1) {
             n++;
             assertThat(n).isAtMost(10);
-            mFakeBackgroundExecutor.runNextReady();
+            mFakeExecutor.runNextReady();
             mFakeSystemClock.advanceTime(1000);
         }
 
@@ -176,7 +176,7 @@
     }
 
     private ImageWallpaper createImageWallpaper() {
-        return new ImageWallpaper(mFakeBackgroundExecutor, mUserTracker) {
+        return new ImageWallpaper(mFakeExecutor, mUserTracker) {
             @Override
             public Engine onCreateEngine() {
                 return new CanvasEngine() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt
new file mode 100644
index 0000000..4e43546
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.keyboard.data.repository
+
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+
+class FakeKeyboardRepository : KeyboardRepository {
+
+    private val _keyboardConnected = MutableStateFlow(false)
+    override val keyboardConnected: Flow<Boolean> = _keyboardConnected
+
+    private val _backlightState: MutableStateFlow<BacklightModel?> = MutableStateFlow(null)
+    // filtering to make sure backlight doesn't have default initial value
+    override val backlight: Flow<BacklightModel> = _backlightState.filterNotNull()
+
+    fun setBacklight(state: BacklightModel) {
+        _backlightState.value = state
+    }
+
+    fun setKeyboardConnected(connected: Boolean) {
+        _keyboardConnected.value = connected
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 194ed02..1a371c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -47,9 +47,6 @@
     private val _isKeyguardShowing = MutableStateFlow(false)
     override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
 
-    private val _isKeyguardUnlocked = MutableStateFlow(false)
-    override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked
-
     private val _isKeyguardOccluded = MutableStateFlow(false)
     override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 16442bb..eac1bd1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -37,7 +37,7 @@
         _transitions.emit(step)
     }
 
-    override fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean): UUID? {
+    override fun startTransition(info: TransitionInfo): UUID? {
         return null
     }
 
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 180b611..2e0a946 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -35,6 +35,7 @@
     ],
     kotlincflags: ["-Xjvm-default=enable"],
     java_version: "1.8",
+    sdk_version: "current",
     min_sdk_version: "current",
     plugins: ["dagger2-compiler"],
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index 068347c..a079668 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -19,8 +19,8 @@
 import android.content.ContentResolver
 import android.content.Context
 import android.hardware.SensorManager
+import android.hardware.display.DisplayManager
 import android.os.Handler
-import android.view.IWindowManager
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
@@ -61,7 +61,7 @@
             @BindsInstance @UnfoldMain executor: Executor,
             @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
             @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
-            @BindsInstance windowManager: IWindowManager,
+            @BindsInstance displayManager: DisplayManager,
             @BindsInstance contentResolver: ContentResolver = context.contentResolver
         ): UnfoldSharedComponent
     }
@@ -84,8 +84,9 @@
             @BindsInstance context: Context,
             @BindsInstance config: UnfoldTransitionConfig,
             @BindsInstance @UnfoldMain executor: Executor,
+            @BindsInstance @UnfoldMain handler: Handler,
             @BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
-            @BindsInstance windowManager: IWindowManager,
+            @BindsInstance displayManager: DisplayManager,
             @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
         ): RemoteUnfoldSharedComponent
     }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 8eb79df..1839919 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -19,8 +19,8 @@
 
 import android.content.Context
 import android.hardware.SensorManager
+import android.hardware.display.DisplayManager
 import android.os.Handler
-import android.view.IWindowManager
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.updates.FoldProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
@@ -47,7 +47,7 @@
         mainExecutor: Executor,
         singleThreadBgExecutor: Executor,
         tracingTagPrefix: String,
-        windowManager: IWindowManager,
+        displayManager: DisplayManager,
 ): UnfoldSharedComponent =
         DaggerUnfoldSharedComponent.factory()
                 .create(
@@ -61,7 +61,7 @@
                         mainExecutor,
                         singleThreadBgExecutor,
                         tracingTagPrefix,
-                        windowManager,
+                        displayManager,
                 )
 
 /**
@@ -73,16 +73,18 @@
         context: Context,
         config: UnfoldTransitionConfig,
         mainExecutor: Executor,
+        mainHandler: Handler,
         singleThreadBgExecutor: Executor,
         tracingTagPrefix: String,
-        windowManager: IWindowManager,
+        displayManager: DisplayManager,
         ): RemoteUnfoldSharedComponent =
         DaggerRemoteUnfoldSharedComponent.factory()
                 .create(
                         context,
                         config,
                         mainExecutor,
+                        mainHandler,
                         singleThreadBgExecutor,
-                        windowManager,
+                        displayManager,
                         tracingTagPrefix,
                 )
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index d19b414..28e4936 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.unfold.progress
 
 import android.os.Trace
-import android.os.Trace.TRACE_TAG_APP
 import android.util.Log
 import androidx.dynamicanimation.animation.DynamicAnimation
 import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -110,7 +109,7 @@
 
         if (DEBUG) {
             Log.d(TAG, "onFoldUpdate = ${update.name()}")
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "fold_update", update)
+            Trace.setCounter("fold_update", update.toLong())
         }
     }
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 82fd225..d653fc7 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -119,7 +119,7 @@
                     "lastHingeAngle: $lastHingeAngle, " +
                     "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition"
             )
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "hinge_angle", angle.toInt())
+            Trace.setCounter( "hinge_angle", angle.toLong())
         }
 
         val currentDirection =
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index 0cf8224..ce8f1a1 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -17,36 +17,32 @@
 package com.android.systemui.unfold.updates
 
 import android.content.Context
+import android.hardware.display.DisplayManager
+import android.os.Handler
 import android.os.RemoteException
-import android.view.IRotationWatcher
-import android.view.IWindowManager
-import android.view.Surface.Rotation
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.util.CallbackController
-import java.util.concurrent.Executor
 import javax.inject.Inject
 
 /**
- * Allows to subscribe to rotation changes.
- *
- * This is needed as rotation updates from [IWindowManager] are received in a binder thread, while
- * most of the times we want them in the main one. Updates are provided for the display associated
+ * Allows to subscribe to rotation changes. Updates are provided for the display associated
  * to [context].
  */
 class RotationChangeProvider
 @Inject
 constructor(
-    private val windowManagerInterface: IWindowManager,
+    private val displayManager: DisplayManager,
     private val context: Context,
-    @UnfoldMain private val mainExecutor: Executor,
+    @UnfoldMain private val mainHandler: Handler,
 ) : CallbackController<RotationChangeProvider.RotationListener> {
 
     private val listeners = mutableListOf<RotationListener>()
 
-    private val rotationWatcher = RotationWatcher()
+    private val displayListener = RotationDisplayListener()
+    private var lastRotation: Int? = null
 
     override fun addCallback(listener: RotationListener) {
-        mainExecutor.execute {
+        mainHandler.post {
             if (listeners.isEmpty()) {
                 subscribeToRotation()
             }
@@ -55,17 +51,18 @@
     }
 
     override fun removeCallback(listener: RotationListener) {
-        mainExecutor.execute {
+        mainHandler.post {
             listeners -= listener
             if (listeners.isEmpty()) {
                 unsubscribeToRotation()
+                lastRotation = null
             }
         }
     }
 
     private fun subscribeToRotation() {
         try {
-            windowManagerInterface.watchRotation(rotationWatcher, context.displayId)
+            displayManager.registerDisplayListener(displayListener, mainHandler)
         } catch (e: RemoteException) {
             throw e.rethrowFromSystemServer()
         }
@@ -73,7 +70,7 @@
 
     private fun unsubscribeToRotation() {
         try {
-            windowManagerInterface.removeRotationWatcher(rotationWatcher)
+            displayManager.unregisterDisplayListener(displayListener)
         } catch (e: RemoteException) {
             throw e.rethrowFromSystemServer()
         }
@@ -82,12 +79,25 @@
     /** Gets notified of rotation changes. */
     fun interface RotationListener {
         /** Called once rotation changes. */
-        fun onRotationChanged(@Rotation newRotation: Int)
+        fun onRotationChanged(newRotation: Int)
     }
 
-    private inner class RotationWatcher : IRotationWatcher.Stub() {
-        override fun onRotationChanged(rotation: Int) {
-            mainExecutor.execute { listeners.forEach { it.onRotationChanged(rotation) } }
+    private inner class RotationDisplayListener : DisplayManager.DisplayListener {
+
+        override fun onDisplayChanged(displayId: Int) {
+            val display = context.display ?: return
+
+            if (displayId == display.displayId) {
+                val currentRotation = display.rotation
+                if (lastRotation == null || lastRotation != currentRotation) {
+                    listeners.forEach { it.onRotationChanged(currentRotation) }
+                    lastRotation = currentRotation
+                }
+            }
         }
+
+        override fun onDisplayAdded(displayId: Int) {}
+
+        override fun onDisplayRemoved(displayId: Int) {}
     }
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index 06ca153..ce5c5f9 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -79,10 +79,9 @@
     companion object {
         fun ContentResolver.areAnimationsEnabled(): Boolean {
             val animationScale =
-                Settings.Global.getStringForUser(
+                Settings.Global.getString(
                         this,
                         Settings.Global.ANIMATOR_DURATION_SCALE,
-                        this.userId
                     )
                     ?.toFloatOrNull()
                     ?: 1f
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d78fe86..f0dac260 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3831,8 +3831,20 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final int callingAppId = UserHandle.getAppId(callingUid);
 
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+        ProcessRecord proc;
+        synchronized (mPidsSelfLocked) {
+            proc = mPidsSelfLocked.get(callingPid);
+        }
+        final boolean hasKillAllPermission = PERMISSION_GRANTED == checkPermission(
+                android.Manifest.permission.FORCE_STOP_PACKAGES, callingPid, callingUid)
+                || UserHandle.isCore(callingUid)
+                || (proc != null && proc.info.isSystemApp());
+
+        userId = mUserController.handleIncomingUser(callingPid, callingUid,
                 userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
         final int[] userIds = mUserController.expandUserId(userId);
 
@@ -3847,7 +3859,7 @@
                                     targetUserId));
                 } catch (RemoteException e) {
                 }
-                if (appId == -1) {
+                if (appId == -1 || (!hasKillAllPermission && appId != callingAppId)) {
                     Slog.w(TAG, "Invalid packageName: " + packageName);
                     return;
                 }
@@ -3875,6 +3887,22 @@
             throw new SecurityException(msg);
         }
 
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+
+        ProcessRecord proc;
+        synchronized (mPidsSelfLocked) {
+            proc = mPidsSelfLocked.get(callingPid);
+        }
+        if (callingUid >= FIRST_APPLICATION_UID
+                && (proc == null || !proc.info.isSystemApp())) {
+            final String msg = "Permission Denial: killAllBackgroundProcesses() from pid="
+                    + callingPid + ", uid=" + callingUid + " is not allowed";
+            Slog.w(TAG, msg);
+            // Silently return to avoid existing apps from crashing.
+            return;
+        }
+
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -13089,12 +13117,17 @@
     public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
             String callerFeatureId, String receiverId, IIntentReceiver receiver,
             IntentFilter filter, String permission, int userId, int flags) {
+        enforceNotIsolatedCaller("registerReceiver");
+
         // Allow Sandbox process to register only unexported receivers.
-        if ((flags & Context.RECEIVER_NOT_EXPORTED) != 0) {
-            enforceNotIsolatedCaller("registerReceiver");
-        } else if (mSdkSandboxSettings.isBroadcastReceiverRestrictionsEnforced()) {
-            enforceNotIsolatedOrSdkSandboxCaller("registerReceiver");
+        boolean unexported = (flags & Context.RECEIVER_NOT_EXPORTED) != 0;
+        if (mSdkSandboxSettings.isBroadcastReceiverRestrictionsEnforced()
+                && Process.isSdkSandboxUid(Binder.getCallingUid())
+                && !unexported) {
+            throw new SecurityException("SDK sandbox process not allowed to call "
+                + "registerReceiver");
         }
+
         ArrayList<Intent> stickyIntents = null;
         ProcessRecord callerApp = null;
         final boolean visibleToInstantApps
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2f95716..207c10c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -21,7 +21,6 @@
 import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
 
 import android.annotation.NonNull;
-import android.app.AlarmManager;
 import android.app.StatsManager;
 import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -357,16 +356,6 @@
         mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
         mStats.setPowerProfileLocked(mPowerProfile);
-
-        final boolean resetOnUnplugHighBatteryLevel = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel);
-        final boolean resetOnUnplugAfterSignificantCharge = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
-        mStats.setBatteryStatsConfig(
-                new BatteryStatsImpl.BatteryStatsConfig.Builder()
-                        .setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
-                        .setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
-                        .build());
         mStats.startTrackingSystemServerCpuTime();
 
         if (BATTERY_USAGE_STORE_ENABLED) {
@@ -397,18 +386,6 @@
             Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
         }
 
-        final AlarmManager am = mContext.getSystemService(AlarmManager.class);
-        mHandler.post(() -> {
-            synchronized (mStats) {
-                mStats.setLongPlugInAlarmInterface(new AlarmInterface(am, () -> {
-                    synchronized (mStats) {
-                        if (mStats.isOnBattery()) return;
-                        mStats.maybeResetWhilePluggedInLocked();
-                    }
-                }));
-            }
-        });
-
         synchronized (mPowerStatsLock) {
             mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
             if (mPowerStatsInternal != null) {
@@ -2282,32 +2259,6 @@
         }
     }
 
-    final class AlarmInterface implements BatteryStatsImpl.AlarmInterface,
-            AlarmManager.OnAlarmListener {
-        private AlarmManager mAm;
-        private Runnable mOnAlarm;
-
-        AlarmInterface(AlarmManager am, Runnable onAlarm) {
-            mAm = am;
-            mOnAlarm = onAlarm;
-        }
-
-        @Override
-        public void schedule(long rtcTimeMs, long windowLengthMs) {
-            mAm.setWindow(AlarmManager.RTC, rtcTimeMs, windowLengthMs, TAG, this, mHandler);
-        }
-
-        @Override
-        public void cancel() {
-            mAm.cancel(this);
-        }
-
-        @Override
-        public void onAlarm() {
-            mOnAlarm.run();
-        }
-    }
-
     private static native int nativeWaitWakeup(ByteBuffer outBuffer);
 
     private void dumpHelp(PrintWriter pw) {
@@ -2494,8 +2445,7 @@
                 } else if ("--reset-all".equals(arg)) {
                     awaitCompletion();
                     synchronized (mStats) {
-                        mStats.resetAllStatsAndHistoryLocked(
-                                BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+                        mStats.resetAllStatsCmdLocked();
                         mBatteryUsageStatsStore.removeAllSnapshots();
                         pw.println("Battery stats and history reset.");
                         noOutput = true;
@@ -2503,8 +2453,7 @@
                 } else if ("--reset".equals(arg)) {
                     awaitCompletion();
                     synchronized (mStats) {
-                        mStats.resetAllStatsAndHistoryLocked(
-                                BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+                        mStats.resetAllStatsCmdLocked();
                         pw.println("Battery stats reset.");
                         noOutput = true;
                     }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 350aa6b..2a807b2 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -608,6 +608,9 @@
     /* package */ void updateRunningUserAndProfiles(int newActiveUserId) {
         synchronized (mLock) {
             if (mCurrentActiveUserId != newActiveUserId) {
+                Slog.i(TAG, TextUtils.formatSimple(
+                        "switchUser | user: %d", newActiveUserId));
+
                 mCurrentActiveUserId = newActiveUserId;
                 for (int i = 0; i < mUserRecords.size(); i++) {
                     int userId = mUserRecords.keyAt(i);
@@ -679,6 +682,10 @@
         userRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::notifyRouterRegistered,
                         userRecord.mHandler, routerRecord));
+
+        Slog.i(TAG, TextUtils.formatSimple(
+                "registerRouter2 | package: %s, uid: %d, pid: %d, router: %d",
+                packageName, uid, pid, routerRecord.mRouterId));
     }
 
     @GuardedBy("mLock")
@@ -689,6 +696,11 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "unregisterRouter2 | package: %s, router: %d",
+                routerRecord.mPackageName,
+                routerRecord.mRouterId));
+
         UserRecord userRecord = routerRecord.mUserRecord;
         userRecord.mRouterRecords.remove(routerRecord);
         routerRecord.mUserRecord.mHandler.sendMessage(
@@ -707,6 +719,11 @@
         if (routerRecord.mDiscoveryPreference.equals(discoveryRequest)) {
             return;
         }
+
+        Slog.i(TAG, TextUtils.formatSimple(
+                "setDiscoveryRequestWithRouter2 | router: %d, discovery request: %s",
+                routerRecord.mRouterId, discoveryRequest.toString()));
+
         routerRecord.mDiscoveryPreference = discoveryRequest;
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers,
@@ -724,6 +741,10 @@
         RouterRecord routerRecord = mAllRouterRecords.get(binder);
 
         if (routerRecord != null) {
+            Slog.i(TAG, TextUtils.formatSimple(
+                    "setRouteVolumeWithRouter2 | router: %d, volume: %d",
+                    routerRecord.mRouterId, volume));
+
             routerRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::setRouteVolumeOnHandler,
                             routerRecord.mUserRecord.mHandler,
@@ -804,6 +825,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "selectRouteWithRouter2 | router: %d, route: %s",
+                routerRecord.mRouterId, route.getId()));
+
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::selectRouteOnHandler,
                         routerRecord.mUserRecord.mHandler,
@@ -819,6 +844,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "deselectRouteWithRouter2 | router: %d, route: %s",
+                routerRecord.mRouterId, route.getId()));
+
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::deselectRouteOnHandler,
                         routerRecord.mUserRecord.mHandler,
@@ -834,6 +863,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "transferToRouteWithRouter2 | router: %d, route: %s",
+                routerRecord.mRouterId, route.getId()));
+
         String defaultRouteId =
                 routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId();
         if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
@@ -859,6 +892,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "setSessionVolumeWithRouter2 | router: %d, session: %s, volume: %d",
+                routerRecord.mRouterId,  uniqueSessionId, volume));
+
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::setSessionVolumeOnHandler,
                         routerRecord.mUserRecord.mHandler,
@@ -874,6 +911,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "releaseSessionWithRouter2 | router: %d, session: %s",
+                routerRecord.mRouterId,  uniqueSessionId));
+
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::releaseSessionOnHandler,
                         routerRecord.mUserRecord.mHandler,
@@ -916,6 +957,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "registerManager | uid: %d, pid: %d, package: %s, user: %d",
+                uid, pid, packageName, userId));
+
         mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid,
                 "Must hold MEDIA_CONTENT_CONTROL permission.");
 
@@ -951,6 +996,13 @@
             return;
         }
         UserRecord userRecord = managerRecord.mUserRecord;
+
+        Slog.i(TAG, TextUtils.formatSimple(
+                "unregisterManager | package: %s, user: %d, manager: %d",
+                managerRecord.mPackageName,
+                userRecord.mUserId,
+                managerRecord.mManagerId));
+
         userRecord.mManagerRecords.remove(managerRecord);
         managerRecord.dispose();
         disposeUserIfNeededLocked(userRecord); // since manager removed from user
@@ -962,6 +1014,10 @@
         if (managerRecord == null) {
             return;
         }
+
+        Slog.i(TAG, TextUtils.formatSimple(
+                "startScan | manager: %d", managerRecord.mManagerId));
+
         managerRecord.startScan();
     }
 
@@ -971,6 +1027,10 @@
         if (managerRecord == null) {
             return;
         }
+
+        Slog.i(TAG, TextUtils.formatSimple(
+                "stopScan | manager: %d", managerRecord.mManagerId));
+
         managerRecord.stopScan();
     }
 
@@ -984,6 +1044,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "setRouteVolumeWithManager | manager: %d, route: %s, volume: %d",
+                managerRecord.mManagerId, route.getId(), volume));
+
         long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
         managerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::setRouteVolumeOnHandler,
@@ -999,6 +1063,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "requestCreateSessionWithManager | manager: %d, route: %s",
+                managerRecord.mManagerId, route.getId()));
+
         String packageName = oldSession.getClientPackageName();
 
         RouterRecord routerRecord = managerRecord.mUserRecord.findRouterRecordLocked(packageName);
@@ -1044,6 +1112,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "selectRouteWithManager | manager: %d, session: %s, route: %s",
+                managerRecord.mManagerId, uniqueSessionId, route.getId()));
+
         // Can be null if the session is system's or RCN.
         RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
                 .findRouterWithSessionLocked(uniqueSessionId);
@@ -1065,6 +1137,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "deselectRouteWithManager | manager: %d, session: %s, route: %s",
+                managerRecord.mManagerId, uniqueSessionId, route.getId()));
+
         // Can be null if the session is system's or RCN.
         RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
                 .findRouterWithSessionLocked(uniqueSessionId);
@@ -1086,6 +1162,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "transferToRouteWithManager | manager: %d, session: %s, route: %s",
+                managerRecord.mManagerId, uniqueSessionId, route.getId()));
+
         // Can be null if the session is system's or RCN.
         RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
                 .findRouterWithSessionLocked(uniqueSessionId);
@@ -1107,6 +1187,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "setSessionVolumeWithManager | manager: %d, session: %s, volume: %d",
+                managerRecord.mManagerId, uniqueSessionId, volume));
+
         long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
         managerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::setSessionVolumeOnHandler,
@@ -1124,6 +1208,10 @@
             return;
         }
 
+        Slog.i(TAG, TextUtils.formatSimple(
+                "releaseSessionWithManager | manager: %d, session: %s",
+                managerRecord.mManagerId, uniqueSessionId));
+
         RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
                 .findRouterWithSessionLocked(uniqueSessionId);
 
@@ -1484,6 +1572,24 @@
 
             List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true);
             List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false);
+
+            if (!addedRoutes.isEmpty()) {
+                // If routes were added, currentInfo cannot be null.
+                Slog.i(TAG,
+                        toLoggingMessage(
+                                /* source= */ "addProviderRoutes",
+                                currentInfo.getUniqueId(),
+                                (ArrayList) addedRoutes));
+            }
+            if (!removedRoutes.isEmpty()) {
+                // If routes were removed, prevInfo cannot be null.
+                Slog.i(TAG,
+                        toLoggingMessage(
+                                /* source= */ "removeProviderRoutes",
+                                prevInfo.getUniqueId(),
+                                (ArrayList) removedRoutes));
+            }
+
             List<IMediaRouter2Manager> managers = getManagers();
             List<MediaRoute2Info> defaultRoute = new ArrayList<>();
             defaultRoute.add(mSystemProvider.getDefaultRoute());
@@ -1522,6 +1628,16 @@
             }
         }
 
+        private static String toLoggingMessage(
+                String source, String providerId, ArrayList<MediaRoute2Info> routes) {
+            String routesString =
+                    routes.stream()
+                            .map(it -> String.format("%s | %s", it.getOriginalId(), it.getName()))
+                            .collect(Collectors.joining(/* delimiter= */ ", "));
+            return TextUtils.formatSimple("%s | provider: %s, routes: [%s]",
+                    source, providerId, routesString);
+        }
+
         private int getLastProviderInfoIndex(@NonNull String providerId) {
             for (int i = 0; i < mLastProviderInfos.size(); i++) {
                 MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i);
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index b75ba75..c5a337d 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -21,7 +21,9 @@
 import android.media.Session2Token;
 import android.media.session.MediaSession;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import java.io.PrintWriter;
@@ -82,6 +84,10 @@
      * @param record The record to add.
      */
     public void addSession(MediaSessionRecordImpl record) {
+        Slog.i(TAG, TextUtils.formatSimple(
+                "addSession to bottom of stack | record: %s",
+                record
+        ));
         mSessions.add(record);
         clearCache(record.getUserId());
 
@@ -97,6 +103,10 @@
      * @param record The record to remove.
      */
     public void removeSession(MediaSessionRecordImpl record) {
+        Slog.i(TAG, TextUtils.formatSimple(
+                "removeSession | record: %s",
+                record
+        ));
         mSessions.remove(record);
         if (mMediaButtonSession == record) {
             // When the media button session is removed, nullify the media button session and do not
@@ -142,6 +152,10 @@
     public void onPlaybackStateChanged(
             MediaSessionRecordImpl record, boolean shouldUpdatePriority) {
         if (shouldUpdatePriority) {
+            Slog.i(TAG, TextUtils.formatSimple(
+                    "onPlaybackStateChanged - Pushing session to top | record: %s",
+                    record
+            ));
             mSessions.remove(record);
             mSessions.add(0, record);
             clearCache(record.getUserId());