Merge "Fix split black if active from pip" into sc-v2-dev
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index adda721..9bf71ec 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1266,6 +1266,11 @@
     public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
         cancelAnimation(runner, false /* invokeCallback */);
         if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
+        if (runner.getAnimationType() == ANIMATION_TYPE_RESIZE) {
+            // The resize animation doesn't show or hide the insets. We shouldn't change the
+            // requested visibility.
+            return;
+        }
         if (shown) {
             showDirectly(runner.getTypes(), true /* fromIme */);
         } else {
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index e1352dd..edcfc95 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -131,6 +131,9 @@
 
     @Override
     public boolean applyChangeInsets(InsetsState outState) {
+        if (mCancelled) {
+            return false;
+        }
         final float fraction = mAnimation.getInterpolatedFraction();
         for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
             final InsetsSource fromSource = mFromState.peekSource(type);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cdfd7be..d053def 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -768,6 +768,12 @@
     private boolean mWaitForBlastSyncComplete = false;
 
     /**
+     * Keeps track of the last frame number that was attempted to draw. Should only be accessed on
+     * the RenderThread.
+     */
+    private long mRtLastAttemptedDrawFrameNum = 0;
+
+    /**
      * Keeps track of whether a traverse was triggered while the UI thread was paused. This can
      * occur when the client is waiting on another process to submit the transaction that
      * contains the buffer. The UI thread needs to wait on the callback before it can submit
@@ -4051,40 +4057,19 @@
     }
 
     /**
-     * The callback will run on the render thread.
+     * Only call this on the UI Thread.
      */
-    private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
-            boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
-        final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
-        mBLASTDrawConsumer = null;
-        return frameNr -> {
-            if (DEBUG_BLAST) {
-                Log.d(mTag, "Received frameCompleteCallback frameNum=" + frameNr);
-            }
-
-            handler.postAtFrontOfQueue(() -> {
-                if (mNextDrawUseBlastSync) {
-                    // We don't need to synchronize mRtBLASTSyncTransaction here since we're
-                    // guaranteed that this is called after onFrameDraw and mNextDrawUseBlastSync
-                    // is only true when the UI thread is paused. Therefore, no one should be
-                    // modifying this object until the next vsync.
-                    mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
-                    if (blastSyncConsumer != null) {
-                        blastSyncConsumer.accept(mSurfaceChangedTransaction);
-                    }
-                }
-
-                if (reportNextDraw) {
-                    // TODO: Use the frame number
-                    pendingDrawFinished();
-                }
-                if (commitCallbacks != null) {
-                    for (int i = 0; i < commitCallbacks.size(); i++) {
-                        commitCallbacks.get(i).run();
-                    }
-                }
-            });
-        };
+    void clearBlastSync() {
+        mNextDrawUseBlastSync = false;
+        mWaitForBlastSyncComplete = false;
+        if (DEBUG_BLAST) {
+            Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
+                    + " due to a previous skipped traversal.");
+        }
+        if (mRequestedTraverseWhilePaused) {
+            mRequestedTraverseWhilePaused = false;
+            scheduleTraversals();
+        }
     }
 
     /**
@@ -4094,30 +4079,90 @@
         return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
     }
 
-    private boolean addFrameCompleteCallbackIfNeeded() {
+    private boolean addFrameCompleteCallbackIfNeeded(boolean reportNextDraw) {
         if (!isHardwareEnabled()) {
             return false;
         }
 
+        if (!mNextDrawUseBlastSync && !reportNextDraw) {
+            return false;
+        }
+
+        if (DEBUG_BLAST) {
+            Log.d(mTag, "Creating frameCompleteCallback");
+        }
+
+        mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
+            long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
+            if (DEBUG_BLAST) {
+                Log.d(mTag, "Received frameCompleteCallback "
+                        + " lastAcquiredFrameNum=" + frameNr
+                        + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum);
+            }
+
+            boolean frameWasNotDrawn = frameNr != mRtLastAttemptedDrawFrameNum;
+            // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
+            // draw attempt. The next transaction and transaction complete callback were only set
+            // for the current draw attempt.
+            if (frameWasNotDrawn) {
+                mBlastBufferQueue.setNextTransaction(null);
+                mBlastBufferQueue.setTransactionCompleteCallback(mRtLastAttemptedDrawFrameNum,
+                        null);
+            }
+
+            mHandler.postAtFrontOfQueue(() -> {
+                if (mNextDrawUseBlastSync) {
+                    // We don't need to synchronize mRtBLASTSyncTransaction here since we're
+                    // guaranteed that this is called after onFrameDraw and mNextDrawUseBlastSync
+                    // is only true when the UI thread is paused. Therefore, no one should be
+                    // modifying this object until the next vsync.
+                    mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
+                    if (mBLASTDrawConsumer != null) {
+                        mBLASTDrawConsumer.accept(mSurfaceChangedTransaction);
+                    }
+                    mBLASTDrawConsumer = null;
+                }
+
+                if (reportNextDraw) {
+                    pendingDrawFinished();
+                }
+
+                if (frameWasNotDrawn) {
+                    clearBlastSync();
+                }
+            });
+        });
+        return true;
+    }
+
+    private void addFrameCommitCallbackIfNeeded() {
+        if (!isHardwareEnabled()) {
+            return;
+        }
+
         ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
                 .captureFrameCommitCallbacks();
-        final boolean needFrameCompleteCallback =
-                mNextDrawUseBlastSync || mReportNextDraw
-                        || (commitCallbacks != null && commitCallbacks.size() > 0);
-        if (needFrameCompleteCallback) {
-            if (DEBUG_BLAST) {
-                Log.d(mTag, "Creating frameCompleteCallback"
-                        + " mNextDrawUseBlastSync=" + mNextDrawUseBlastSync
-                        + " mReportNextDraw=" + mReportNextDraw
-                        + " commitCallbacks size="
-                        + (commitCallbacks == null ? 0 : commitCallbacks.size()));
-            }
-            mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(
-                    createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw,
-                            commitCallbacks));
-            return true;
+        final boolean needFrameCommitCallback =
+                (commitCallbacks != null && commitCallbacks.size() > 0);
+        if (!needFrameCommitCallback) {
+            return;
         }
-        return false;
+
+        if (DEBUG_DRAW) {
+            Log.d(mTag, "Creating frameCommitCallback"
+                    + " commitCallbacks size=" + commitCallbacks.size());
+        }
+        mAttachInfo.mThreadedRenderer.setFrameCommitCallback(didProduceBuffer -> {
+            if (DEBUG_DRAW) {
+                Log.d(mTag, "Received frameCommitCallback didProduceBuffer=" + didProduceBuffer);
+            }
+
+            mHandler.postAtFrontOfQueue(() -> {
+                for (int i = 0; i < commitCallbacks.size(); i++) {
+                    commitCallbacks.get(i).run();
+                }
+            });
+        });
     }
 
     private void addFrameCallbackIfNeeded() {
@@ -4147,6 +4192,8 @@
                         + " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
             }
 
+            mRtLastAttemptedDrawFrameNum = frame;
+
             if (needsCallbackForBlur) {
                 mBlurRegionAggregator
                     .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
@@ -4169,18 +4216,7 @@
                     if (DEBUG_BLAST) {
                         Log.d(mTag, "Received transactionCompleteCallback frameNum=" + frame);
                     }
-                    mHandler.postAtFrontOfQueue(() -> {
-                        mNextDrawUseBlastSync = false;
-                        mWaitForBlastSyncComplete = false;
-                        if (DEBUG_BLAST) {
-                            Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
-                                    + " due to a previous skipped traversal.");
-                        }
-                        if (mRequestedTraverseWhilePaused) {
-                            mRequestedTraverseWhilePaused = false;
-                            scheduleTraversals();
-                        }
-                    });
+                    mHandler.postAtFrontOfQueue(this::clearBlastSync);
                 });
             }
         };
@@ -4201,8 +4237,9 @@
         mIsDrawing = true;
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
 
-        boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
         addFrameCallbackIfNeeded();
+        addFrameCommitCallbackIfNeeded();
+        boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(mReportNextDraw);
 
         try {
             boolean canUseAsync = draw(fullRedrawNeeded);
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 1c915cb..7631269 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -352,11 +352,25 @@
             throw e.rethrowFromSystemServer();
         }
 
-        Set<WindowMetrics> maxMetrics = new HashSet<>();
-        WindowInsets windowInsets;
+        int size = possibleDisplayInfos.size();
         DisplayInfo currentDisplayInfo;
-        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
-        for (int i = 0; i < possibleDisplayInfos.size(); i++) {
+        WindowInsets windowInsets = null;
+        if (size > 0) {
+            currentDisplayInfo = possibleDisplayInfos.get(0);
+
+            final WindowManager.LayoutParams params =  new WindowManager.LayoutParams();
+            final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
+            // TODO(181127261) not computing insets correctly - need to have underlying
+            // frame reflect the faked orientation.
+            windowInsets = getWindowInsetsFromServerForDisplay(
+                    currentDisplayInfo.displayId, params,
+                    new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
+                            currentDisplayInfo.getNaturalHeight()), isScreenRound,
+                    WINDOWING_MODE_FULLSCREEN);
+        }
+
+        Set<WindowMetrics> maxMetrics = new HashSet<>();
+        for (int i = 0; i < size; i++) {
             currentDisplayInfo = possibleDisplayInfos.get(i);
 
             // Calculate max bounds for this rotation and state.
@@ -364,18 +378,7 @@
                     currentDisplayInfo.logicalHeight);
 
             // Calculate insets for the rotated max bounds.
-            final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
-            // Initialize insets based upon display rotation. Note any window-provided insets
-            // will not be set.
-            windowInsets = getWindowInsetsFromServerForDisplay(
-                    currentDisplayInfo.displayId, params,
-                    new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
-                            currentDisplayInfo.getNaturalHeight()), isScreenRound,
-                    WINDOWING_MODE_FULLSCREEN);
-            // Set the hardware-provided insets.
-            windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
-                    currentDisplayInfo.roundedCorners)
-                    .setDisplayCutout(currentDisplayInfo.displayCutout).build();
+            // TODO(181127261) calculate insets for each display rotation and state.
 
             maxMetrics.add(new WindowMetrics(maxBounds, windowInsets));
         }
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index c1587eb..3b7328e 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -134,6 +134,11 @@
     }
 }
 
+static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    return queue->getLastAcquiredFrameNum();
+}
+
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         // clang-format off
@@ -145,7 +150,8 @@
         {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
         {"nativeSetTransactionCompleteCallback",
                 "(JJLandroid/graphics/BLASTBufferQueue$TransactionCompleteCallback;)V",
-                (void*)nativeSetTransactionCompleteCallback}
+                (void*)nativeSetTransactionCompleteCallback},
+        {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
         // clang-format on
 };
 
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 4c2b114..5e0d9b3 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -34,6 +34,7 @@
 #include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <bionic/malloc.h>
 #include <debuggerd/client.h>
 #include <log/log.h>
@@ -859,7 +860,22 @@
     return poolsSizeKb;
 }
 
+static bool halSupportsGpuPrivateMemory() {
+    int productApiLevel =
+            android::base::GetIntProperty("ro.product.first_api_level",
+                                          android::base::GetIntProperty("ro.build.version.sdk",
+                                                                         __ANDROID_API_FUTURE__));
+    int boardApiLevel =
+            android::base::GetIntProperty("ro.board.api_level",
+                                          android::base::GetIntProperty("ro.board.first_api_level",
+                                                                         __ANDROID_API_FUTURE__));
+
+    return std::min(productApiLevel, boardApiLevel) >= __ANDROID_API_S__;
+}
+
 static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz) {
+    static bool gpuPrivateMemorySupported = halSupportsGpuPrivateMemory();
+
     struct memtrack_proc* p = memtrack_proc_new();
     if (p == nullptr) {
         LOG(ERROR) << "getGpuPrivateMemoryKb: Failed to create memtrack_proc";
@@ -876,6 +892,12 @@
     ssize_t gpuPrivateMem = memtrack_proc_gl_pss(p);
 
     memtrack_proc_destroy(p);
+
+    // Old HAL implementations may return 0 for GPU private memory if not supported
+    if (gpuPrivateMem == 0 && !gpuPrivateMemorySupported) {
+        return -1;
+    }
+
     return gpuPrivateMem / 1024;
 }
 
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index fca4de9..2393eaf 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -38,6 +38,7 @@
                                                               long frameNumber);
     private static native void nativeSetTransactionCompleteCallback(long ptr, long frameNumber,
             TransactionCompleteCallback callback);
+    private static native long nativeGetLastAcquiredFrameNum(long ptr);
 
     /**
      * Callback sent to {@link #setTransactionCompleteCallback(long, TransactionCompleteCallback)}
@@ -140,4 +141,7 @@
         nativeMergeWithNextTransaction(mNativeObject, nativeTransaction, frameNumber);
     }
 
+    public long getLastAcquiredFrameNum() {
+        return nativeGetLastAcquiredFrameNum(mNativeObject);
+    }
 }
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index c3b1cd74..14ad74e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -388,7 +388,8 @@
          */
         public @NonNull FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
                 @NonNull Runnable frameCommitCallback) {
-            setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
+            nSetFrameCommitCallback(mNativeProxy,
+                    didProduceBuffer -> executor.execute(frameCommitCallback));
             return this;
         }
 
@@ -609,6 +610,11 @@
     }
 
     /** @hide */
+    public void setFrameCommitCallback(FrameCommitCallback callback) {
+        nSetFrameCommitCallback(mNativeProxy, callback);
+    }
+
+    /** @hide */
     public void setFrameCompleteCallback(FrameCompleteCallback callback) {
         nSetFrameCompleteCallback(mNativeProxy, callback);
     }
@@ -904,13 +910,27 @@
      *
      * @hide
      */
+    public interface FrameCommitCallback {
+        /**
+         * Invoked after a new frame was drawn
+         *
+         * @param didProduceBuffer The draw successfully produced a new buffer.
+         */
+        void onFrameCommit(boolean didProduceBuffer);
+    }
+
+    /**
+     * Interface used to be notified when RenderThread has finished an attempt to draw. This doesn't
+     * mean a new frame has drawn, specifically if there's nothing new to draw, but only that
+     * RenderThread had a chance to draw a frame.
+     *
+     * @hide
+     */
     public interface FrameCompleteCallback {
         /**
-         * Invoked after a frame draw
-         *
-         * @param frameNr The id of the frame that was drawn.
+         * Invoked after a frame draw was attempted.
          */
-        void onFrameComplete(long frameNr);
+        void onFrameComplete();
     }
 
     /**
@@ -1362,6 +1382,9 @@
 
     private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
 
+    private static native void nSetFrameCommitCallback(long nativeProxy,
+            FrameCommitCallback callback);
+
     private static native void nSetFrameCompleteCallback(long nativeProxy,
             FrameCompleteCallback callback);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 7f37036..8e98b82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -216,6 +216,14 @@
         }
     }
 
+    @Override
+    public void unregisterOrganizer() {
+        super.unregisterOrganizer();
+        if (mStartingWindow != null) {
+            mStartingWindow.clearAllWindows();
+        }
+    }
+
     public void createRootTask(int displayId, int windowingMode, TaskListener listener) {
         ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s",
                 displayId, windowingMode, listener.toString());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 300319a..b40021e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -32,6 +32,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
+import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -529,9 +530,10 @@
                 // Otherwise, we either tapped the stack (which means we're collapsed
                 // and should expand) or the currently selected bubble (we're expanded
                 // and should collapse).
-                if (!maybeShowStackEdu()) {
+                if (!maybeShowStackEdu() && !mShowedUserEducationInTouchListenerActive) {
                     mBubbleData.setExpanded(!mBubbleData.isExpanded());
                 }
+                mShowedUserEducationInTouchListenerActive = false;
             }
         }
     };
@@ -549,6 +551,14 @@
                 return true;
             }
 
+            mShowedUserEducationInTouchListenerActive = false;
+            if (maybeShowStackEdu()) {
+                mShowedUserEducationInTouchListenerActive = true;
+                return true;
+            } else if (isStackEduShowing()) {
+                mStackEduView.hide(false /* fromExpansion */);
+            }
+
             // If the manage menu is visible, just hide it.
             if (mShowingManage) {
                 showManageMenu(false /* show */);
@@ -607,7 +617,8 @@
             // If we're expanding or collapsing, ignore all touch events.
             if (mIsExpansionAnimating
                     // Also ignore events if we shouldn't be draggable.
-                    || (mPositioner.showingInTaskbar() && !mIsExpanded)) {
+                    || (mPositioner.showingInTaskbar() && !mIsExpanded)
+                    || mShowedUserEducationInTouchListenerActive) {
                 return;
             }
 
@@ -628,7 +639,7 @@
                     mExpandedAnimationController.dragBubbleOut(
                             v, viewInitialX + dx, viewInitialY + dy);
                 } else {
-                    if (mStackEduView != null) {
+                    if (isStackEduShowing()) {
                         mStackEduView.hide(false /* fromExpansion */);
                     }
                     mStackAnimationController.moveStackFromTouch(
@@ -646,6 +657,10 @@
                     || (mPositioner.showingInTaskbar() && !mIsExpanded)) {
                 return;
             }
+            if (mShowedUserEducationInTouchListenerActive) {
+                mShowedUserEducationInTouchListenerActive = false;
+                return;
+            }
 
             // First, see if the magnetized object consumes the event - if so, the bubble was
             // released in the target or flung out of it, and we should ignore the event.
@@ -738,6 +753,7 @@
     private ImageView mManageSettingsIcon;
     private TextView mManageSettingsText;
     private boolean mShowingManage = false;
+    private boolean mShowedUserEducationInTouchListenerActive = false;
     private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig(
             SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
     private BubblePositioner mPositioner;
@@ -929,10 +945,12 @@
                 showManageMenu(false /* show */);
             } else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
                 mManageEduView.hide();
-            } else if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+            } else if (isStackEduShowing()) {
                 mStackEduView.hide(false /* isExpanding */);
             } else if (mBubbleData.isExpanded()) {
                 mBubbleData.setExpanded(false);
+            } else {
+                maybeShowStackEdu();
             }
         });
 
@@ -1116,6 +1134,9 @@
      * Whether the educational view should show for the expanded view "manage" menu.
      */
     private boolean shouldShowManageEdu() {
+        if (ActivityManager.isRunningInTestHarness()) {
+            return false;
+        }
         final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
         final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
                 && mExpandedBubble != null;
@@ -1140,6 +1161,9 @@
      * Whether education view should show for the collapsed stack.
      */
     private boolean shouldShowStackEdu() {
+        if (ActivityManager.isRunningInTestHarness()) {
+            return false;
+        }
         final boolean seen = getPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION);
         final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
         if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
@@ -1157,7 +1181,7 @@
      * @return true if education view for collapsed stack should show and was not showing before.
      */
     private boolean maybeShowStackEdu() {
-        if (!shouldShowStackEdu()) {
+        if (!shouldShowStackEdu() || isExpanded()) {
             return false;
         }
         if (mStackEduView == null) {
@@ -1168,9 +1192,13 @@
         return mStackEduView.show(mPositioner.getDefaultStartPosition());
     }
 
+    private boolean isStackEduShowing() {
+        return mStackEduView != null && mStackEduView.getVisibility() == VISIBLE;
+    }
+
     // Recreates & shows the education views. Call when a theme/config change happens.
     private void updateUserEdu() {
-        if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+        if (isStackEduShowing()) {
             removeView(mStackEduView);
             mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
             addView(mStackEduView);
@@ -1852,7 +1880,7 @@
         cancelDelayedExpandCollapseSwitchAnimations();
         final boolean showVertically = mPositioner.showBubblesVertically();
         mIsExpanded = true;
-        if (mStackEduView != null) {
+        if (isStackEduShowing()) {
             mStackEduView.hide(true /* fromExpansion */);
         }
         beforeExpandedViewAnimation();
@@ -2390,7 +2418,7 @@
         if (flyoutMessage == null
                 || flyoutMessage.message == null
                 || !bubble.showFlyout()
-                || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE)
+                || isStackEduShowing()
                 || isExpanded()
                 || mIsExpansionAnimating
                 || mIsGestureInProgress
@@ -2512,7 +2540,7 @@
      * them.
      */
     public void getTouchableRegion(Rect outRect) {
-        if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+        if (isStackEduShowing()) {
             // When user education shows then capture all touches
             outRect.set(0, 0, getWidth(), getHeight());
             return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index f6a90b7..3846de7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -125,6 +125,7 @@
      * @return true if user education was shown, false otherwise.
      */
     fun show(stackPosition: PointF): Boolean {
+        isHiding = false
         if (visibility == VISIBLE) return false
 
         controller.updateWindowFlagsForBackpress(true /* interceptBack */)
@@ -164,6 +165,7 @@
      */
     fun hide(isExpanding: Boolean) {
         if (visibility != VISIBLE || isHiding) return
+        isHiding = true
 
         controller.updateWindowFlagsForBackpress(false /* interceptBack */)
         animate()
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 6280f76..a5579ae 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
@@ -781,6 +781,7 @@
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             // Make the stages adjacent to each other so they occlude what's behind them.
             wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+            wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
             mTaskOrganizer.applyTransaction(wct);
         }
     }
@@ -788,6 +789,7 @@
     private void onStageRootTaskVanished(StageListenerImpl stageListener) {
         if (stageListener == mMainStageListener || stageListener == mSideStageListener) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
             // Deactivate the main stage if it no longer has a root task.
             mMainStage.deactivate(wct);
             mTaskOrganizer.applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index bd48696..270107c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -134,7 +134,8 @@
         mDisplayManager.getDisplay(DEFAULT_DISPLAY);
     }
 
-    private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
+    @VisibleForTesting
+    final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
 
     /**
      * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
@@ -464,8 +465,24 @@
             Slog.d(TAG, "Task start finish, remove starting surface for task "
                     + removalInfo.taskId);
         }
-        removeWindowSynced(removalInfo);
+        removeWindowSynced(removalInfo, false /* immediately */);
+    }
 
+    /**
+     * Clear all starting windows immediately.
+     */
+    public void clearAllWindows() {
+        if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
+            Slog.d(TAG, "Clear all starting windows immediately");
+        }
+        final int taskSize = mStartingWindowRecords.size();
+        final int[] taskIds = new int[taskSize];
+        for (int i = taskSize - 1; i >= 0; --i) {
+            taskIds[i] = mStartingWindowRecords.keyAt(i);
+        }
+        for (int i = taskSize - 1; i >= 0; --i) {
+            removeWindowNoAnimate(taskIds[i]);
+        }
     }
 
     /**
@@ -550,7 +567,8 @@
         return shouldSaveView;
     }
 
-    private void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
+    @VisibleForTesting
+    void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
             @StartingWindowType int suggestType) {
         final StartingWindowRecord tView = new StartingWindowRecord(appToken, view,
                 null/* TaskSnapshotWindow */, suggestType);
@@ -559,19 +577,18 @@
 
     private void removeWindowNoAnimate(int taskId) {
         mTmpRemovalInfo.taskId = taskId;
-        removeWindowSynced(mTmpRemovalInfo);
+        removeWindowSynced(mTmpRemovalInfo, true /* immediately */);
     }
 
     void onImeDrawnOnTask(int taskId) {
         final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
         if (record != null && record.mTaskSnapshotWindow != null
                 && record.mTaskSnapshotWindow.hasImeSurface()) {
-            record.mTaskSnapshotWindow.removeImmediately();
+            removeWindowNoAnimate(taskId);
         }
-        mStartingWindowRecords.remove(taskId);
     }
 
-    protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo) {
+    protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, boolean immediately) {
         final int taskId = removalInfo.taskId;
         final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
         if (record != null) {
@@ -580,7 +597,8 @@
                     Slog.v(TAG, "Removing splash screen window for task: " + taskId);
                 }
                 if (record.mContentView != null) {
-                    if (record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
+                    if (immediately
+                            || record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
                         removeWindowInner(record.mDecorView, false);
                     } else {
                         if (removalInfo.playRevealAnimation) {
@@ -604,8 +622,12 @@
                 if (DEBUG_TASK_SNAPSHOT) {
                     Slog.v(TAG, "Removing task snapshot window for " + taskId);
                 }
-                record.mTaskSnapshotWindow.scheduleRemove(
-                        () -> mStartingWindowRecords.remove(taskId), removalInfo.deferRemoveForIme);
+                if (immediately) {
+                    record.mTaskSnapshotWindow.removeImmediately();
+                } else {
+                    record.mTaskSnapshotWindow.scheduleRemove(() ->
+                            mStartingWindowRecords.remove(taskId), removalInfo.deferRemoveForIme);
+                }
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index e98a3e8..b0a6605 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -29,9 +29,7 @@
 import android.content.Context;
 import android.graphics.Color;
 import android.os.IBinder;
-import android.os.RemoteException;
 import android.os.Trace;
-import android.util.Slog;
 import android.util.SparseIntArray;
 import android.window.StartingWindowInfo;
 import android.window.StartingWindowInfo.StartingWindowType;
@@ -199,6 +197,18 @@
     }
 
     /**
+     * Clear all starting window immediately, called this method when releasing the task organizer.
+     */
+    public void clearAllWindows() {
+        mSplashScreenExecutor.execute(() -> {
+            mStartingSurfaceDrawer.clearAllWindows();
+            synchronized (mTaskBackgroundColors) {
+                mTaskBackgroundColors.clear();
+            }
+        });
+    }
+
+    /**
      * The interface for calls from outside the Shell, within the host process.
      */
     private class StartingSurfaceImpl implements StartingSurface {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 70b7c67..d92b12e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -31,6 +31,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -117,16 +118,19 @@
                 WindowManager.LayoutParams params, int suggestType) {
             // listen for addView
             mAddWindowForTask = taskId;
+            saveSplashScreenRecord(appToken, taskId, view, suggestType);
             // Do not wait for background color
             return false;
         }
 
         @Override
-        protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo) {
+        protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo,
+                boolean immediately) {
             // listen for removeView
             if (mAddWindowForTask == removalInfo.taskId) {
                 mAddWindowForTask = 0;
             }
+            mStartingWindowRecords.remove(removalInfo.taskId);
         }
     }
 
@@ -179,7 +183,7 @@
         removalInfo.taskId = windowInfo.taskInfo.taskId;
         mStartingSurfaceDrawer.removeStartingWindow(removalInfo);
         waitHandlerIdle(mTestHandler);
-        verify(mStartingSurfaceDrawer).removeWindowSynced(any());
+        verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(false));
         assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
     }
 
@@ -267,11 +271,32 @@
 
             // Verify the task snapshot with IME snapshot will be removed when received the real IME
             // drawn callback.
+            // makeTaskSnapshotWindow shall call removeWindowSynced before there add a new
+            // StartingWindowRecord for the task.
             mStartingSurfaceDrawer.onImeDrawnOnTask(1);
-            verify(mockSnapshotWindow).removeImmediately();
+            verify(mStartingSurfaceDrawer, times(2))
+                    .removeWindowSynced(any(), eq(true));
         }
     }
 
+    @Test
+    public void testClearAllWindows() {
+        final int taskId = 1;
+        final StartingWindowInfo windowInfo =
+                createWindowInfo(taskId, android.R.style.Theme);
+        mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
+                STARTING_WINDOW_TYPE_SPLASH_SCREEN);
+        waitHandlerIdle(mTestHandler);
+        verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(),
+                eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
+        assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
+
+        mStartingSurfaceDrawer.clearAllWindows();
+        waitHandlerIdle(mTestHandler);
+        verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(true));
+        assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0);
+    }
+
     private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
         StartingWindowInfo windowInfo = new StartingWindowInfo();
         final ActivityInfo info = new ActivityInfo();
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 54367b8..b5536ad 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -73,6 +73,10 @@
 } gFrameDrawingCallback;
 
 struct {
+    jmethodID onFrameCommit;
+} gFrameCommitCallback;
+
+struct {
     jmethodID onFrameComplete;
 } gFrameCompleteCallback;
 
@@ -101,22 +105,21 @@
     JavaVM* mVm;
 };
 
-class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
+class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> {
 public:
-    explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
+    explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) {
         env->GetJavaVM(&mVm);
         mObject = env->NewGlobalRef(jobject);
         LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
     }
 
-    ~FrameCompleteWrapper() {
-        releaseObject();
-    }
+    ~FrameCommitWrapper() { releaseObject(); }
 
-    void onFrameComplete(int64_t frameNr) {
+    void onFrameCommit(bool didProduceBuffer) {
         if (mObject) {
-            ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
-            getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
+            ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer);
+            getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
+                                        didProduceBuffer);
             releaseObject();
         }
     }
@@ -637,15 +640,33 @@
     }
 }
 
+static void android_view_ThreadedRenderer_setFrameCommitCallback(JNIEnv* env, jobject clazz,
+                                                                 jlong proxyPtr, jobject callback) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    if (!callback) {
+        proxy->setFrameCommitCallback(nullptr);
+    } else {
+        sp<FrameCommitWrapper> wrapper = new FrameCommitWrapper{env, callback};
+        proxy->setFrameCommitCallback(
+                [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); });
+    }
+}
+
 static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
         jobject clazz, jlong proxyPtr, jobject callback) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     if (!callback) {
         proxy->setFrameCompleteCallback(nullptr);
     } else {
-        sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback};
-        proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) {
-            wrapper->onFrameComplete(frameNr);
+        RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+        JavaVM* vm = nullptr;
+        LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+        auto globalCallbackRef =
+                std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
+        proxy->setFrameCompleteCallback([globalCallbackRef]() {
+            JNIEnv* env = getenv(globalCallbackRef->vm());
+            env->CallVoidMethod(globalCallbackRef->object(),
+                                gFrameCompleteCallback.onFrameComplete);
         });
     }
 }
@@ -929,6 +950,8 @@
          (void*)android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback},
         {"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
          (void*)android_view_ThreadedRenderer_setFrameCallback},
+        {"nSetFrameCommitCallback", "(JLandroid/graphics/HardwareRenderer$FrameCommitCallback;)V",
+         (void*)android_view_ThreadedRenderer_setFrameCommitCallback},
         {"nSetFrameCompleteCallback",
          "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V",
          (void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
@@ -994,10 +1017,15 @@
     gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
             "onFrameDraw", "(J)V");
 
+    jclass frameCommitClass =
+            FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback");
+    gFrameCommitCallback.onFrameCommit =
+            GetMethodIDOrDie(env, frameCommitClass, "onFrameCommit", "(Z)V");
+
     jclass frameCompleteClass = FindClassOrDie(env,
             "android/graphics/HardwareRenderer$FrameCompleteCallback");
-    gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass,
-            "onFrameComplete", "(J)V");
+    gFrameCompleteCallback.onFrameComplete =
+            GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");
 
     void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
     fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2f3a509..a066e6f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -491,10 +491,10 @@
         // Notify the callbacks, even if there's nothing to draw so they aren't waiting
         // indefinitely
         waitOnFences();
-        for (auto& func : mFrameCompleteCallbacks) {
-            std::invoke(func, mFrameNumber);
+        for (auto& func : mFrameCommitCallbacks) {
+            std::invoke(func, false /* didProduceBuffer */);
         }
-        mFrameCompleteCallbacks.clear();
+        mFrameCommitCallbacks.clear();
         return 0;
     }
 
@@ -603,10 +603,10 @@
 #endif
 
     if (didSwap) {
-        for (auto& func : mFrameCompleteCallbacks) {
-            std::invoke(func, frameCompleteNr);
+        for (auto& func : mFrameCommitCallbacks) {
+            std::invoke(func, true /* didProduceBuffer */);
         }
-        mFrameCompleteCallbacks.clear();
+        mFrameCommitCallbacks.clear();
     }
 
     if (requireSwap) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6dbfcc3..9df429b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -187,8 +187,8 @@
 
     IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); }
 
-    void addFrameCompleteListener(std::function<void(int64_t)>&& func) {
-        mFrameCompleteCallbacks.push_back(std::move(func));
+    void addFrameCommitListener(std::function<void(bool)>&& func) {
+        mFrameCommitCallbacks.push_back(std::move(func));
     }
 
     void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback) {
@@ -320,7 +320,7 @@
     std::vector<std::future<void>> mFrameFences;
     std::unique_ptr<IRenderPipeline> mRenderPipeline;
 
-    std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;
+    std::vector<std::function<void(bool)>> mFrameCommitCallbacks;
 
     // If set to true, we expect that callbacks into onSurfaceStatsAvailable
     bool mExpectSurfaceStats = false;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index e7081df..94aedd0 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -150,16 +150,18 @@
         canUnblockUiThread = syncFrameState(info);
         canDrawThisFrame = info.out.canDrawThisFrame;
 
-        if (mFrameCompleteCallback) {
-            mContext->addFrameCompleteListener(std::move(mFrameCompleteCallback));
-            mFrameCompleteCallback = nullptr;
+        if (mFrameCommitCallback) {
+            mContext->addFrameCommitListener(std::move(mFrameCommitCallback));
+            mFrameCommitCallback = nullptr;
         }
     }
 
     // Grab a copy of everything we need
     CanvasContext* context = mContext;
-    std::function<void(int64_t)> callback = std::move(mFrameCallback);
+    std::function<void(int64_t)> frameCallback = std::move(mFrameCallback);
+    std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
     mFrameCallback = nullptr;
+    mFrameCompleteCallback = nullptr;
     int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
     int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
     int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
@@ -170,9 +172,9 @@
     }
 
     // Even if we aren't drawing this vsync pulse the next frame number will still be accurate
-    if (CC_UNLIKELY(callback)) {
+    if (CC_UNLIKELY(frameCallback)) {
         context->enqueueFrameWork(
-                [callback, frameNr = context->getFrameNumber()]() { callback(frameNr); });
+                [frameCallback, frameNr = context->getFrameNumber()]() { frameCallback(frameNr); });
     }
 
     nsecs_t dequeueBufferDuration = 0;
@@ -189,6 +191,10 @@
         context->waitOnFences();
     }
 
+    if (CC_UNLIKELY(frameCompleteCallback)) {
+        std::invoke(frameCompleteCallback);
+    }
+
     if (!canUnblockUiThread) {
         unblockUiThread();
     }
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 6a61a2b..e3ea802 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -81,7 +81,11 @@
         mFrameCallback = std::move(callback);
     }
 
-    void setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+    void setFrameCommitCallback(std::function<void(bool)>&& callback) {
+        mFrameCommitCallback = std::move(callback);
+    }
+
+    void setFrameCompleteCallback(std::function<void()>&& callback) {
         mFrameCompleteCallback = std::move(callback);
     }
 
@@ -123,7 +127,8 @@
     int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
 
     std::function<void(int64_t)> mFrameCallback;
-    std::function<void(int64_t)> mFrameCompleteCallback;
+    std::function<void(bool)> mFrameCommitCallback;
+    std::function<void()> mFrameCompleteCallback;
 
     nsecs_t mLastDequeueBufferDuration = 0;
     nsecs_t mLastTargetWorkDuration = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c485ce2..72d4ac5 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -326,7 +326,11 @@
     mDrawFrameTask.setFrameCallback(std::move(callback));
 }
 
-void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+void RenderProxy::setFrameCommitCallback(std::function<void(bool)>&& callback) {
+    mDrawFrameTask.setFrameCommitCallback(std::move(callback));
+}
+
+void RenderProxy::setFrameCompleteCallback(std::function<void()>&& callback) {
     mDrawFrameTask.setFrameCompleteCallback(std::move(callback));
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 2b5405c..6417b38 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -124,7 +124,8 @@
             const std::function<bool(int64_t, int64_t, int64_t)>& callback);
     void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback);
     void setFrameCallback(std::function<void(int64_t)>&& callback);
-    void setFrameCompleteCallback(std::function<void(int64_t)>&& callback);
+    void setFrameCommitCallback(std::function<void(bool)>&& callback);
+    void setFrameCompleteCallback(std::function<void()>&& callback);
 
     void addFrameMetricsObserver(FrameMetricsObserver* observer);
     void removeFrameMetricsObserver(FrameMetricsObserver* observer);
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 98518c2..4a5b637 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -185,7 +185,7 @@
                 <LinearLayout
                     android:id="@+id/turn_on_wifi_layout"
                     style="@style/InternetDialog.Network"
-                    android:layout_height="72dp"
+                    android:layout_height="@dimen/internet_dialog_wifi_network_height"
                     android:gravity="center"
                     android:clickable="false"
                     android:focusable="false">
@@ -227,7 +227,7 @@
                 <LinearLayout
                     android:id="@+id/wifi_connected_layout"
                     style="@style/InternetDialog.Network"
-                    android:layout_height="72dp"
+                    android:layout_height="@dimen/internet_dialog_wifi_network_height"
                     android:paddingStart="20dp"
                     android:paddingEnd="24dp"
                     android:background="@drawable/settingslib_switch_bar_bg_on"
@@ -249,7 +249,7 @@
                         android:orientation="vertical"
                         android:clickable="false"
                         android:layout_width="wrap_content"
-                        android:layout_height="72dp"
+                        android:layout_height="@dimen/internet_dialog_wifi_network_height"
                         android:layout_marginEnd="30dp"
                         android:layout_weight="1"
                         android:gravity="start|center_vertical">
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
index 868331e..f6a2136 100644
--- a/packages/SystemUI/res/layout/internet_list_item.xml
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -25,7 +25,7 @@
     <LinearLayout
         android:id="@+id/wifi_list"
         style="@style/InternetDialog.Network"
-        android:layout_height="72dp"
+        android:layout_height="@dimen/internet_dialog_wifi_network_height"
         android:paddingStart="20dp"
         android:paddingEnd="24dp">
         <FrameLayout
@@ -45,7 +45,7 @@
             android:orientation="vertical"
             android:clickable="false"
             android:layout_width="wrap_content"
-            android:layout_height="72dp"
+            android:layout_height="@dimen/internet_dialog_wifi_network_height"
             android:layout_marginEnd="30dp"
             android:layout_weight="1"
             android:gravity="start|center_vertical">
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 8dbd59d..759670e 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -17,7 +17,6 @@
 
 <com.android.systemui.statusbar.phone.KeyguardBottomAreaView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/keyguard_bottom_area"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
@@ -114,7 +113,8 @@
         android:layout_height="match_parent">
 
         <include layout="@layout/keyguard_bottom_area_overlay" />
-
     </FrameLayout>
 
+    <include layout="@layout/ambient_indication"
+             android:id="@+id/ambient_indication_container" />
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 82186c1..591d8f5 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -119,9 +119,6 @@
             systemui:layout_constraintEnd_toEndOf="parent"
         />
 
-        <include layout="@layout/ambient_indication"
-            android:id="@+id/ambient_indication_container" />
-
         <include layout="@layout/photo_preview_overlay" />
 
         <include
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a2fd669..1938e48 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1243,6 +1243,8 @@
 
     <!-- Internet panel related dimensions -->
     <dimen name="internet_dialog_list_max_height">662dp</dimen>
+    <!-- The height of the WiFi network in Internet panel. -->
+    <dimen name="internet_dialog_wifi_network_height">72dp</dimen>
 
     <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
     <dimen name="large_dialog_width">@dimen/match_parent</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 61591bd..92985de 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -837,7 +837,6 @@
     <style name="Widget.SliceView.Panel">
         <item name="titleSize">16sp</item>
         <item name="rowStyle">@style/SliceRow</item>
-        <item name="android:background">?android:attr/colorBackgroundFloating</item>
     </style>
 
     <style name="SliceRow">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
index 07ad0c8..8aa3aba 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
@@ -51,6 +51,9 @@
     private static final Interpolator ALPHA_OUT_INTERPOLATOR =
             new PathInterpolator(0f, 0f, 0.8f, 1f);
 
+    @DimenRes
+    private final int mMaxWidthResource;
+
     private Paint mRipplePaint;
     private CanvasProperty<Float> mLeftProp;
     private CanvasProperty<Float> mTopProp;
@@ -90,10 +93,17 @@
     private Type mType = Type.ROUNDED_RECT;
 
     public KeyButtonRipple(Context ctx, View targetView, @DimenRes int maxWidthResource) {
+        mMaxWidthResource = maxWidthResource;
         mMaxWidth = ctx.getResources().getDimensionPixelSize(maxWidthResource);
         mTargetView = targetView;
     }
 
+    public void updateResources() {
+        mMaxWidth = mTargetView.getContext().getResources()
+                .getDimensionPixelSize(mMaxWidthResource);
+        invalidateSelf();
+    }
+
     public void setDarkIntensity(float darkIntensity) {
         mDark = darkIntensity >= 0.5f;
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index cbf7397..857cc462 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -21,6 +21,8 @@
 import android.annotation.LayoutRes;
 import android.annotation.StringRes;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.AnimatedVectorDrawable;
@@ -29,12 +31,12 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.FrameLayout;
 
 import androidx.core.view.OneShotPreDrawListener;
 
-import com.android.systemui.shared.R;
 import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
 
 /**
@@ -48,7 +50,21 @@
     private final ViewGroup mKeyButtonContainer;
     private final FloatingRotationButtonView mKeyButtonView;
 
-    private final int mContainerSize;
+    private int mContainerSize;
+    private final Context mContext;
+
+    @StringRes
+    private final int mContentDescriptionResource;
+    @DimenRes
+    private final int mMinMarginResource;
+    @DimenRes
+    private final int mRoundedContentPaddingResource;
+    @DimenRes
+    private final int mTaskbarLeftMarginResource;
+    @DimenRes
+    private final int mTaskbarBottomMarginResource;
+    @DimenRes
+    private final int mButtonDiameterResource;
 
     private AnimatedVectorDrawable mAnimatedDrawable;
     private boolean mIsShowing;
@@ -58,13 +74,13 @@
     private boolean mIsTaskbarVisible = false;
     private boolean mIsTaskbarStashed = false;
 
-    private final FloatingRotationButtonPositionCalculator mPositionCalculator;
+    private FloatingRotationButtonPositionCalculator mPositionCalculator;
 
     private RotationButtonController mRotationButtonController;
     private RotationButtonUpdatesCallback mUpdatesCallback;
     private Position mPosition;
 
-    public FloatingRotationButton(Context context, @StringRes int contentDescription,
+    public FloatingRotationButton(Context context, @StringRes int contentDescriptionResource,
             @LayoutRes int layout, @IdRes int keyButtonId, @DimenRes int minMargin,
             @DimenRes int roundedContentPadding, @DimenRes int taskbarLeftMargin,
             @DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
@@ -73,24 +89,37 @@
         mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(layout, null);
         mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
         mKeyButtonView.setVisibility(View.VISIBLE);
-        mKeyButtonView.setContentDescription(context.getString(contentDescription));
+        mKeyButtonView.setContentDescription(context.getString(contentDescriptionResource));
         mKeyButtonView.setRipple(rippleMaxWidth);
 
-        Resources res = context.getResources();
+        mContext = context;
+
+        mContentDescriptionResource = contentDescriptionResource;
+        mMinMarginResource = minMargin;
+        mRoundedContentPaddingResource = roundedContentPadding;
+        mTaskbarLeftMarginResource = taskbarLeftMargin;
+        mTaskbarBottomMarginResource = taskbarBottomMargin;
+        mButtonDiameterResource = buttonDiameter;
+
+        updateDimensionResources();
+    }
+
+    private void updateDimensionResources() {
+        Resources res = mContext.getResources();
 
         int defaultMargin = Math.max(
-                res.getDimensionPixelSize(minMargin),
-                res.getDimensionPixelSize(roundedContentPadding));
+                res.getDimensionPixelSize(mMinMarginResource),
+                res.getDimensionPixelSize(mRoundedContentPaddingResource));
 
         int taskbarMarginLeft =
-                res.getDimensionPixelSize(taskbarLeftMargin);
+                res.getDimensionPixelSize(mTaskbarLeftMarginResource);
         int taskbarMarginBottom =
-                res.getDimensionPixelSize(taskbarBottomMargin);
+                res.getDimensionPixelSize(mTaskbarBottomMarginResource);
 
         mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
                 taskbarMarginLeft, taskbarMarginBottom);
 
-        final int diameter = res.getDimensionPixelSize(buttonDiameter);
+        final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
         mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
                 taskbarMarginBottom));
     }
@@ -119,32 +148,10 @@
         }
 
         mIsShowing = true;
-        int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 
-        // TODO(b/200103245): add new window type that has z-index above
-        //  TYPE_NAVIGATION_BAR_PANEL as currently it could be below the taskbar which has
-        //  the same window type
-        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                mContainerSize,
-                mContainerSize,
-                0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
-                PixelFormat.TRANSLUCENT);
+        final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
+        mWindowManager.addView(mKeyButtonContainer, layoutParams);
 
-        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-        lp.setTitle("FloatingRotationButton");
-        lp.setFitInsetsTypes(0 /*types */);
-
-        mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
-        mPosition = mPositionCalculator
-                .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
-
-        lp.gravity = mPosition.getGravity();
-        ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
-                mPosition.getGravity();
-
-        updateTranslation(mPosition, /* animate */ false);
-
-        mWindowManager.addView(mKeyButtonContainer, lp);
         if (mAnimatedDrawable != null) {
             mAnimatedDrawable.reset();
             mAnimatedDrawable.start();
@@ -232,6 +239,53 @@
         }
     }
 
+    /**
+     * Updates resources that could be changed in runtime, should be called on configuration
+     * change with changes diff integer mask
+     * @param configurationChanges - configuration changes with flags from ActivityInfo e.g.
+     * {@link android.content.pm.ActivityInfo#CONFIG_DENSITY}
+     */
+    public void onConfigurationChanged(@Config int configurationChanges) {
+        if ((configurationChanges & ActivityInfo.CONFIG_DENSITY) != 0
+                || (configurationChanges & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+            updateDimensionResources();
+
+            if (mIsShowing) {
+                final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
+                mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
+            }
+        }
+
+        if ((configurationChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+            mKeyButtonView.setContentDescription(mContext.getString(mContentDescriptionResource));
+        }
+    }
+
+    private LayoutParams adjustViewPositionAndCreateLayoutParams() {
+        final LayoutParams lp = new LayoutParams(
+                mContainerSize,
+                mContainerSize,
+                /* xpos */ 0, /* ypos */ 0, LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                LayoutParams.FLAG_NOT_FOCUSABLE,
+                PixelFormat.TRANSLUCENT);
+
+        lp.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        lp.setTitle("FloatingRotationButton");
+        lp.setFitInsetsTypes(/* types */ 0);
+
+        mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
+        mPosition = mPositionCalculator
+                .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+        lp.gravity = mPosition.getGravity();
+        ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
+                mPosition.getGravity();
+
+        updateTranslation(mPosition, /* animate */ false);
+
+        return lp;
+    }
+
     private void updateTranslation(Position position, boolean animate) {
         final int translationX = position.getTranslationX();
         final int translationY = position.getTranslationY();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index c5f8fc1..a4b6451 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -17,6 +17,8 @@
 package com.android.systemui.shared.rotation;
 
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -37,12 +39,15 @@
     private KeyButtonRipple mRipple;
     private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
 
+    private final Configuration mLastConfiguration;
+
     public FloatingRotationButtonView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
     public FloatingRotationButtonView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        mLastConfiguration = getResources().getConfiguration();
 
         setClickable(true);
 
@@ -63,6 +68,17 @@
         }
     }
 
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        final int changes = mLastConfiguration.updateFrom(newConfig);
+        if ((changes & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
+                || ((changes & ActivityInfo.CONFIG_DENSITY) != 0)) {
+            if (mRipple != null) {
+                mRipple.updateResources();
+            }
+        }
+    }
+
     public void setColors(int lightColor, int darkColor) {
         getDrawable().setColorFilter(new PorterDuffColorFilter(lightColor, PorterDuff.Mode.SRC_IN));
 
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 68132f4..b2ecc614 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.graphics.Color;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
@@ -30,6 +31,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.graphics.ColorUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
@@ -82,14 +84,18 @@
 
     void updateColorAndBackgroundVisibility() {
         if (mUseBackground && mLockIcon.getDrawable() != null) {
-            mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
-                    android.R.attr.textColorPrimary);
+            mLockIconColor = ColorUtils.blendARGB(
+                    Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
+                    Color.WHITE,
+                    mDozeAmount);
             mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
             mBgView.setAlpha(1f - mDozeAmount);
             mBgView.setVisibility(View.VISIBLE);
         } else {
-            mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
-                    R.attr.wallpaperTextColorAccent);
+            mLockIconColor = ColorUtils.blendARGB(
+                    Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
+                    Color.WHITE,
+                    mDozeAmount);
             mBgView.setVisibility(View.GONE);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 23062d8..25337b6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -24,12 +24,15 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.view.View;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.NonNull;
@@ -42,12 +45,14 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -69,6 +74,7 @@
         Dumpable {
     private final AccessibilityManager mAccessibilityManager;
     private final Lazy<AssistManager> mAssistManagerLazy;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final UserTracker mUserTracker;
     private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
     private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
@@ -98,12 +104,14 @@
             AccessibilityButtonModeObserver accessibilityButtonModeObserver,
             OverviewProxyService overviewProxyService,
             Lazy<AssistManager> assistManagerLazy,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
             NavigationModeController navigationModeController,
             UserTracker userTracker,
             DumpManager dumpManager) {
         mContext = context;
         mAccessibilityManager = accessibilityManager;
         mAssistManagerLazy = assistManagerLazy;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
         mUserTracker = userTracker;
         accessibilityManagerWrapper.addCallback(
                 accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate());
@@ -232,6 +240,19 @@
     }
 
     /**
+     * @return Whether the IME is shown on top of the screen given the {@code vis} flag of
+     * {@link InputMethodService} and the keyguard states.
+     */
+    public boolean isImeShown(int vis) {
+        View shadeWindowView = mStatusBarOptionalLazy.get().get().getNotificationShadeWindowView();
+        boolean isKeyguardShowing = mStatusBarOptionalLazy.get().get().isKeyguardShowing();
+        boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
+                && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+        return imeVisibleOnShade
+                || (!isKeyguardShowing && (vis & InputMethodService.IME_VISIBLE) != 0);
+    }
+
+    /**
      * Callbacks will get fired once immediately after registering via
      * {@link #registerNavTaskStateUpdater(NavbarTaskbarStateUpdater)}
      */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 1219c7a..03bceac 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -72,7 +72,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.inputmethodservice.InputMethodService;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -884,7 +883,8 @@
         if (displayId != mDisplayId) {
             return;
         }
-        boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+        boolean imeShown = mNavBarHelper.isImeShown(vis);
+        showImeSwitcher = imeShown && showImeSwitcher;
         int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
                 imeShown, showImeSwitcher);
         if (hints == mNavigationIconHints) return;
@@ -1757,4 +1757,4 @@
                     mInputMethodManager);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 364a8ae..7c8c3e0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -1212,7 +1212,9 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         mTmpLastConfiguration.updateFrom(mConfiguration);
-        mConfiguration.updateFrom(newConfig);
+        final int changes = mConfiguration.updateFrom(newConfig);
+        mFloatingRotationButton.onConfigurationChanged(changes);
+
         boolean uiCarModeChanged = updateCarMode();
         updateIcons(mTmpLastConfiguration);
         updateRecentsIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index fb9b8eb..8fb394c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -41,7 +41,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.hardware.display.DisplayManager;
-import android.inputmethodservice.InputMethodService;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -265,7 +264,8 @@
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
-        boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+        boolean imeShown = mNavBarHelper.isImeShown(vis);
+        showImeSwitcher = imeShown && showImeSwitcher;
         int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
                 imeShown, showImeSwitcher);
         if (hints != mNavigationIconHints) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 883552a..033fe1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -130,6 +130,7 @@
     private boolean mCanConfigMobileData;
 
     // Wi-Fi entries
+    private int mWifiNetworkHeight;
     @VisibleForTesting
     protected WifiEntry mConnectedWifiEntry;
     @VisibleForTesting
@@ -187,6 +188,9 @@
 
         window.setWindowAnimations(R.style.Animation_InternetDialog);
 
+        mWifiNetworkHeight = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height);
+
         mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog);
         mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
         mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
@@ -335,9 +339,6 @@
         mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
         mWiFiToggle.setOnCheckedChangeListener(
                 (buttonView, isChecked) -> {
-                    if (isChecked) {
-                        mWifiScanNotifyLayout.setVisibility(View.GONE);
-                    }
                     buttonView.setChecked(isChecked);
                     mWifiManager.setWifiEnabled(isChecked);
                 });
@@ -390,6 +391,8 @@
             array.recycle();
 
             mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+            mMobileToggleDivider.setVisibility(
+                    mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
         }
     }
 
@@ -427,9 +430,26 @@
             mSeeAllLayout.setVisibility(View.GONE);
             return;
         }
-        mWifiRecyclerView.setVisibility(mWifiEntriesCount > 0 ? View.VISIBLE : View.GONE);
-        mSeeAllLayout.setVisibility(
-                (mConnectedWifiEntry != null || mWifiEntriesCount > 0) ? View.VISIBLE : View.GONE);
+        mWifiRecyclerView.setMinimumHeight(mWifiNetworkHeight * getWifiListMaxCount());
+        mWifiRecyclerView.setVisibility(View.VISIBLE);
+        final boolean showSeeAll = mConnectedWifiEntry != null || mWifiEntriesCount > 0;
+        mSeeAllLayout.setVisibility(showSeeAll ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    @VisibleForTesting
+    @MainThread
+    int getWifiListMaxCount() {
+        int count = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+        if (mEthernetLayout.getVisibility() == View.VISIBLE) {
+            count -= 1;
+        }
+        if (mMobileNetworkLayout.getVisibility() == View.VISIBLE) {
+            count -= 1;
+        }
+        if (mConnectedWifListLayout.getVisibility() == View.VISIBLE) {
+            count -= 1;
+        }
+        return count;
     }
 
     @MainThread
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index e26f75f..d27b5fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -71,6 +71,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.LockPatternUtils;
@@ -150,6 +151,7 @@
     private ControlsComponent mControlsComponent;
     private boolean mControlServicesAvailable = false;
 
+    @Nullable private View mAmbientIndicationArea;
     private ViewGroup mIndicationArea;
     private TextView mIndicationText;
     private TextView mIndicationTextBottom;
@@ -270,6 +272,29 @@
 
     public void initFrom(KeyguardBottomAreaView oldBottomArea) {
         setStatusBar(oldBottomArea.mStatusBar);
+
+        // if it exists, continue to use the original ambient indication container
+        // instead of the newly inflated one
+        if (mAmbientIndicationArea != null) {
+            // remove old ambient indication from its parent
+            View originalAmbientIndicationView =
+                    oldBottomArea.findViewById(R.id.ambient_indication_container);
+            ((ViewGroup) originalAmbientIndicationView.getParent())
+                    .removeView(originalAmbientIndicationView);
+
+            // remove current ambient indication from its parent (discard)
+            ViewGroup ambientIndicationParent = (ViewGroup) mAmbientIndicationArea.getParent();
+            int ambientIndicationIndex =
+                    ambientIndicationParent.indexOfChild(mAmbientIndicationArea);
+            ambientIndicationParent.removeView(mAmbientIndicationArea);
+
+            // add the old ambient indication to this view
+            ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex);
+            mAmbientIndicationArea = originalAmbientIndicationView;
+
+            // update burn-in offsets
+            dozeTimeTick();
+        }
     }
 
     @Override
@@ -283,6 +308,7 @@
         mWalletButton = findViewById(R.id.wallet_button);
         mControlsButton = findViewById(R.id.controls_button);
         mIndicationArea = findViewById(R.id.keyguard_indication_area);
+        mAmbientIndicationArea = findViewById(R.id.ambient_indication_container);
         mIndicationText = findViewById(R.id.keyguard_indication_text);
         mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
         mIndicationBottomMargin = getResources().getDimensionPixelSize(
@@ -901,6 +927,7 @@
         int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */)
                 - mBurnInYOffset;
         mIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
+        mAmbientIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
     }
 
     public void setAntiBurnInOffsetX(int burnInXOffset) {
@@ -909,6 +936,7 @@
         }
         mBurnInXOffset = burnInXOffset;
         mIndicationArea.setTranslationX(burnInXOffset);
+        mAmbientIndicationArea.setTranslationX(burnInXOffset);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 50a320c..d96fec5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -519,7 +519,6 @@
     private QSPanelController mQSPanelController;
 
     private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
-    private final PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
     KeyguardIndicationController mKeyguardIndicationController;
 
     private View mReportRejectedTouch;
@@ -764,7 +763,6 @@
             ExtensionController extensionController,
             UserInfoControllerImpl userInfoControllerImpl,
             OperatorNameViewController.Factory operatorNameViewControllerFactory,
-            PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
             PhoneStatusBarPolicy phoneStatusBarPolicy,
             KeyguardIndicationController keyguardIndicationController,
             DemoModeController demoModeController,
@@ -803,7 +801,6 @@
         mKeyguardStateController = keyguardStateController;
         mHeadsUpManager = headsUpManagerPhone;
         mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
-        mPhoneStatusBarViewControllerFactory = phoneStatusBarViewControllerFactory;
         mKeyguardIndicationController = keyguardIndicationController;
         mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
         mDynamicPrivacyController = dynamicPrivacyController;
@@ -1148,12 +1145,8 @@
                     }
 
                     mStatusBarView = statusBarFragmentComponent.getPhoneStatusBarView();
-
-                    // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
-                    mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
-                            .create(mStatusBarView, mNotificationPanelViewController
-                                    .getStatusBarTouchEventHandler());
-                    mPhoneStatusBarViewController.init();
+                    mPhoneStatusBarViewController =
+                            statusBarFragmentComponent.getPhoneStatusBarViewController();
 
                     // Ensure we re-propagate panel expansion values to the panel controller and
                     // any listeners it may have, such as PanelBar. This will also ensure we
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 50e6151..633be3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -88,7 +88,6 @@
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -216,7 +215,6 @@
             ExtensionController extensionController,
             UserInfoControllerImpl userInfoControllerImpl,
             OperatorNameViewController.Factory operatorNameViewControllerFactory,
-            PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
             PhoneStatusBarPolicy phoneStatusBarPolicy,
             KeyguardIndicationController keyguardIndicationController,
             DemoModeController demoModeController,
@@ -317,7 +315,6 @@
                 extensionController,
                 userInfoControllerImpl,
                 operatorNameViewControllerFactory,
-                phoneStatusBarViewControllerFactory,
                 phoneStatusBarPolicy,
                 keyguardIndicationController,
                 demoModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 7e39664..3656ed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -20,6 +20,7 @@
 import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 
 import dagger.BindsInstance;
@@ -58,6 +59,7 @@
         // get initialized.
         getBatteryMeterViewController().init();
         getHeadsUpAppearanceController().init();
+        getPhoneStatusBarViewController().init();
     }
 
     /** */
@@ -71,5 +73,9 @@
 
     /** */
     @StatusBarFragmentScope
+    PhoneStatusBarViewController getPhoneStatusBarViewController();
+
+    /** */
+    @StatusBarFragmentScope
     HeadsUpAppearanceController getHeadsUpAppearanceController();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 969361b..d244558 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -19,7 +19,9 @@
 import com.android.systemui.R;
 import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 
 import dagger.Module;
@@ -43,4 +45,16 @@
     static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) {
         return view.findViewById(R.id.battery);
     }
+
+    /** */
+    @Provides
+    @StatusBarFragmentScope
+    static PhoneStatusBarViewController providePhoneStatusBarViewController(
+            PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
+            @RootView PhoneStatusBarView phoneStatusBarView,
+            NotificationPanelViewController notificationPanelViewController) {
+        return phoneStatusBarViewControllerFactory.create(
+                phoneStatusBarView,
+                notificationPanelViewController.getStatusBarTouchEventHandler());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 85add6c..aec9db5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -219,6 +219,7 @@
     private void clearLayoutLineCount(View view) {
         if (view instanceof TextView) {
             ((TextView) view).nullLayouts();
+            view.forceLayout();
         }
     }
 
@@ -270,6 +271,9 @@
 
             clearLayoutLineCount(child);
             child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
+            if (((Button) child).getLayout() == null) {
+                Log.wtf(TAG, "Button layout is null after measure.");
+            }
 
             coveredSuggestions.add(child);
 
@@ -590,6 +594,9 @@
                 button.getPaddingLeft() + button.getPaddingRight() + textWidth
                       + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
         button.measure(widthMeasureSpec, heightMeasureSpec);
+        if (button.getLayout() == null) {
+            Log.wtf(TAG, "Button layout is null after measure.");
+        }
 
         final int newWidth = button.getMeasuredWidth();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index ed5b8cb..a445d6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -18,6 +18,7 @@
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -34,6 +35,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 
 import org.junit.Before;
@@ -42,6 +44,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 import dagger.Lazy;
 
 @RunWith(AndroidJUnit4.class)
@@ -82,8 +86,8 @@
 
         mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
                 mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver,
-                mOverviewProxyService, mAssistManagerLazy, mNavigationModeController,
-                mUserTracker, mDumpManager);
+                mOverviewProxyService, mAssistManagerLazy, () -> Optional.of(mock(StatusBar.class)),
+                mNavigationModeController, mUserTracker, mDumpManager);
 
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 776b6aa..5003013 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -23,17 +23,20 @@
 import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.WindowInsets.Type.ime;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -57,6 +60,7 @@
 import android.view.DisplayInfo;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
 import android.view.accessibility.AccessibilityManager;
@@ -72,6 +76,7 @@
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -83,8 +88,10 @@
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -98,6 +105,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 import java.util.Optional;
 
@@ -130,7 +138,6 @@
     EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
     @Mock
     EdgeBackGestureHandler mEdgeBackGestureHandler;
-    @Mock
     NavBarHelper mNavBarHelper;
     @Mock
     private LightBarController mLightBarController;
@@ -148,6 +155,8 @@
     private InputMethodManager mInputMethodManager;
     @Mock
     private AssistManager mAssistManager;
+    @Mock
+    private StatusBar mStatusBar;
 
     @Rule
     public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -172,6 +181,12 @@
         mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
         mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
         TestableLooper.get(this).runWithLooper(() -> {
+            mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
+                    mock(AccessibilityManagerWrapper.class),
+                    mock(AccessibilityButtonModeObserver.class), mOverviewProxyService,
+                    () -> mock(AssistManager.class), () -> Optional.of(mStatusBar),
+                    mock(NavigationModeController.class), mock(UserTracker.class),
+                    mock(DumpManager.class)));
             mNavigationBar = createNavBar(mContext);
             mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
         });
@@ -256,6 +271,11 @@
         // Create default & external NavBar fragment.
         NavigationBar defaultNavBar = mNavigationBar;
         NavigationBar externalNavBar = mExternalDisplayNavigationBar;
+        NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
+        WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
+        doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+        doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+        doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
         doNothing().when(defaultNavBar).checkNavBarModes();
         doNothing().when(externalNavBar).checkNavBarModes();
         defaultNavBar.createView(null);
@@ -282,6 +302,40 @@
     }
 
     @Test
+    public void testSetImeWindowStatusWhenKeyguardLockingAndImeInsetsChange() {
+        NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
+        doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+        doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
+        doNothing().when(mNavigationBar).checkNavBarModes();
+        mNavigationBar.createView(null);
+        WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
+        doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+
+        // Verify navbar altered back icon when an app is showing IME
+        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+                BACK_DISPOSITION_DEFAULT, true);
+        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+
+        // Verify navbar didn't alter and showing back icon when the keyguard is showing without
+        // requesting IME insets visible.
+        doReturn(true).when(mStatusBar).isKeyguardShowing();
+        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+                BACK_DISPOSITION_DEFAULT, true);
+        assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+        assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+
+        // Verify navbar altered and showing back icon when the keyguard is showing and
+        // requesting IME insets visible.
+        windowInsets = new WindowInsets.Builder().setVisible(ime(), true).build();
+        doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+                BACK_DISPOSITION_DEFAULT, true);
+        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+        assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+    }
+
+    @Test
     public void testA11yEventAfterDetach() {
         View v = mNavigationBar.createView(null);
         mNavigationBar.onViewAttachedToWindow(v);
@@ -314,7 +368,7 @@
                 Optional.of(mock(Pip.class)),
                 Optional.of(mock(LegacySplitScreen.class)),
                 Optional.of(mock(Recents.class)),
-                () -> Optional.of(mock(StatusBar.class)),
+                () -> Optional.of(mStatusBar),
                 mock(ShadeController.class),
                 mock(NotificationRemoteInputManager.class),
                 mock(NotificationShadeDepthController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index b32b4d4..339d5bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -1,5 +1,7 @@
 package com.android.systemui.qs.tiles.dialog;
 
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -219,7 +221,7 @@
     }
 
     @Test
-    public void updateDialog_wifiOnAndNoWifiEntry_hideWifiEntryAndSeeAll() {
+    public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
         // The precondition WiFi ON is already in setUp()
         mInternetDialog.mConnectedWifiEntry = null;
         mInternetDialog.mWifiEntriesCount = 0;
@@ -227,19 +229,21 @@
         mInternetDialog.updateDialog(false);
 
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+        // Show a blank block to fix the dialog height even if there is no WiFi list
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
     }
 
     @Test
-    public void updateDialog_wifiOnAndHasConnectedWifi_showConnectedWifiAndSeeAll() {
+    public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
         // The preconditions WiFi ON and WiFi entries are already in setUp()
         mInternetDialog.mWifiEntriesCount = 0;
 
         mInternetDialog.updateDialog(false);
 
         assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+        // Show a blank block to fix the dialog height even if there is no WiFi list
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
@@ -412,4 +416,54 @@
         assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
         assertThat(mInternetDialog.mIsSearchingHidden).isTrue();
     }
+
+    @Test
+    public void getWifiListMaxCount_returnCountCorrectly() {
+        // Ethernet, MobileData, ConnectedWiFi are all hidden.
+        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+        setNetworkVisible(false, false, false);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+
+        // Only one of Ethernet, MobileData, ConnectedWiFi is displayed.
+        // Then the maximum count  is equal to MAX_WIFI_ENTRY_COUNT - 1.
+        setNetworkVisible(true, false, false);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+        setNetworkVisible(false, true, false);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+        setNetworkVisible(false, false, true);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+        // Only one of Ethernet, MobileData, ConnectedWiFi is hidden.
+        // Then the maximum count  is equal to MAX_WIFI_ENTRY_COUNT - 2.
+        setNetworkVisible(true, true, false);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+        setNetworkVisible(true, false, true);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+        setNetworkVisible(false, true, true);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+        // Ethernet, MobileData, ConnectedWiFi are all displayed.
+        // Then the maximum count  is equal to MAX_WIFI_ENTRY_COUNT - 3.
+        setNetworkVisible(true, true, true);
+
+        assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 3);
+    }
+
+    private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
+            boolean connectedWifiVisible) {
+        mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
+        mMobileDataToggle.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
+        mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 210744e..3257a84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -47,8 +47,8 @@
 
     @Test
     fun initFrom_doesntCrash() {
-        val other = LayoutInflater.from(mContext).inflate(
-                R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
+        val other = LayoutInflater.from(mContext).inflate(R.layout.keyguard_bottom_area,
+                null, false) as KeyguardBottomAreaView
 
         other.initFrom(mKeyguardBottomArea)
         other.launchVoiceAssist()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d58e13c..b0933b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -275,7 +275,6 @@
     @Mock private StartingSurface mStartingSurface;
     @Mock private OperatorNameViewController mOperatorNameViewController;
     @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
-    @Mock private PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
     @Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -433,7 +432,6 @@
                 mExtensionController,
                 mUserInfoControllerImpl,
                 mOperatorNameViewControllerFactory,
-                mPhoneStatusBarViewControllerFactory,
                 mPhoneStatusBarPolicy,
                 mKeyguardIndicationController,
                 mDemoModeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 26e0387..6818947b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.when;
 
 import android.app.Fragment;
@@ -63,7 +62,6 @@
 import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -85,7 +83,6 @@
     // Set in instantiate()
     private StatusBarIconController mStatusBarIconController;
     private NetworkController mNetworkController;
-    private StatusBarStateController mStatusBarStateController;
     private KeyguardStateController mKeyguardStateController;
 
     private final StatusBar mStatusBar = mock(StatusBar.class);
@@ -98,7 +95,11 @@
     @Mock
     private StatusBarFragmentComponent mStatusBarFragmentComponent;
     @Mock
+    private StatusBarStateController mStatusBarStateController;
+    @Mock
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
+    @Mock
+    private NotificationPanelViewController mNotificationPanelViewController;
 
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
@@ -106,49 +107,35 @@
 
     @Before
     public void setup() {
-        mStatusBarStateController = mDependency
-                .injectMockDependency(StatusBarStateController.class);
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
-        when(mStatusBar.getPanelController()).thenReturn(
-                mock(NotificationPanelViewController.class));
     }
 
     @Test
-    public void testDisableNone() throws Exception {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+    public void testDisableNone() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
-                .getVisibility());
-        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
-                .getVisibility());
+        assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
     @Test
-    public void testDisableSystemInfo() throws Exception {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+    public void testDisableSystemInfo() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
 
-        assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
-                .getVisibility());
+        assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
-                .getVisibility());
+        assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
     }
 
     @Test
-    public void testDisableNotifications() throws Exception {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+    public void testDisableNotifications() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
@@ -160,25 +147,21 @@
     }
 
     @Test
-    public void testDisableClock() throws Exception {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+    public void testDisableClock() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
 
-        assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+        assertEquals(View.GONE, getClockView().getVisibility());
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
     @Test
     public void disable_noOngoingCall_chipHidden() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
 
@@ -190,9 +173,7 @@
 
     @Test
     public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
 
@@ -206,9 +187,7 @@
 
     @Test
     public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
 
@@ -221,9 +200,7 @@
 
     @Test
     public void disable_ongoingCallEnded_chipHidden() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
 
@@ -241,56 +218,87 @@
                 mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
     }
 
-    @Ignore("b/192618546")
     @Test
-    public void testOnDozingChanged() throws Exception {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
-
-        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
-
-        Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
-
-        reset(mStatusBarStateController);
+    public void disable_isDozingButNoCustomClock_clockAndSystemInfoVisible() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(true);
+        when(mNotificationPanelViewController.hasCustomClock()).thenReturn(false);
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+    }
+
+    @Test
+    public void disable_customClockButNotDozing_clockAndSystemInfoVisible() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+        when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+    }
+
+    @Test
+    public void disable_dozingAndCustomClock_clockAndSystemInfoHidden() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        when(mStatusBarStateController.isDozing()).thenReturn(true);
+        when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+        // Make sure they start out as visible
+        assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+        assertEquals(View.GONE, getClockView().getVisibility());
+    }
+
+    @Test
+    public void onDozingChanged_clockAndSystemInfoVisibilitiesUpdated() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        when(mStatusBarStateController.isDozing()).thenReturn(true);
+        when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+        // Make sure they start out as visible
+        assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+
         fragment.onDozingChanged(true);
 
-        Mockito.verify(mStatusBarStateController).isDozing();
-        Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+        // When this callback is triggered, we want to make sure the clock and system info
+        // visibilities are recalculated. Since dozing=true, they shouldn't be visible.
+        assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+        assertEquals(View.GONE, getClockView().getVisibility());
     }
 
     @Test
     public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
-
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+        assertEquals(View.GONE, getClockView().getVisibility());
     }
 
     @Test
     public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
-
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
     }
 
     @Test
     public void setUp_fragmentCreatesDaggerComponent() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent());
     }
@@ -325,7 +333,7 @@
                 new StatusBarHideIconsForBouncerManager(
                         mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
                 mKeyguardStateController,
-                mock(NotificationPanelViewController.class),
+                mNotificationPanelViewController,
                 mNetworkController,
                 mStatusBarStateController,
                 mCommandQueue,
@@ -361,4 +369,18 @@
         when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
                 mNotificationAreaInner);
     }
+
+    private CollapsedStatusBarFragment resumeAndGetFragment() {
+        mFragments.dispatchResume();
+        processAllMessages();
+        return (CollapsedStatusBarFragment) mFragment;
+    }
+
+    private View getClockView() {
+        return mFragment.getView().findViewById(R.id.clock);
+    }
+
+    private View getSystemIconAreaView() {
+        return mFragment.getView().findViewById(R.id.system_icon_area);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 61b8ded..7341e74 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -251,7 +251,7 @@
                         "Successful background authentication!");
             }
 
-            mAlreadyDone = true;
+            markAlreadyDone();
 
             if (mTaskStackListener != null) {
                 mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
@@ -327,7 +327,7 @@
             final @LockoutTracker.LockoutMode int lockoutMode =
                     handleFailedAttempt(getTargetUserId());
             if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
-                mAlreadyDone = true;
+                markAlreadyDone();
             }
 
             final CoexCoordinator coordinator = CoexCoordinator.getInstance();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 9764a16..b73e911 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -114,7 +114,7 @@
     // Currently only used for authentication client. The cookie generated by BiometricService
     // is never 0.
     private final int mCookie;
-    boolean mAlreadyDone;
+    private boolean mAlreadyDone = false;
 
     // Use an empty callback by default since delayed operations can receive events
     // before they are started and cause NPE in subclasses that access this field directly.
@@ -202,11 +202,9 @@
         return callback;
     }
 
-    public boolean isAlreadyDone() {
-        return mAlreadyDone;
-    }
-
-    public void destroy() {
+    /** Signals this operation has completed its lifecycle and should no longer be used. */
+    void destroy() {
+        mAlreadyDone = true;
         if (mToken != null) {
             try {
                 mToken.unlinkToDeath(this, 0);
@@ -218,6 +216,20 @@
         }
     }
 
+    /**
+     * Call while the operation is still active, but nearly done, to prevent any action
+     * upon client death (only needed for authentication clients).
+     */
+    void markAlreadyDone() {
+        Slog.d(TAG, "marking operation as done: " + this);
+        mAlreadyDone = true;
+    }
+
+    /** If this operation has been marked as completely done (or cancelled). */
+    public boolean isAlreadyDone() {
+        return mAlreadyDone;
+    }
+
     @Override
     public void binderDied() {
         binderDiedInternal(true /* clearListener */);
@@ -225,10 +237,9 @@
 
     // TODO(b/157790417): Move this to the scheduler
     void binderDiedInternal(boolean clearListener) {
-        Slog.e(TAG, "Binder died, owner: " + getOwnerString()
-                + ", operation: " + this.getClass().getName());
+        Slog.e(TAG, "Binder died, operation: " + this);
 
-        if (isAlreadyDone()) {
+        if (mAlreadyDone) {
             Slog.w(TAG, "Binder died but client is finished, ignoring");
             return;
         }
@@ -299,7 +310,7 @@
     @Override
     public String toString() {
         return "{[" + mSequentialId + "] "
-                + this.getClass().getSimpleName()
+                + this.getClass().getName()
                 + ", proto=" + getProtoEnum()
                 + ", owner=" + getOwnerString()
                 + ", cookie=" + getCookie()
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 361ec40..a358bc2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -605,6 +605,9 @@
         if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
             Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
             // We can set it to null immediately, since the HAL was never notified to start.
+            if (mCurrentOperation != null) {
+                mCurrentOperation.mClientMonitor.destroy();
+            }
             mCurrentOperation = null;
             startNextOperationIfIdle();
             return;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9d6678053..51da107 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -542,9 +542,6 @@
      */
     private InputMethodSubtype mCurrentSubtype;
 
-    // Was the keyguard locked when this client became current?
-    private boolean mCurClientInKeyguard;
-
     /**
      * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
      */
@@ -2363,14 +2360,9 @@
         mImeHiddenByDisplayPolicy = false;
 
         if (mCurClient != cs) {
-            // Was the keyguard locked when switching over to the new client?
-            mCurClientInKeyguard = isKeyguardLocked();
             // If the client is changing, we need to switch over to the new
             // one.
             unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT);
-            if (DEBUG) Slog.v(TAG, "switching to client: client="
-                    + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
-
             // If the screen is on, inform the new client it is active
             if (mIsInteractive) {
                 scheduleSetActiveToClient(cs, true /* active */, false /* fullscreen */,
@@ -2875,10 +2867,6 @@
         // all updateSystemUi happens on system previlege.
         final long ident = Binder.clearCallingIdentity();
         try {
-            // apply policy for binder calls
-            if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) {
-                vis = 0;
-            }
             if (!mCurPerceptible) {
                 vis &= ~InputMethodService.IME_VISIBLE;
             }
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 8460d67..1781588 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -52,8 +52,6 @@
 
     private class GnssMeasurementListenerRegistration extends GnssListenerRegistration {
 
-        private static final String GNSS_MEASUREMENTS_BUCKET = "gnss_measurement";
-
         protected GnssMeasurementListenerRegistration(
                 @Nullable GnssMeasurementRequest request,
                 CallerIdentity callerIdentity,
@@ -70,15 +68,13 @@
         @Nullable
         @Override
         protected void onActive() {
-            mLocationAttributionHelper.reportHighPowerLocationStart(
-                    getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+            mLocationAttributionHelper.reportHighPowerLocationStart(getIdentity());
         }
 
         @Nullable
         @Override
         protected void onInactive() {
-            mLocationAttributionHelper.reportHighPowerLocationStop(
-                    getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+            mLocationAttributionHelper.reportHighPowerLocationStop(getIdentity());
         }
     }
 
diff --git a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
index 5cb360b..4838752 100644
--- a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
@@ -24,55 +24,23 @@
 
 import android.location.util.identity.CallerIdentity;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
 
 /**
  * Helps manage appop monitoring for multiple location clients.
  */
 public class LocationAttributionHelper {
 
-    private static class BucketKey {
-        private final String mBucket;
-        private final Object mKey;
-
-        private BucketKey(String bucket, Object key) {
-            mBucket = Objects.requireNonNull(bucket);
-            mKey = Objects.requireNonNull(key);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            BucketKey that = (BucketKey) o;
-            return mBucket.equals(that.mBucket)
-                    && mKey.equals(that.mKey);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mBucket, mKey);
-        }
-    }
-
     private final AppOpsHelper mAppOpsHelper;
 
     @GuardedBy("this")
-    private final Map<CallerIdentity, Set<BucketKey>> mAttributions;
+    private final Map<CallerIdentity, Integer> mAttributions;
     @GuardedBy("this")
-    private final Map<CallerIdentity, Set<BucketKey>> mHighPowerAttributions;
+    private final Map<CallerIdentity, Integer> mHighPowerAttributions;
 
     public LocationAttributionHelper(AppOpsHelper appOpsHelper) {
         mAppOpsHelper = appOpsHelper;
@@ -84,15 +52,16 @@
     /**
      * Report normal location usage for the given caller in the given bucket, with a unique key.
      */
-    public synchronized void reportLocationStart(CallerIdentity identity, String bucket,
-            Object key) {
-        Set<BucketKey> keySet = mAttributions.computeIfAbsent(identity,
-                i -> new ArraySet<>());
-        boolean empty = keySet.isEmpty();
-        if (keySet.add(new BucketKey(bucket, key)) && empty) {
-            if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
-                mAttributions.remove(identity);
+    public synchronized void reportLocationStart(CallerIdentity identity) {
+        identity = CallerIdentity.forAggregation(identity);
+
+        int count = mAttributions.getOrDefault(identity, 0);
+        if (count == 0) {
+            if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
+                mAttributions.put(identity, 1);
             }
+        } else {
+            mAttributions.put(identity, count + 1);
         }
     }
 
@@ -100,13 +69,15 @@
      * Report normal location usage has stopped for the given caller in the given bucket, with a
      * unique key.
      */
-    public synchronized void reportLocationStop(CallerIdentity identity, String bucket,
-            Object key) {
-        Set<BucketKey> keySet = mAttributions.get(identity);
-        if (keySet != null && keySet.remove(new BucketKey(bucket, key))
-                && keySet.isEmpty()) {
+    public synchronized void reportLocationStop(CallerIdentity identity) {
+        identity = CallerIdentity.forAggregation(identity);
+
+        int count = mAttributions.getOrDefault(identity, 0);
+        if (count == 1) {
             mAttributions.remove(identity);
             mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
+        } else if (count > 1) {
+            mAttributions.put(identity, count - 1);
         }
     }
 
@@ -114,19 +85,19 @@
      * Report high power location usage for the given caller in the given bucket, with a unique
      * key.
      */
-    public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String bucket,
-            Object key) {
-        Set<BucketKey> keySet = mHighPowerAttributions.computeIfAbsent(identity,
-                i -> new ArraySet<>());
-        boolean empty = keySet.isEmpty();
-        if (keySet.add(new BucketKey(bucket, key)) && empty) {
-            if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
-                if (D) {
-                    Log.v(TAG, "starting high power location attribution for " + identity);
-                }
-            } else {
-                mHighPowerAttributions.remove(identity);
+    public synchronized void reportHighPowerLocationStart(CallerIdentity identity) {
+        identity = CallerIdentity.forAggregation(identity);
+
+        int count = mHighPowerAttributions.getOrDefault(identity, 0);
+        if (count == 0) {
+            if (D) {
+                Log.v(TAG, "starting high power location attribution for " + identity);
             }
+            if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+                mHighPowerAttributions.put(identity, 1);
+            }
+        } else {
+            mHighPowerAttributions.put(identity, count + 1);
         }
     }
 
@@ -134,16 +105,18 @@
      * Report high power location usage has stopped for the given caller in the given bucket,
      * with a unique key.
      */
-    public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String bucket,
-            Object key) {
-        Set<BucketKey> keySet = mHighPowerAttributions.get(identity);
-        if (keySet != null && keySet.remove(new BucketKey(bucket, key))
-                && keySet.isEmpty()) {
+    public synchronized void reportHighPowerLocationStop(CallerIdentity identity) {
+        identity = CallerIdentity.forAggregation(identity);
+
+        int count = mHighPowerAttributions.getOrDefault(identity, 0);
+        if (count == 1) {
             if (D) {
                 Log.v(TAG, "stopping high power location attribution for " + identity);
             }
             mHighPowerAttributions.remove(identity);
             mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
+        } else if (count > 1) {
+            mHighPowerAttributions.put(identity, count - 1);
         }
     }
 }
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index f7c4d03..155b618 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -398,7 +398,7 @@
             EVENT_LOG.logProviderClientActive(mName, getIdentity());
 
             if (!getRequest().isHiddenFromAppOps()) {
-                mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
+                mLocationAttributionHelper.reportLocationStart(getIdentity());
             }
             onHighPowerUsageChanged();
 
@@ -413,7 +413,7 @@
 
             onHighPowerUsageChanged();
             if (!getRequest().isHiddenFromAppOps()) {
-                mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
+                mLocationAttributionHelper.reportLocationStop(getIdentity());
             }
 
             onProviderListenerInactive();
@@ -488,10 +488,10 @@
                 if (!getRequest().isHiddenFromAppOps()) {
                     if (mIsUsingHighPower) {
                         mLocationAttributionHelper.reportHighPowerLocationStart(
-                                getIdentity(), getName(), getKey());
+                                getIdentity());
                     } else {
                         mLocationAttributionHelper.reportHighPowerLocationStop(
-                                getIdentity(), getName(), getKey());
+                                getIdentity());
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index dc5126d..2cf23c5 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2741,8 +2741,8 @@
         // If it exist, we need to reparent target root task from TDA to launch root task.
         final TaskDisplayArea tda = mTargetRootTask.getDisplayArea();
         final Task launchRootTask = tda.getLaunchRootTask(mTargetRootTask.getWindowingMode(),
-                mTargetRootTask.getActivityType(), null /** options */,
-                mSourceRootTask, 0 /** launchFlags */);
+                mTargetRootTask.getActivityType(), null /** options */, mSourceRootTask,
+                mLaunchFlags);
         // If target root task is created by organizer, let organizer handle reparent itself.
         if (!mTargetRootTask.mCreatedByOrganizer && launchRootTask != null
                 && launchRootTask != mTargetRootTask) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c3c8318..b7c992e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -591,7 +591,7 @@
     /** Caches the value whether told display manager that we have content. */
     private boolean mLastHasContent;
 
-    private static DisplayRotationUtil sRotationUtil = new DisplayRotationUtil();
+    private DisplayRotationUtil mRotationUtil = new DisplayRotationUtil();
 
     /**
      * The input method window for this display.
@@ -2090,35 +2090,29 @@
         return mDisplayCutoutCache.getOrCompute(mInitialDisplayCutout, rotation);
     }
 
-    static WmDisplayCutout calculateDisplayCutoutForRotationAndDisplaySizeUncached(
-            DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) {
+    private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
+            DisplayCutout cutout, int rotation) {
         if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
             return WmDisplayCutout.NO_CUTOUT;
         }
         if (rotation == ROTATION_0) {
             return WmDisplayCutout.computeSafeInsets(
-                    cutout, displayWidth, displayHeight);
+                    cutout, mInitialDisplayWidth, mInitialDisplayHeight);
         }
         final Insets waterfallInsets =
                 RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final Rect[] newBounds = sRotationUtil.getRotatedBounds(
+        final Rect[] newBounds = mRotationUtil.getRotatedBounds(
                 cutout.getBoundingRectsAll(),
-                rotation, displayWidth, displayHeight);
+                rotation, mInitialDisplayWidth, mInitialDisplayHeight);
         final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
         final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
                 info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
                 info.getCutoutSpec(), rotation, info.getScale());
         return WmDisplayCutout.computeSafeInsets(
                 DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
-                rotated ? displayHeight : displayWidth,
-                rotated ? displayWidth : displayHeight);
-    }
-
-    private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
-            DisplayCutout cutout, int rotation) {
-        return calculateDisplayCutoutForRotationAndDisplaySizeUncached(cutout, rotation,
-                mInitialDisplayWidth, mInitialDisplayHeight);
+                rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
+                rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
     }
 
     RoundedCorners calculateRoundedCornersForRotation(int rotation) {
diff --git a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
index 11a27c5..ef8dee4 100644
--- a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
+++ b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
@@ -120,15 +120,8 @@
             @Surface.Rotation int rotation) {
         DisplayInfo updatedDisplayInfo = new DisplayInfo();
         updatedDisplayInfo.copyFrom(displayInfo);
-        // Apply rotations before updating width and height
-        updatedDisplayInfo.roundedCorners = updatedDisplayInfo.roundedCorners.rotate(rotation,
-                updatedDisplayInfo.logicalWidth, updatedDisplayInfo.logicalHeight);
-        updatedDisplayInfo.displayCutout =
-                DisplayContent.calculateDisplayCutoutForRotationAndDisplaySizeUncached(
-                        updatedDisplayInfo.displayCutout, rotation, updatedDisplayInfo.logicalWidth,
-                        updatedDisplayInfo.logicalHeight).getDisplayCutout();
-
         updatedDisplayInfo.rotation = rotation;
+
         final int naturalWidth = updatedDisplayInfo.getNaturalWidth();
         final int naturalHeight = updatedDisplayInfo.getNaturalHeight();
         updatedDisplayInfo.logicalWidth = naturalWidth;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
index e2e7f5d..94dcdf9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
@@ -58,72 +58,86 @@
     @Test
     public void testLocationMonitoring() {
         CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
-        Object key1 = new Object();
-        Object key2 = new Object();
         CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
-        Object key3 = new Object();
-        Object key4 = new Object();
 
-        mHelper.reportLocationStart(caller1, "gps", key1);
-        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
+        mHelper.reportLocationStart(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller1));
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller1));
 
-        mHelper.reportLocationStart(caller1, "gps", key2);
-        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
+        mHelper.reportLocationStart(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller1));
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller1));
 
-        mHelper.reportLocationStart(caller2, "gps", key3);
-        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
+        mHelper.reportLocationStart(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller2));
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller2));
 
-        mHelper.reportLocationStart(caller2, "gps", key4);
-        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
+        mHelper.reportLocationStart(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller2));
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller2));
 
-        mHelper.reportLocationStop(caller1, "gps", key2);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
-        mHelper.reportLocationStop(caller1, "gps", key1);
-        verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
+        mHelper.reportLocationStop(caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller1));
+        mHelper.reportLocationStop(caller1);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller1));
 
-        mHelper.reportLocationStop(caller2, "gps", key3);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
-        mHelper.reportLocationStop(caller2, "gps", key4);
-        verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
+        mHelper.reportLocationStop(caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+                CallerIdentity.forAggregation(caller2));
+        mHelper.reportLocationStop(caller2);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller2));
     }
 
     @Test
     public void testHighPowerLocationMonitoring() {
         CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
-        Object key1 = new Object();
-        Object key2 = new Object();
         CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
-        Object key3 = new Object();
-        Object key4 = new Object();
 
-        mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
-        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+        mHelper.reportHighPowerLocationStart(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller1));
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller1));
 
-        mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
-        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+        mHelper.reportHighPowerLocationStart(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller1));
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller1));
 
-        mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
-        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+        mHelper.reportHighPowerLocationStart(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller2));
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller2));
 
-        mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
-        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+        mHelper.reportHighPowerLocationStart(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller2));
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller2));
 
-        mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
-        mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
-        verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+        mHelper.reportHighPowerLocationStop(caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller1));
+        mHelper.reportHighPowerLocationStop(caller1);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller1));
 
-        mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
-        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
-        mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
-        verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+        mHelper.reportHighPowerLocationStop(caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller2));
+        mHelper.reportHighPowerLocationStop(caller2);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+                CallerIdentity.forAggregation(caller2));
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index d0b2eda..890a549 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -845,6 +845,48 @@
     }
 
     @Test
+    public void testLocationMonitoring_multipleIdentities() {
+        CallerIdentity identity1 = CallerIdentity.forTest(CURRENT_USER, 1,
+                "mypackage", "attribution", "listener1");
+        CallerIdentity identity2 = CallerIdentity.forTest(CURRENT_USER, 1,
+                "mypackage", "attribution", "listener2");
+
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+                IDENTITY.getPackageName())).isFalse();
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+                IDENTITY.getPackageName())).isFalse();
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = new LocationRequest.Builder(0).setWorkSource(
+                WORK_SOURCE).build();
+        mManager.registerLocationRequest(request1, identity1, PERMISSION_FINE, listener1);
+
+        ILocationListener listener2 = createMockLocationListener();
+        LocationRequest request2 = new LocationRequest.Builder(0).setWorkSource(
+                WORK_SOURCE).build();
+        mManager.registerLocationRequest(request2, identity2, PERMISSION_FINE, listener2);
+
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+                "mypackage")).isTrue();
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+                "mypackage")).isTrue();
+
+        mManager.unregisterLocationRequest(listener2);
+
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+                "mypackage")).isTrue();
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+                "mypackage")).isTrue();
+
+        mManager.unregisterLocationRequest(listener1);
+
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+                "mypackage")).isFalse();
+        assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+                "mypackage")).isFalse();
+    }
+
+    @Test
     public void testProviderRequest() {
         assertThat(mProvider.getRequest().isActive()).isFalse();
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index e3e3900..d192697 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -143,8 +143,8 @@
 
         final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
 
-        final BiometricPromptClientMonitor client1 =
-                new BiometricPromptClientMonitor(mContext, mToken, lazyDaemon1, listener1);
+        final TestAuthenticationClient client1 =
+                new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
         final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
 
         final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
@@ -180,8 +180,8 @@
     @Test
     public void testCancelNotInvoked_whenOperationWaitingForCookie() {
         final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class);
-        final BiometricPromptClientMonitor client1 = new BiometricPromptClientMonitor(mContext,
-                mToken, lazyDaemon1, mock(ClientMonitorCallbackConverter.class));
+        final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
+                lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class));
         final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
 
         // Schedule a BiometricPrompt authentication request
@@ -195,6 +195,8 @@
         // should go back to idle, since in this case the framework has not even requested the HAL
         // to authenticate yet.
         mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+        assertTrue(client1.isAlreadyDone());
+        assertTrue(client1.mDestroyed);
         assertNull(mScheduler.mCurrentOperation);
     }
 
@@ -316,6 +318,10 @@
                 eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
                 eq(0) /* vendorCode */);
         assertNull(mScheduler.getCurrentClient());
+        assertTrue(client1.isAlreadyDone());
+        assertTrue(client1.mDestroyed);
+        assertTrue(client2.isAlreadyDone());
+        assertTrue(client2.mDestroyed);
     }
 
     @Test
@@ -465,39 +471,9 @@
         return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
     }
 
-    private static class BiometricPromptClientMonitor extends AuthenticationClient<Object> {
-
-        public BiometricPromptClientMonitor(@NonNull Context context, @NonNull IBinder token,
-                @NonNull LazyDaemon<Object> lazyDaemon, ClientMonitorCallbackConverter listener) {
-            super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
-                    false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
-                    TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */,
-                    0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class),
-                    false /* isKeyguard */, true /* shouldVibrate */,
-                    false /* isKeyguardBypassEnabled */);
-        }
-
-        @Override
-        protected void stopHalOperation() {
-        }
-
-        @Override
-        protected void startHalOperation() {
-        }
-
-        @Override
-        protected void handleLifecycleAfterAuth(boolean authenticated) {
-
-        }
-
-        @Override
-        public boolean wasUserDetected() {
-            return false;
-        }
-    }
-
     private static class TestAuthenticationClient extends AuthenticationClient<Object> {
         int mNumCancels = 0;
+        boolean mDestroyed = false;
 
         public TestAuthenticationClient(@NonNull Context context,
                 @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -530,6 +506,13 @@
             return false;
         }
 
+        @Override
+        public void destroy() {
+            mDestroyed = true;
+            super.destroy();
+        }
+
+        @Override
         public void cancel() {
             mNumCancels++;
             super.cancel();
@@ -595,6 +578,7 @@
 
         @Override
         public void destroy() {
+            super.destroy();
             mDestroyed = true;
         }