Merge "AOD lock icons should always be white" into sc-v2-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a4fb2c1..a8e06fc 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3204,12 +3204,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR;
   }
 
-  public final class TaskFragmentAppearedInfo implements android.os.Parcelable {
-    method @NonNull public android.view.SurfaceControl getLeash();
-    method @NonNull public android.window.TaskFragmentInfo getTaskFragmentInfo();
-    field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentAppearedInfo> CREATOR;
-  }
-
   public final class TaskFragmentCreationParams implements android.os.Parcelable {
     method @NonNull public android.os.IBinder getFragmentToken();
     method @NonNull public android.graphics.Rect getInitialBounds();
@@ -3246,7 +3240,7 @@
     ctor public TaskFragmentOrganizer(@NonNull java.util.concurrent.Executor);
     method @NonNull public java.util.concurrent.Executor getExecutor();
     method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizerToken();
-    method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentAppearedInfo);
+    method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentInfo);
     method public void onTaskFragmentError(@NonNull android.os.IBinder, @NonNull Throwable);
     method public void onTaskFragmentInfoChanged(@NonNull android.window.TaskFragmentInfo);
     method public void onTaskFragmentParentInfoChanged(@NonNull android.os.IBinder, @NonNull android.content.res.Configuration);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 992f02c..db45466 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4076,6 +4076,34 @@
     }
 
     /**
+     * Gets the message that is shown when a user is switched from.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
+    public @Nullable String getSwitchingFromUserMessage() {
+        try {
+            return getService().getSwitchingFromUserMessage();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the message that is shown when a user is switched to.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USERS)
+    public @Nullable String getSwitchingToUserMessage() {
+        try {
+            return getService().getSwitchingToUserMessage();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Uses the value defined by the platform.
      *
      * @hide
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 9e9e28b..4912703 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -338,6 +338,8 @@
     void setPackageScreenCompatMode(in String packageName, int mode);
     @UnsupportedAppUsage
     boolean switchUser(int userid);
+    String getSwitchingFromUserMessage();
+    String getSwitchingToUserMessage();
     @UnsupportedAppUsage
     void setStopUserOnSwitch(int value);
     boolean removeTask(int taskId);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 8864939..9b19fc4 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -110,6 +110,11 @@
     private int mRepeatingRequestId = REQUEST_ID_NONE;
     // Latest repeating request list's types
     private int[] mRepeatingRequestTypes;
+
+    // Cache failed requests to process later in case of a repeating error callback
+    private int mFailedRepeatingRequestId = REQUEST_ID_NONE;
+    private int[] mFailedRepeatingRequestTypes;
+
     // Map stream IDs to input/output configurations
     private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
             new SimpleEntry<>(REQUEST_ID_NONE, null);
@@ -1326,16 +1331,25 @@
 
                 int requestId = mRepeatingRequestId;
                 mRepeatingRequestId = REQUEST_ID_NONE;
+                mFailedRepeatingRequestId = REQUEST_ID_NONE;
                 int[] requestTypes = mRepeatingRequestTypes;
                 mRepeatingRequestTypes = null;
+                mFailedRepeatingRequestTypes = null;
 
                 long lastFrameNumber;
                 try {
                     lastFrameNumber = mRemoteDevice.cancelRequest(requestId);
                 } catch (IllegalArgumentException e) {
                     if (DEBUG) {
-                        Log.v(TAG, "Repeating request was already stopped for request " + requestId);
+                        Log.v(TAG, "Repeating request was already stopped for request " +
+                                requestId);
                     }
+                    // Cache request id and request types in case of a race with
+                    // "onRepeatingRequestError" which may no yet be scheduled on another thread
+                    // or blocked by us.
+                    mFailedRepeatingRequestId = requestId;
+                    mFailedRepeatingRequestTypes = requestTypes;
+
                     // Repeating request was already stopped. Nothing more to do.
                     return;
                 }
@@ -1965,7 +1979,17 @@
             synchronized(mInterfaceLock) {
                 // Camera is already closed or no repeating request is present.
                 if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) {
-                    return; // Camera already closed
+                    if ((mFailedRepeatingRequestId == repeatingRequestId) &&
+                            (mFailedRepeatingRequestTypes != null) && (mRemoteDevice != null)) {
+                        Log.v(TAG, "Resuming stop of failed repeating request with id: " +
+                                mFailedRepeatingRequestId);
+
+                        checkEarlyTriggerSequenceCompleteLocked(mFailedRepeatingRequestId,
+                                lastFrameNumber, mFailedRepeatingRequestTypes);
+                        mFailedRepeatingRequestId = REQUEST_ID_NONE;
+                        mFailedRepeatingRequestTypes = null;
+                    }
+                    return;
                 }
 
                 // Redirect device callback to the offline session in case we are in the middle
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 7d71984..0b1ed65 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -109,7 +109,8 @@
 
     /** {@hide} */
     public boolean quickPickupSensorEnabled(int user) {
-        return !TextUtils.isEmpty(quickPickupSensorType())
+        return boolSettingDefaultOn(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE, user)
+                && !TextUtils.isEmpty(quickPickupSensorType())
                 && pickupGestureEnabled(user)
                 && !alwaysOnEnabled(user);
     }
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 02b2c5d..d609fb8 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -124,7 +124,12 @@
     public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
             int[] hideTypes) {
         super.setControl(control, showTypes, hideTypes);
-        if (control == null && !isRequestedVisibleAwaitingControl()) {
+        // TODO(b/204524304): clean-up how to deal with the timing issues of hiding IME:
+        //  1) Already requested show IME, in the meantime of WM callback the control but got null
+        //  control when relayout comes first
+        //  2) Make sure no regression on some implicit request IME visibility calls (e.g.
+        //  toggleSoftInput)
+        if (control == null && !mIsRequestedVisibleAwaitingControl) {
             hide();
             removeSurface();
         }
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/WindowManager.java b/core/java/android/view/WindowManager.java
index 8764ccc..a85c9b5 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -4769,6 +4769,16 @@
                     return Integer.toString(inputFeature);
             }
         }
+
+        /**
+         * True if the window should consume all pointer events itself, regardless of whether they
+         * are inside of the window. If the window is modal, its touchable region will expand to the
+         * size of its task.
+         * @hide
+         */
+        public boolean isModal() {
+            return (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+        }
     }
 
     /**
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index e634d60..94f6333 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -236,6 +236,9 @@
      */
     int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
 
+    // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
+    //                    make app pair split only have single root then we can just attach the
+    //                    divider to the single root task in shell.
     int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3;
     int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100;
     int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101;
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 5eb432e..cdfa206 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -19,12 +19,11 @@
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.window.TaskFragmentAppearedInfo;
 import android.window.TaskFragmentInfo;
 
 /** @hide */
 oneway interface ITaskFragmentOrganizer {
-    void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+    void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
     void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
     void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
 
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/window/TaskFragmentAppearedInfo.aidl
deleted file mode 100644
index 3729c09..0000000
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.window;
-
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java
deleted file mode 100644
index 89d9a95..0000000
--- a/core/java/android/window/TaskFragmentAppearedInfo.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.window;
-
-import android.annotation.NonNull;
-import android.annotation.TestApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.SurfaceControl;
-
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-@TestApi
-public final class TaskFragmentAppearedInfo implements Parcelable {
-
-    @NonNull
-    private final TaskFragmentInfo mTaskFragmentInfo;
-
-    @NonNull
-    private final SurfaceControl mLeash;
-
-    /** @hide */
-    public TaskFragmentAppearedInfo(
-            @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) {
-        mTaskFragmentInfo = taskFragmentInfo;
-        mLeash = leash;
-    }
-
-    @NonNull
-    public TaskFragmentInfo getTaskFragmentInfo() {
-        return mTaskFragmentInfo;
-    }
-
-    @NonNull
-    public SurfaceControl getLeash() {
-        return mLeash;
-    }
-
-    private TaskFragmentAppearedInfo(Parcel in) {
-        mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
-        mLeash = in.readTypedObject(SurfaceControl.CREATOR);
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeTypedObject(mTaskFragmentInfo, flags);
-        dest.writeTypedObject(mLeash, flags);
-    }
-
-    @NonNull
-    public static final Creator<TaskFragmentAppearedInfo> CREATOR =
-            new Creator<TaskFragmentAppearedInfo>() {
-                @Override
-                public TaskFragmentAppearedInfo createFromParcel(Parcel in) {
-                    return new TaskFragmentAppearedInfo(in);
-                }
-
-                @Override
-                public TaskFragmentAppearedInfo[] newArray(int size) {
-                    return new TaskFragmentAppearedInfo[size];
-                }
-            };
-
-    @Override
-    public String toString() {
-        return "TaskFragmentAppearedInfo{"
-                + " taskFragmentInfo=" + mTaskFragmentInfo
-                + "}";
-    }
-
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 337c5a1..7e7d370 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -120,8 +120,7 @@
     }
 
     /** Called when a TaskFragment is created and organized by this organizer. */
-    public void onTaskFragmentAppeared(
-            @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
+    public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {}
 
     /** Called when the status of an organized TaskFragment is changed. */
     public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {}
@@ -169,7 +168,7 @@
 
     private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
         @Override
-        public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
+        public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
             mExecutor.execute(
                     () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
         }
diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml
index 2b9f952..304affe 100644
--- a/core/res/res/layout/splash_screen_view.xml
+++ b/core/res/res/layout/splash_screen_view.xml
@@ -36,6 +36,7 @@
           android:layout_marginBottom="60dp"
           android:padding="0dp"
           android:background="@null"
+          android:forceHasOverlappingRendering="false"
           android:contentDescription="@string/splash_screen_view_branding_description"/>
 
 </android.window.SplashScreenView>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5aff0e0..4d85164 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4879,9 +4879,10 @@
          the app in the letterbox mode. -->
     <item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item>
 
-    <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
-         min between device bottom corner radii will be used instead. -->
-    <integer name="config_letterboxActivityCornersRadius">-1</integer>
+    <!-- Corners radius for activity presented the letterbox mode. Values < 0 enable rounded
+         corners with radius equal to min between device bottom corner radii. Default 0 value turns
+         off rounded corners logic in LetterboxUiController. -->
+    <integer name="config_letterboxActivityCornersRadius">0</integer>
 
     <!-- Blur radius for the Option 3 in R.integer.config_letterboxBackgroundType. Values < 0 are
         ignored and 0 is used. -->
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 8ccf02ca..6bfbd8d 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -889,6 +889,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
     },
+    "-1145384901": {
+      "message": "shouldWaitAnimatingExit: isTransition: %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/WindowState.java"
+    },
     "-1142279614": {
       "message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
       "level": "VERBOSE",
@@ -1273,6 +1279,12 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-743856570": {
+      "message": "shouldWaitAnimatingExit: isAnimating: %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/WindowState.java"
+    },
     "-743431900": {
       "message": "Configuration no differences in %s",
       "level": "VERBOSE",
@@ -1777,6 +1789,12 @@
       "group": "WM_DEBUG_SYNC_ENGINE",
       "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
     },
+    "-208825711": {
+      "message": "shouldWaitAnimatingExit: isWallpaperTarget: %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/WindowState.java"
+    },
     "-198463978": {
       "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
       "level": "VERBOSE",
@@ -2989,6 +3007,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
     },
+    "1087494661": {
+      "message": "Clear window stuck on animatingExit status: %s",
+      "level": "WARN",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/WindowState.java"
+    },
     "1088929964": {
       "message": "onLockTaskPackagesUpdated: starting new locktask task=%s",
       "level": "DEBUG",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 85ef270..df751fc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -27,8 +27,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.util.ArrayMap;
-import android.view.SurfaceControl;
-import android.window.TaskFragmentAppearedInfo;
 import android.window.TaskFragmentCreationParams;
 import android.window.TaskFragmentInfo;
 import android.window.TaskFragmentOrganizer;
@@ -51,9 +49,6 @@
     /** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */
     private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
 
-    /** Mapping from the client assigned unique token to the TaskFragment {@link SurfaceControl}. */
-    private final Map<IBinder, SurfaceControl> mFragmentLeashes = new ArrayMap<>();
-
     /**
      * Mapping from the client assigned unique token to the TaskFragment parent
      * {@link Configuration}.
@@ -67,7 +62,7 @@
      * Callback that notifies the controller about changes to task fragments.
      */
     interface TaskFragmentCallback {
-        void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+        void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
         void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
         void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
         void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
@@ -259,15 +254,12 @@
     }
 
     @Override
-    public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
-        final TaskFragmentInfo info = taskFragmentAppearedInfo.getTaskFragmentInfo();
-        final IBinder fragmentToken = info.getFragmentToken();
-        final SurfaceControl leash = taskFragmentAppearedInfo.getLeash();
-        mFragmentInfos.put(fragmentToken, info);
-        mFragmentLeashes.put(fragmentToken, leash);
+    public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+        final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
+        mFragmentInfos.put(fragmentToken, taskFragmentInfo);
 
         if (mCallback != null) {
-            mCallback.onTaskFragmentAppeared(taskFragmentAppearedInfo);
+            mCallback.onTaskFragmentAppeared(taskFragmentInfo);
         }
     }
 
@@ -284,7 +276,6 @@
     @Override
     public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
         mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
-        mFragmentLeashes.remove(taskFragmentInfo.getFragmentToken());
         mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
 
         if (mCallback != null) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 68c1904..fe6c7ba 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -37,7 +37,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.window.TaskFragmentAppearedInfo;
 import android.window.TaskFragmentInfo;
 import android.window.WindowContainerTransaction;
 
@@ -110,14 +109,13 @@
     }
 
     @Override
-    public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
-        TaskFragmentContainer container = getContainer(
-                taskFragmentAppearedInfo.getTaskFragmentInfo().getFragmentToken());
+    public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+        TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
         if (container == null) {
             return;
         }
 
-        container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
+        container.setInfo(taskFragmentInfo);
         if (container.isFinished()) {
             mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
         }
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 75bc461..7f37036 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -29,6 +29,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.TaskInfo;
+import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.LocusId;
 import android.content.pm.ActivityInfo;
@@ -122,6 +123,16 @@
     }
 
     /**
+     * Callbacks for events in which the focus has changed.
+     */
+    public interface FocusListener {
+        /**
+         * Notifies when the task which is focused has changed.
+         */
+        void onFocusTaskChanged(RunningTaskInfo taskInfo);
+    }
+
+    /**
      * Keys map from either a task id or {@link TaskListenerType}.
      * @see #addListenerForTaskId
      * @see #addListenerForType
@@ -142,6 +153,8 @@
     /** @see #addLocusIdListener */
     private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>();
 
+    private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>();
+
     private final Object mLock = new Object();
     private StartingWindowController mStartingWindow;
 
@@ -155,6 +168,9 @@
     @Nullable
     private final Optional<RecentTasksController> mRecentTasks;
 
+    @Nullable
+    private RunningTaskInfo mLastFocusedTaskInfo;
+
     public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
         this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
                 Optional.empty() /* recentTasksController */);
@@ -331,6 +347,27 @@
         }
     }
 
+    /**
+     * Adds a listener to be notified for task focus changes.
+     */
+    public void addFocusListener(FocusListener listener) {
+        synchronized (mLock) {
+            mFocusListeners.add(listener);
+            if (mLastFocusedTaskInfo != null) {
+                listener.onFocusTaskChanged(mLastFocusedTaskInfo);
+            }
+        }
+    }
+
+    /**
+     * Removes listener.
+     */
+    public void removeLocusIdListener(FocusListener listener) {
+        synchronized (mLock) {
+            mFocusListeners.remove(listener);
+        }
+    }
+
     @Override
     public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
         if (mStartingWindow != null) {
@@ -422,6 +459,18 @@
                 mRecentTasks.ifPresent(recentTasks ->
                         recentTasks.onTaskWindowingModeChanged(taskInfo));
             }
+            // TODO (b/207687679): Remove check for HOME once bug is fixed
+            final boolean isFocusedOrHome = taskInfo.isFocused
+                    || (taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME
+                    && taskInfo.isVisible);
+            final boolean focusTaskChanged = (mLastFocusedTaskInfo == null
+                    || mLastFocusedTaskInfo.taskId != taskInfo.taskId) && isFocusedOrHome;
+            if (focusTaskChanged) {
+                for (int i = 0; i < mFocusListeners.size(); i++) {
+                    mFocusListeners.valueAt(i).onFocusTaskChanged(taskInfo);
+                }
+                mLastFocusedTaskInfo = taskInfo;
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index d07fff3..3579bf4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -321,6 +321,16 @@
         mSplitLayoutHandler.onLayoutSizeChanged(this);
     }
 
+    /** Sets divide position base on the ratio within root bounds. */
+    public void setDivideRatio(float ratio) {
+        final int position = isLandscape()
+                ? mRootBounds.left + (int) (mRootBounds.width() * ratio)
+                : mRootBounds.top + (int) (mRootBounds.height() * ratio);
+        DividerSnapAlgorithm.SnapTarget snapTarget =
+                mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+        setDividePosition(snapTarget.position);
+    }
+
     /** Resets divider position. */
     public void resetDividerPosition() {
         mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index d239e56..6b62204 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -337,7 +337,7 @@
     @Provides
     static Optional<OneHandedController> providesOneHandedController(
             @DynamicOverride Optional<OneHandedController> oneHandedController) {
-        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+        if (SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
             return oneHandedController;
         }
         return Optional.empty();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 46c7b50..f562fd9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -241,10 +241,11 @@
     static PhonePipMenuController providesPipPhoneMenuController(Context context,
             PipBoundsState pipBoundsState, PipMediaController pipMediaController,
             SystemWindows systemWindows,
+            Optional<SplitScreenController> splitScreenOptional,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
         return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
-                systemWindows, mainExecutor, mainHandler);
+                systemWindows, splitScreenOptional, mainExecutor, mainHandler);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 5c8e7d0..52ff21b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.freeform;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 
@@ -28,7 +27,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.SurfaceControl;
-import android.window.WindowContainerTransaction;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -85,13 +83,6 @@
             Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
             return;
         }
-
-        // Clears windowing mode and window bounds to let the task inherits from its new parent.
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setBounds(taskInfo.token, null)
-                .setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
-        mSyncQueue.queue(wct);
-
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
                 taskInfo.taskId);
         mTasks.remove(taskInfo.taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index 8d9ad4d..caa1f01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -90,6 +90,11 @@
     default void updateMenuBounds(Rect destinationBounds) {}
 
     /**
+     * Update when the current focused task changes.
+     */
+    default void onFocusTaskChanged(RunningTaskInfo taskInfo) {}
+
+    /**
      * Returns a default LayoutParams for the PIP Menu.
      * @param width the PIP stack width.
      * @param height the PIP stack height.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 6cc5f09..c2ebc30 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -98,7 +98,7 @@
  * see also {@link PipMotionHelper}.
  */
 public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
-        DisplayController.OnDisplaysChangedListener {
+        DisplayController.OnDisplaysChangedListener, ShellTaskOrganizer.FocusListener {
     private static final String TAG = PipTaskOrganizer.class.getSimpleName();
     private static final boolean DEBUG = false;
     /**
@@ -286,6 +286,7 @@
         mMainExecutor.execute(() -> {
             mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
         });
+        mTaskOrganizer.addFocusListener(this);
         mPipTransitionController.setPipOrganizer(this);
         displayController.addDisplayWindowListener(this);
     }
@@ -772,6 +773,11 @@
     }
 
     @Override
+    public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        mPipMenuController.onFocusTaskChanged(taskInfo);
+    }
+
+    @Override
     public boolean supportSizeCompatUI() {
         // PIP doesn't support size compat.
         return false;
@@ -1282,6 +1288,9 @@
     }
 
     private boolean isPipTopLeft() {
+        if (!mSplitScreenOptional.isPresent()) {
+            return false;
+        }
         final Rect topLeft = new Rect();
         final Rect bottomRight = new Rect();
         mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 5687f4d..eb512af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -43,10 +44,12 @@
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipMediaController.ActionListener;
 import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Manages the PiP menu view which can show menu options or a scrim.
@@ -114,6 +117,7 @@
 
     private final ArrayList<Listener> mListeners = new ArrayList<>();
     private final SystemWindows mSystemWindows;
+    private final Optional<SplitScreenController> mSplitScreenController;
     private ParceledListSlice<RemoteAction> mAppActions;
     private ParceledListSlice<RemoteAction> mMediaActions;
     private SyncRtSurfaceTransactionApplier mApplier;
@@ -145,6 +149,7 @@
 
     public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
             PipMediaController mediaController, SystemWindows systemWindows,
+            Optional<SplitScreenController> splitScreenOptional,
             ShellExecutor mainExecutor, Handler mainHandler) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
@@ -152,6 +157,7 @@
         mSystemWindows = systemWindows;
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
+        mSplitScreenController = splitScreenOptional;
     }
 
     public boolean isMenuVisible() {
@@ -180,7 +186,8 @@
         if (mPipMenuView != null) {
             detachPipMenuView();
         }
-        mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler);
+        mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
+                mSplitScreenController);
         mSystemWindows.addView(mPipMenuView,
                 getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
                 0, SHELL_ROOT_LAYER_PIP);
@@ -209,6 +216,13 @@
         updateMenuLayout(destinationBounds);
     }
 
+    @Override
+    public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        if (mPipMenuView != null) {
+            mPipMenuView.onFocusTaskChanged(taskInfo);
+        }
+    }
+
     /**
      * Tries to grab a surface control from {@link PipMenuView}. If this isn't available for some
      * reason (ie. the window isn't ready yet, thus {@link android.view.ViewRootImpl} is
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index b209699..82e8273 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip.phone;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
@@ -32,8 +33,10 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.app.ActivityManager;
 import android.app.PendingIntent.CanceledException;
 import android.app.RemoteAction;
+import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -61,11 +64,13 @@
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Translucent window that gets started on top of a task in PIP to allow the user to control it.
@@ -105,6 +110,7 @@
     private boolean mAllowMenuTimeout = true;
     private boolean mAllowTouches = true;
     private int mDismissFadeOutDurationMs;
+    private boolean mFocusedTaskAllowSplitScreen;
 
     private final List<RemoteAction> mActions = new ArrayList<>();
 
@@ -116,6 +122,7 @@
 
     private AnimatorSet mMenuContainerAnimator;
     private PhonePipMenuController mController;
+    private Optional<SplitScreenController> mSplitScreenControllerOptional;
 
     private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
             new ValueAnimator.AnimatorUpdateListener() {
@@ -144,12 +151,14 @@
     protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
 
     public PipMenuView(Context context, PhonePipMenuController controller,
-            ShellExecutor mainExecutor, Handler mainHandler) {
+            ShellExecutor mainExecutor, Handler mainHandler,
+            Optional<SplitScreenController> splitScreenController) {
         super(context, null, 0);
         mContext = context;
         mController = controller;
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
+        mSplitScreenControllerOptional = splitScreenController;
 
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         inflate(context, R.layout.pip_menu, this);
@@ -255,6 +264,15 @@
         return super.dispatchGenericMotionEvent(event);
     }
 
+    public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        final boolean isSplitScreen = mSplitScreenControllerOptional.isPresent()
+                && mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId);
+        mFocusedTaskAllowSplitScreen = isSplitScreen
+                || (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                && taskInfo.supportsSplitScreenMultiWindow
+                && taskInfo.topActivityType != WindowConfiguration.ACTIVITY_TYPE_HOME);
+    }
+
     void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
             boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
         mAllowMenuTimeout = allowMenuTimeout;
@@ -278,7 +296,8 @@
             ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
                     mDismissButton.getAlpha(), 1f);
             ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
-                    mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
+                    mEnterSplitButton.getAlpha(),
+                    ENABLE_ENTER_SPLIT && mFocusedTaskAllowSplitScreen ? 1f : 0f);
             if (menuState == MENU_STATE_FULL) {
                 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
                         enterSplitAnim);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 3d3a630..4a99097 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -83,14 +83,15 @@
      * Starts tasks simultaneously in one transition.
      */
     oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
-            in Bundle sideOptions, int sidePosition, in RemoteTransition remoteTransition) = 10;
+            in Bundle sideOptions, int sidePosition, float splitRatio,
+            in RemoteTransition remoteTransition) = 10;
 
     /**
      * Version of startTasks using legacy transition system.
      */
      oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
                             int sideTaskId, in Bundle sideOptions, int sidePosition,
-                            in RemoteAnimationAdapter adapter) = 11;
+                            float splitRatio, in RemoteAnimationAdapter adapter) = 11;
 
     /**
      * Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 6b42ed7..262a9a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -183,6 +183,11 @@
         return mStageCoordinator.isSplitScreenVisible();
     }
 
+    public boolean isTaskInSplitScreen(int taskId) {
+        return isSplitScreenVisible()
+                && mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
+    }
+
     public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
         return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
                 new WindowContainerTransaction());
@@ -605,21 +610,21 @@
         @Override
         public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
                 int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
-                RemoteAnimationAdapter adapter) {
+                float splitRatio, RemoteAnimationAdapter adapter) {
             executeRemoteCallWithTaskPermission(mController, "startTasks",
                     (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
                             mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
-                            adapter));
+                            splitRatio, adapter));
         }
 
         @Override
         public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
                 int sideTaskId, @Nullable Bundle sideOptions,
-                @SplitPosition int sidePosition,
+                @SplitPosition int sidePosition, float splitRatio,
                 @Nullable RemoteTransition remoteTransition) {
             executeRemoteCallWithTaskPermission(mController, "startTasks",
                     (controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
-                            sideTaskId, sideOptions, sidePosition, remoteTransition));
+                            sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition));
         }
 
         @Override
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 a3726d4..6280f76 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
@@ -274,6 +274,17 @@
         return mSideStageListener.mVisible && mMainStageListener.mVisible;
     }
 
+    @SplitScreen.StageType
+    int getStageOfTask(int taskId) {
+        if (mMainStage.containsTask(taskId)) {
+            return STAGE_TYPE_MAIN;
+        } else if (mSideStage.containsTask(taskId)) {
+            return STAGE_TYPE_SIDE;
+        }
+
+        return STAGE_TYPE_UNDEFINED;
+    }
+
     boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitScreen.StageType int stageType,
             @SplitPosition int stagePosition, WindowContainerTransaction wct) {
         StageTaskListener targetStage;
@@ -322,7 +333,7 @@
 
     /** Starts 2 tasks in one transition. */
     void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
-            @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+            @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
             @Nullable RemoteTransition remoteTransition) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -334,6 +345,7 @@
         mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
         mSideStage.setBounds(getSideStageBounds(), wct);
 
+        mSplitLayout.setDivideRatio(splitRatio);
         // Make sure the launch options will put tasks in the corresponding split roots
         addActivityOptions(mainOptions, mMainStage);
         addActivityOptions(sideOptions, mSideStage);
@@ -349,7 +361,7 @@
     /** Starts 2 tasks in one legacy transition. */
     void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
             int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
-            RemoteAnimationAdapter adapter) {
+            float splitRatio, RemoteAnimationAdapter adapter) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         // Need to add another wrapper here in shell so that we can inject the divider bar
         // and also manage the process elevation via setRunningRemote
@@ -404,6 +416,7 @@
         sideOptions = sideOptions != null ? sideOptions : new Bundle();
         setSideStagePosition(sidePosition, wct);
 
+        mSplitLayout.setDivideRatio(splitRatio);
         // Build a request WCT that will launch both apps such that task 0 is on the main stage
         // while task 1 is on the side stage.
         mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 003d8a3..e7b5744 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -72,6 +72,7 @@
     private final int mAppRevealDuration;
     private final int mAnimationDuration;
     private final float mIconStartAlpha;
+    private final float mBrandingStartAlpha;
     private final TransactionPool mTransactionPool;
 
     private ValueAnimator mMainAnimator;
@@ -94,9 +95,17 @@
                 || iconView.getLayoutParams().height == 0) {
             mIconFadeOutDuration = 0;
             mIconStartAlpha = 0;
+            mBrandingStartAlpha = 0;
             mAppRevealDelay = 0;
         } else {
             iconView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            // The branding view could only exists when the icon is present.
+            final View brandingView = view.getBrandingView();
+            if (brandingView != null) {
+                mBrandingStartAlpha = brandingView.getAlpha();
+            } else {
+                mBrandingStartAlpha = 0;
+            }
             mIconFadeOutDuration = context.getResources().getInteger(
                     R.integer.starting_window_app_reveal_icon_fade_out_duration);
             mAppRevealDelay = context.getResources().getInteger(
@@ -334,13 +343,21 @@
         // ignore
     }
 
-    private void onAnimationProgress(float linearProgress) {
-        View iconView = mSplashScreenView.getIconView();
+    private void onFadeOutProgress(float linearProgress) {
+        final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
+                getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
+        final View iconView = mSplashScreenView.getIconView();
+        final View brandingView = mSplashScreenView.getBrandingView();
         if (iconView != null) {
-            final float iconProgress = ICON_INTERPOLATOR.getInterpolation(
-                    getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration));
             iconView.setAlpha(mIconStartAlpha * (1 - iconProgress));
         }
+        if (brandingView != null) {
+            brandingView.setAlpha(mBrandingStartAlpha * (1 - iconProgress));
+        }
+    }
+
+    private void onAnimationProgress(float linearProgress) {
+        onFadeOutProgress(linearProgress);
 
         final float revealLinearProgress = getProgress(linearProgress, mAppRevealDelay,
                 mAppRevealDuration);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 73eebad..453050f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -106,6 +106,12 @@
     }
 
     @Test
+    public void testSetDivideRatio() {
+        mSplitLayout.setDivideRatio(0.5f);
+        verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
+    }
+
+    @Test
     public void testOnDoubleTappedDivider() {
         mSplitLayout.onDoubleTappedDivider();
         verify(mSplitLayoutHandler).onDoubleTappedDivider();
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 9aad278..e5726b0 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -27,7 +27,6 @@
 import android.util.Log
 import android.util.MathUtils
 import android.view.GhostView
-import android.view.Gravity
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewTreeObserver.OnPreDrawListener
@@ -87,10 +86,11 @@
         // If the parent of the view we are launching from is the background of some other animated
         // dialog, then this means the caller intent is to launch a dialog from another dialog. In
         // this case, we also animate the parent (which is the dialog background).
-        val animatedParent = openedDialogs
-            .firstOrNull { it.dialogContentParent == view.parent }
-        val parentHostDialog = animatedParent?.hostDialog
-        val animateFrom = animatedParent?.dialogContentParent ?: view
+        val animatedParent = openedDialogs.firstOrNull {
+            it.dialogContentWithBackground == view || it.dialogContentWithBackground == view.parent
+        }
+        val dialogContentWithBackground = animatedParent?.dialogContentWithBackground
+        val animateFrom = dialogContentWithBackground ?: view
 
         // Make sure we don't run the launch animation from the same view twice at the same time.
         if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
@@ -109,7 +109,7 @@
                 onDialogDismissed = { openedDialogs.remove(it) },
                 originalDialog = dialog,
                 animateBackgroundBoundsChange,
-                openedDialogs.firstOrNull { it.hostDialog == parentHostDialog }
+                animatedParent
         )
         val hostDialog = animatedDialog.hostDialog
         openedDialogs.add(animatedDialog)
@@ -288,13 +288,12 @@
     private val hostDialogRoot = FrameLayout(context)
 
     /**
-     * The parent of the original dialog content view, that serves as a fake window that will have
-     * the same size as the original dialog window and to which we will set the original dialog
-     * window background.
+     * The dialog content with its background. When animating a fullscreen dialog, this is just the
+     * first ViewGroup of the dialog that has a background. When animating a normal (not fullscreen)
+     * dialog, this is an additional view that serves as a fake window that will have the same size
+     * as the original dialog window and to which we will set the original dialog window background.
      */
-    val dialogContentParent = FrameLayout(context).apply {
-        id = DIALOG_CONTENT_PARENT_ID
-    }
+    var dialogContentWithBackground: ViewGroup? = null
 
     /**
      * The background color of [originalDialogView], taking into consideration the [originalDialog]
@@ -451,59 +450,87 @@
         hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
         dialogView.isClickable = true
 
-        // Set the background of the window dialog to the dialog itself.
-        // TODO(b/193634619): Support dialog windows without background.
-        // TODO(b/193634619): Support dialog whose background comes from the content view instead of
-        // the window.
-        val typedArray =
-            originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window)
-        val backgroundRes =
-            typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0)
-        typedArray.recycle()
-        if (backgroundRes == 0) {
-            throw IllegalStateException("Dialogs with no backgrounds on window are not supported")
+        // Remove the original dialog view from its parent.
+        (dialogView.parent as? ViewGroup)?.removeView(dialogView)
+
+        val originalDialogWindow = originalDialog.window!!
+        val isOriginalWindowFullScreen =
+            originalDialogWindow.attributes.width == ViewGroup.LayoutParams.MATCH_PARENT &&
+            originalDialogWindow.attributes.height == ViewGroup.LayoutParams.MATCH_PARENT
+        if (isOriginalWindowFullScreen) {
+            // If the original dialog window is fullscreen, then we look for the first ViewGroup
+            // that has a background and animate towards that ViewGroup given that this is probably
+            // what represents the actual dialog view.
+            dialogContentWithBackground = findFirstViewGroupWithBackground(dialogView)
+                ?: throw IllegalStateException("Unable to find ViewGroup with background")
+
+            hostDialogRoot.addView(
+                dialogView,
+
+                FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT
+                )
+            )
+        } else {
+            // Add a parent view to the original dialog view to which we will set the original
+            // dialog window background. This View serves as a fake window with background, so that
+            // we are sure that we don't override the original dialog content view paddings with the
+            // window background that usually has insets.
+            dialogContentWithBackground = FrameLayout(context).apply {
+                id = DIALOG_CONTENT_PARENT_ID
+
+                // TODO(b/193634619): Support dialog windows without background.
+                background = originalDialogWindow.decorView?.background
+                    ?: throw IllegalStateException(
+                        "Dialogs with no backgrounds on window are not supported")
+
+                addView(
+                    dialogView,
+
+                    // It should match its parent size, which is sized the same as the original
+                    // dialog window.
+                    FrameLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT
+                    )
+                )
+            }
+
+            // Add the parent (that has the background) to the host window.
+            hostDialogRoot.addView(
+                dialogContentWithBackground,
+
+                // We give it the size and gravity of its original dialog window.
+                FrameLayout.LayoutParams(
+                    originalDialogWindow.attributes.width,
+                    originalDialogWindow.attributes.height,
+                    originalDialogWindow.attributes.gravity
+                )
+            )
         }
 
-        // Add a parent view to the original dialog view to which we will set the original dialog
-        // window background. This View serves as a fake window with background, so that we are sure
-        // that we don't override the dialog view paddings with the window background that usually
-        // has insets.
-        dialogContentParent.setBackgroundResource(backgroundRes)
-        hostDialogRoot.addView(
-            dialogContentParent,
+        val dialogContentWithBackground = this.dialogContentWithBackground!!
 
-            // We give it the size of its original dialog window.
-            FrameLayout.LayoutParams(
-                originalDialog.window.attributes.width,
-                originalDialog.window.attributes.height,
-                Gravity.CENTER
-            )
-        )
+        // Make the dialog and its background invisible for now, to make sure it's not drawn yet.
+        dialogContentWithBackground.visibility = View.INVISIBLE
 
-        // Make the dialog view parent invisible for now, to make sure it's not drawn yet.
-        dialogContentParent.visibility = View.INVISIBLE
-
-        val background = dialogContentParent.background!!
+        val background = dialogContentWithBackground.background!!
         originalDialogBackgroundColor =
             GhostedViewLaunchAnimatorController.findGradientDrawable(background)
                 ?.color
                 ?.defaultColor ?: Color.BLACK
 
-        // Add the dialog view to its parent (that has the original window background).
-        (dialogView.parent as? ViewGroup)?.removeView(dialogView)
-        dialogContentParent.addView(
-            dialogView,
-
-            // It should match its parent size, which is sized the same as the original dialog
-            // window.
-            FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT
-            )
-        )
+        if (isOriginalWindowFullScreen) {
+            // If the original window is full screen, the ViewGroup with background might already be
+            // correctly laid out. Make sure we relayout and that the layout listener below is still
+            // called.
+            dialogContentWithBackground.layout(0, 0, 0, 0)
+            dialogContentWithBackground.requestLayout()
+        }
 
         // Start the animation when the dialog is laid out in the center of the host dialog.
-        dialogContentParent.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+        dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
             override fun onLayoutChange(
                 view: View,
                 left: Int,
@@ -515,7 +542,7 @@
                 oldRight: Int,
                 oldBottom: Int
             ) {
-                dialogContentParent.removeOnLayoutChangeListener(this)
+                dialogContentWithBackground.removeOnLayoutChangeListener(this)
 
                 isOriginalDialogViewLaidOut = true
                 maybeStartLaunchAnimation()
@@ -523,6 +550,25 @@
         })
     }
 
+    private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
+        if (view !is ViewGroup) {
+            return null
+        }
+
+        if (view.background != null) {
+            return view
+        }
+
+        for (i in 0 until view.childCount) {
+            val match = findFirstViewGroupWithBackground(view.getChildAt(i))
+            if (match != null) {
+                return match
+            }
+        }
+
+        return null
+    }
+
     fun onOriginalDialogSizeChanged() {
         // The dialog is the single child of the root.
         if (hostDialogRoot.childCount != 1) {
@@ -571,7 +617,8 @@
                 // at the end of the launch animation, because the lauch animation already correctly
                 // handles bounds changes.
                 if (backgroundLayoutListener != null) {
-                    dialogContentParent.addOnLayoutChangeListener(backgroundLayoutListener)
+                    dialogContentWithBackground!!
+                        .addOnLayoutChangeListener(backgroundLayoutListener)
                 }
             }
         )
@@ -638,10 +685,12 @@
                 (touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
 
                 touchSurface.visibility = View.VISIBLE
-                dialogContentParent.visibility = View.INVISIBLE
+                val dialogContentWithBackground = this.dialogContentWithBackground!!
+                dialogContentWithBackground.visibility = View.INVISIBLE
 
                 if (backgroundLayoutListener != null) {
-                    dialogContentParent.removeOnLayoutChangeListener(backgroundLayoutListener)
+                    dialogContentWithBackground
+                        .removeOnLayoutChangeListener(backgroundLayoutListener)
                 }
 
                 // The animated ghost was just removed. We create a temporary ghost that will be
@@ -674,8 +723,8 @@
     ) {
         // Create 2 ghost controllers to animate both the dialog and the touch surface in the host
         // dialog.
-        val startView = if (isLaunching) touchSurface else dialogContentParent
-        val endView = if (isLaunching) dialogContentParent else touchSurface
+        val startView = if (isLaunching) touchSurface else dialogContentWithBackground!!
+        val endView = if (isLaunching) dialogContentWithBackground!! else touchSurface
         val startViewController = GhostedViewLaunchAnimatorController(startView)
         val endViewController = GhostedViewLaunchAnimatorController(endView)
         startViewController.launchContainer = hostDialogRoot
@@ -736,7 +785,9 @@
     }
 
     private fun shouldAnimateDialogIntoView(): Boolean {
-        if (exitAnimationDisabled) {
+        // Don't animate if the dialog was previously hidden using hide() (either on the host dialog
+        // or on the original dialog) or if we disabled the exit animation.
+        if (exitAnimationDisabled || !hostDialog.isShowing) {
             return false
         }
 
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/packages/SystemUI/res/color/prv_text_color_on_accent.xml
deleted file mode 100644
index 9f44aca..0000000
--- a/packages/SystemUI/res/color/prv_text_color_on_accent.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-          xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="?androidprv:attr/textColorOnAccent" />
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
index 1a128df..14cb1de9 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
@@ -16,8 +16,8 @@
   -->
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-       android:insetTop="@dimen/qs_dialog_button_vertical_inset"
-       android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+       android:insetTop="@dimen/dialog_button_vertical_inset"
+       android:insetBottom="@dimen/dialog_button_vertical_inset">
     <ripple android:color="?android:attr/colorControlHighlight">
         <item android:id="@android:id/mask">
             <shape android:shape="rectangle">
@@ -29,10 +29,10 @@
             <shape android:shape="rectangle">
                 <corners android:radius="?android:attr/buttonCornerRadius"/>
                 <solid android:color="?androidprv:attr/colorAccentPrimary"/>
-                <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
-                         android:top="@dimen/qs_dialog_button_vertical_padding"
-                         android:right="@dimen/qs_dialog_button_horizontal_padding"
-                         android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+                <padding android:left="@dimen/dialog_button_horizontal_padding"
+                         android:top="@dimen/dialog_button_vertical_padding"
+                         android:right="@dimen/dialog_button_horizontal_padding"
+                         android:bottom="@dimen/dialog_button_vertical_padding"/>
             </shape>
         </item>
     </ripple>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index 467c20f..665b456 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -16,8 +16,8 @@
   -->
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-       android:insetTop="@dimen/qs_dialog_button_vertical_inset"
-       android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+       android:insetTop="@dimen/dialog_button_vertical_inset"
+       android:insetBottom="@dimen/dialog_button_vertical_inset">
     <ripple android:color="?android:attr/colorControlHighlight">
         <item android:id="@android:id/mask">
             <shape android:shape="rectangle">
@@ -32,10 +32,10 @@
                 <stroke android:color="?androidprv:attr/colorAccentPrimary"
                         android:width="1dp"
                 />
-                <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
-                         android:top="@dimen/qs_dialog_button_vertical_padding"
-                         android:right="@dimen/qs_dialog_button_horizontal_padding"
-                         android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+                <padding android:left="@dimen/dialog_button_horizontal_padding"
+                         android:top="@dimen/dialog_button_vertical_padding"
+                         android:right="@dimen/dialog_button_horizontal_padding"
+                         android:bottom="@dimen/dialog_button_vertical_padding"/>
             </shape>
         </item>
     </ripple>
diff --git a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
new file mode 100644
index 0000000..a3e289a
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@*android:id/buttonPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:scrollbarAlwaysDrawVerticalTrack="true"
+    android:scrollIndicators="top|bottom"
+    android:fillViewport="true"
+    android:paddingTop="@dimen/dialog_button_bar_top_padding"
+    android:paddingStart="@dimen/dialog_side_padding"
+    android:paddingEnd="@dimen/dialog_side_padding"
+    android:paddingBottom="@dimen/dialog_bottom_padding"
+    style="?android:attr/buttonBarStyle">
+    <com.android.internal.widget.ButtonBarLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layoutDirection="locale"
+        android:orientation="horizontal"
+        android:gravity="bottom">
+
+        <Button
+            android:id="@android:id/button3"
+            style="?android:attr/buttonBarNeutralButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <Space
+            android:id="@*android:id/spacer"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:visibility="invisible" />
+
+        <Button
+            android:id="@android:id/button2"
+            style="?android:attr/buttonBarNegativeButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <Button
+            android:id="@android:id/button1"
+            style="?android:attr/buttonBarPositiveButtonStyle"
+            android:layout_marginStart="8dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+    </com.android.internal.widget.ButtonBarLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
new file mode 100644
index 0000000..f280cbd
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<com.android.internal.widget.AlertDialogLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@*android:id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_horizontal|top"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/dialog_top_padding"
+    >
+
+    <include layout="@layout/alert_dialog_title_systemui" />
+
+    <FrameLayout
+        android:id="@*android:id/contentPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="48dp"
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding"
+        >
+
+        <ScrollView
+            android:id="@*android:id/scrollView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:clipToPadding="false">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <Space
+                    android:id="@*android:id/textSpacerNoTitle"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp" />
+
+                <TextView
+                    android:id="@*android:id/message"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/TextAppearance.Dialog.Body.Message" />
+
+                <Space
+                    android:id="@*android:id/textSpacerNoButtons"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="6dp" />
+            </LinearLayout>
+        </ScrollView>
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@*android:id/customPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="48dp"
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding"
+        >
+
+        <FrameLayout
+            android:id="@*android:id/custom"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </FrameLayout>
+
+    <include
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        layout="@layout/alert_dialog_button_bar_systemui" />
+
+</com.android.internal.widget.AlertDialogLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
new file mode 100644
index 0000000..480ba00
--- /dev/null
+++ b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:id="@*android:id/topPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingStart="@dimen/dialog_side_padding"
+    android:paddingEnd="@dimen/dialog_side_padding"
+>
+
+    <!-- If the client uses a customTitle, it will be added here. -->
+
+    <LinearLayout
+        android:id="@*android:id/title_template"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:gravity="center_horizontal|top">
+
+        <ImageView
+            android:id="@*android:id/icon"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_marginBottom="16dp"
+            android:scaleType="fitCenter"
+            android:src="@null"
+            android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+            />
+
+        <com.android.internal.widget.DialogTitle
+            android:id="@*android:id/alertTitle"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="16dp"
+            style="@style/TextAppearance.Dialog.Title" />
+    </LinearLayout>
+
+    <Space
+        android:id="@*android:id/titleDividerNoCustom"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="0dp" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 86e2661..4a5b637 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -156,6 +156,14 @@
                             style="@style/InternetDialog.NetworkSummary"/>
                     </LinearLayout>
 
+                    <View
+                        android:id="@+id/mobile_toggle_divider"
+                        android:layout_width="1dp"
+                        android:layout_height="28dp"
+                        android:layout_marginEnd="16dp"
+                        android:layout_gravity="center_vertical"
+                        android:background="?android:attr/textColorSecondary"/>
+
                     <FrameLayout
                         android:layout_width="@dimen/settingslib_switch_track_width"
                         android:layout_height="48dp"
@@ -177,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">
@@ -219,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"
@@ -241,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">
@@ -367,8 +375,9 @@
                 android:id="@+id/done_layout"
                 android:layout_width="67dp"
                 android:layout_height="48dp"
+                android:layout_marginTop="8dp"
                 android:layout_marginEnd="24dp"
-                android:layout_marginBottom="40dp"
+                android:layout_marginBottom="34dp"
                 android:layout_gravity="end|center_vertical"
                 android:clickable="true"
                 android:focusable="true">
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/privacy_dialog.xml b/packages/SystemUI/res/layout/privacy_dialog.xml
index ee4530c..9368a6a 100644
--- a/packages/SystemUI/res/layout/privacy_dialog.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog.xml
@@ -22,10 +22,9 @@
     android:layout_height="wrap_content"
     android:layout_marginStart="@dimen/ongoing_appops_dialog_side_margins"
     android:layout_marginEnd="@dimen/ongoing_appops_dialog_side_margins"
-    android:layout_marginTop="8dp"
     android:orientation="vertical"
     android:paddingBottom="8dp"
-    android:paddingTop="12dp"
+    android:paddingTop="8dp"
     android:paddingHorizontal="@dimen/ongoing_appops_dialog_side_padding"
     android:background="@drawable/qs_dialog_bg"
 />
diff --git a/packages/SystemUI/res/layout/qs_user_detail.xml b/packages/SystemUI/res/layout/qs_user_detail.xml
index 91d3a53..1aec296 100644
--- a/packages/SystemUI/res/layout/qs_user_detail.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail.xml
@@ -22,6 +22,6 @@
         xmlns:sysui="http://schemas.android.com/apk/res-auto"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        sysui:verticalSpacing="4dp"
+        sysui:verticalSpacing="20dp"
         sysui:horizontalSpacing="4dp"
         style="@style/UserDetailView" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index cc6c5d3..91b11fc 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -24,8 +24,6 @@
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:gravity="top|center_horizontal"
-        android:paddingTop="16dp"
-        android:minHeight="112dp"
         android:clipChildren="false"
         android:clipToPadding="false"
         android:focusable="true"
diff --git a/packages/SystemUI/res/layout/qs_user_dialog_content.xml b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
index 9495ee6..355df2c 100644
--- a/packages/SystemUI/res/layout/qs_user_dialog_content.xml
+++ b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
@@ -15,75 +15,19 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<androidx.constraintlayout.widget.ConstraintLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:sysui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="24dp"
-    android:layout_marginStart="16dp"
-    android:layout_marginEnd="16dp"
->
-    <TextView
-        android:id="@+id/title"
-        android:layout_height="wrap_content"
-        android:layout_width="0dp"
-        android:textAlignment="center"
-        android:text="@string/qs_user_switch_dialog_title"
-        android:textAppearance="@style/TextAppearance.QSDialog.Title"
-        android:layout_marginBottom="32dp"
-        sysui:layout_constraintTop_toTopOf="parent"
-        sysui:layout_constraintStart_toStartOf="parent"
-        sysui:layout_constraintEnd_toEndOf="parent"
-        sysui:layout_constraintBottom_toTopOf="@id/grid"
-        />
-
+    >
     <com.android.systemui.qs.PseudoGridView
-        android:id="@+id/grid"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="28dp"
-        sysui:verticalSpacing="4dp"
-        sysui:horizontalSpacing="4dp"
-        sysui:fixedChildWidth="80dp"
-        sysui:layout_constraintTop_toBottomOf="@id/title"
-        sysui:layout_constraintStart_toStartOf="parent"
-        sysui:layout_constraintEnd_toEndOf="parent"
-        sysui:layout_constraintBottom_toTopOf="@id/barrier"
-    />
-
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/barrier"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        sysui:barrierDirection="top"
-        sysui:constraint_referenced_ids="settings,done"
+            android:id="@+id/grid"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            sysui:verticalSpacing="20dp"
+            sysui:horizontalSpacing="4dp"
+            sysui:fixedChildWidth="80dp"
         />
-
-    <Button
-        android:id="@+id/settings"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:text="@string/quick_settings_more_user_settings"
-        sysui:layout_constraintTop_toBottomOf="@id/barrier"
-        sysui:layout_constraintBottom_toBottomOf="parent"
-        sysui:layout_constraintStart_toStartOf="parent"
-        sysui:layout_constraintEnd_toStartOf="@id/done"
-        sysui:layout_constraintHorizontal_chainStyle="spread_inside"
-        style="@style/Widget.QSDialog.Button.BorderButton"
-        />
-
-    <Button
-        android:id="@+id/done"
-        android:layout_width="wrap_content"
-        android:layout_height="48dp"
-        android:text="@string/quick_settings_done"
-        sysui:layout_constraintTop_toBottomOf="@id/barrier"
-        sysui:layout_constraintBottom_toBottomOf="parent"
-        sysui:layout_constraintStart_toEndOf="@id/settings"
-        sysui:layout_constraintEnd_toEndOf="parent"
-        style="@style/Widget.QSDialog.Button"
-        />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index e43a149..6c5ad50 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -93,41 +93,6 @@
                         android:id="@+id/screenrecord_audio_switch"
                         style="@style/ScreenRecord.Switch"/>
                 </LinearLayout>
-
-                <LinearLayout
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:orientation="horizontal"
-                    android:layout_marginTop="@dimen/screenrecord_option_padding">
-                    <ImageView
-                        android:layout_width="@dimen/screenrecord_option_icon_size"
-                        android:layout_height="@dimen/screenrecord_option_icon_size"
-                        android:layout_weight="0"
-                        android:src="@drawable/ic_touch"
-                        android:tint="?android:attr/textColorSecondary"
-                        android:layout_gravity="center"
-                        android:layout_marginRight="@dimen/screenrecord_option_padding"/>
-                    <TextView
-                        android:layout_width="0dp"
-                        android:layout_height="wrap_content"
-                        android:minHeight="48dp"
-                        android:layout_weight="1"
-                        android:layout_gravity="fill_vertical"
-                        android:gravity="center_vertical"
-                        android:text="@string/screenrecord_taps_label"
-                        android:textAppearance="?android:attr/textAppearanceMedium"
-                        android:fontFamily="@*android:string/config_headlineFontFamily"
-                        android:textColor="?android:attr/textColorPrimary"
-                        android:importantForAccessibility="no"/>
-                    <Switch
-                        android:layout_width="wrap_content"
-                        android:minWidth="48dp"
-                        android:layout_height="48dp"
-                        android:layout_weight="0"
-                        android:id="@+id/screenrecord_taps_switch"
-                        android:contentDescription="@string/screenrecord_taps_label"
-                        style="@style/ScreenRecord.Switch"/>
-                </LinearLayout>
             </LinearLayout>
 
             <!-- Buttons -->
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 07e28b6..cb963e6 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -16,9 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog">
-        <item name="android:buttonCornerRadius">28dp</item>
-    </style>
+    <style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Dialog"/>
 
     <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" />
 
@@ -53,13 +51,17 @@
     <style name="TextAppearance.InternetDialog.Active">
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:textSize">16sp</item>
-        <item name="android:textColor">@color/connected_network_primary_color</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
         <item name="android:textDirection">locale</item>
     </style>
 
     <style name="TextAppearance.InternetDialog.Secondary.Active">
         <item name="android:textSize">14sp</item>
-        <item name="android:textColor">@color/connected_network_secondary_color</item>
+        <item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
+    </style>
+
+    <style name="InternetDialog.Divider.Active">
+        <item name="android:background">?android:attr/textColorSecondaryInverse</item>
     </style>
 
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e00b941..475f70f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -595,6 +595,17 @@
         280
     </integer>
 
+    <!-- Haptic feedback intensity for ticks used for the udfps dwell time -->
+    <item name="config_udfpsTickIntensity" translatable="false" format="float"
+          type="dimen">.5</item>
+
+    <!-- Haptic feedback delay between ticks used for udfps dwell time -->
+    <integer name="config_udfpsTickDelay" translatable="false">25</integer>
+
+    <!-- Haptic feedback tick type - if true, uses VibrationEffect.Composition.PRIMITIVE_LOW_TICK
+         else uses VibrationEffect.Composition.PRIMITIVE_TICK -->
+    <bool name="config_udfpsUseLowTick">true</bool>
+
     <!-- package name of a built-in camera app to use to restrict implicit intent resolution
          when the double-press power gesture is used. Ignored if empty. -->
     <string translatable="false" name="config_cameraGesturePackage"></string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2afbe8c6..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>
@@ -1275,10 +1277,19 @@
 
     <dimen name="drag_and_drop_icon_size">70dp</dimen>
 
-    <dimen name="qs_dialog_button_horizontal_padding">16dp</dimen>
-    <dimen name="qs_dialog_button_vertical_padding">8dp</dimen>
+    <!-- Dimensions for unified SystemUI dialogs styling. Used by Theme.SystemUI.Dialog and
+         alert_dialog_systemui.xml
+      -->
+    <dimen name="dialog_button_horizontal_padding">16dp</dimen>
+    <dimen name="dialog_button_vertical_padding">8dp</dimen>
     <!-- The button will be 48dp tall, but the background needs to be 36dp tall -->
-    <dimen name="qs_dialog_button_vertical_inset">6dp</dimen>
+    <dimen name="dialog_button_vertical_inset">6dp</dimen>
+    <dimen name="dialog_top_padding">24dp</dimen>
+    <dimen name="dialog_bottom_padding">18dp</dimen>
+    <dimen name="dialog_side_padding">24dp</dimen>
+    <dimen name="dialog_button_bar_top_padding">32dp</dimen>
+
+    <!-- ************************************************************************* -->
 
     <dimen name="keyguard_unfold_translation_x">16dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 94d10de..61591bd 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -14,8 +14,8 @@
      limitations under the License.
 -->
 
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- NOTE: Adding the androidprv: namespace to this file will break the studio build. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+           xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
 
     <!-- HybridNotification themes and styles -->
 
@@ -351,11 +351,19 @@
         <item name="android:windowIsFloating">true</item>
     </style>
 
-    <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
+    <style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog"/>
+
+    <style name="Theme.SystemUI.Dialog" parent="@style/Theme.SystemUI.DayNightDialog">
         <item name="android:buttonCornerRadius">28dp</item>
-        <item name="android:buttonBarPositiveButtonStyle">@style/Widget.QSDialog.Button</item>
-        <item name="android:buttonBarNegativeButtonStyle">@style/Widget.QSDialog.Button.BorderButton</item>
-        <item name="android:buttonBarNeutralButtonStyle">@style/Widget.QSDialog.Button.BorderButton</item>
+        <item name="android:buttonBarPositiveButtonStyle">@style/Widget.Dialog.Button</item>
+        <item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
+        <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
+        <item name="android:colorBackground">?androidprv:attr/colorSurface</item>
+        <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
+    </style>
+
+    <style name="AlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault">
+        <item name="android:layout">@layout/alert_dialog_systemui</item>
     </style>
 
     <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -365,11 +373,11 @@
         <item name="android:windowIsFloating">true</item>
     </style>
 
-    <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
-        <item name="android:windowIsFloating">true</item>
+    <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="Theme.SystemUI.Dialog">
+        <!-- Settings windowFullscreen: true is necessary to be able to intercept touch events -->
+        <!-- that would otherwise be intercepted by the Shade. -->
+        <item name="android:windowFullscreen">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:backgroundDimEnabled">true</item>
-        <item name="android:windowCloseOnTouchOutside">true</item>
     </style>
 
     <style name="QSBorderlessButton">
@@ -853,24 +861,37 @@
         <item name="actionDividerHeight">32dp</item>
     </style>
 
-    <style name="TextAppearance.QSDialog.Title" parent="Theme.SystemUI.Dialog">
+    <style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textSize">24sp</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:lineHeight">32sp</item>
+        <item name="android:gravity">center</item>
+        <item name="android:textAlignment">center</item>
     </style>
 
-    <style name="Widget.QSDialog.Button" parent = "Theme.SystemUI.Dialog">
+    <style name="TextAppearance.Dialog.Body" parent="@android:style/TextAppearance.DeviceDefault.Medium">
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:lineHeight">20sp</item>
+    </style>
+
+    <style name="TextAppearance.Dialog.Body.Message">
+        <item name="android:gravity">center</item>
+        <item name="android:textAlignment">center</item>
+    </style>
+
+    <style name="Widget.Dialog.Button" parent = "Theme.SystemUI.Dialog">
         <item name="android:background">@drawable/qs_dialog_btn_filled</item>
-        <item name="android:textColor">@color/prv_text_color_on_accent</item>
+        <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
         <item name="android:textSize">14sp</item>
         <item name="android:lineHeight">20sp</item>
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
         <item name="android:stateListAnimator">@null</item>
-        <item name="android:layout_marginHorizontal">4dp</item>
     </style>
 
-    <style name="Widget.QSDialog.Button.BorderButton">
+    <style name="Widget.Dialog.Button.BorderButton">
         <item name="android:background">@drawable/qs_dialog_btn_outline</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
@@ -947,4 +968,10 @@
 
     <style name="TextAppearance.InternetDialog.Secondary.Active"/>
 
+    <style name="InternetDialog.Divider">
+        <item name="android:background">?android:attr/textColorSecondary</item>
+    </style>
+
+    <style name="InternetDialog.Divider.Active"/>
+
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 8bd0f91..0149751 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -95,4 +95,9 @@
      * Sent when screen turned on and ready to use (blocker scrim is hidden)
      */
     void onScreenTurnedOn() = 21;
+
+    /**
+     * Sent when the desired dark intensity of the nav buttons has changed
+     */
+    void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 3128ffd..675dc9b5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -28,6 +28,7 @@
 import android.os.Parcelable;
 import android.view.ViewDebug;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import java.io.PrintWriter;
@@ -50,6 +51,7 @@
         @ViewDebug.ExportedProperty(category="recents")
         public int windowingMode;
         @ViewDebug.ExportedProperty(category="recents")
+        @NonNull
         public final Intent baseIntent;
         @ViewDebug.ExportedProperty(category="recents")
         public final int userId;
@@ -83,7 +85,7 @@
             updateHashCode();
         }
 
-        public TaskKey(int id, int windowingMode, Intent intent,
+        public TaskKey(int id, int windowingMode, @NonNull Intent intent,
                 ComponentName sourceComponent, int userId, long lastActiveTime) {
             this.id = id;
             this.windowingMode = windowingMode;
@@ -95,7 +97,7 @@
             updateHashCode();
         }
 
-        public TaskKey(int id, int windowingMode, Intent intent,
+        public TaskKey(int id, int windowingMode, @NonNull Intent intent,
                 ComponentName sourceComponent, int userId, long lastActiveTime, int displayId) {
             this.id = id;
             this.windowingMode = windowingMode;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 2dbd5de..78867f7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -481,7 +481,9 @@
      * orientation overview.
      */
     public void setSkipOverrideUserLockPrefsOnce() {
-        mSkipOverrideUserLockPrefsOnce = true;
+        // If live-tile is enabled (recents animation keeps running in overview), there is no
+        // activity switch so the display rotation is not changed, then it is no need to skip.
+        mSkipOverrideUserLockPrefsOnce = !mIsRecentsAnimationRunning;
     }
 
     private boolean shouldOverrideUserLockPrefs(final int rotation) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9ce0e94..09f2af4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -163,7 +163,10 @@
     private boolean mOnFingerDown;
     private boolean mAttemptedToDismissKeyguard;
     private Set<Callback> mCallbacks = new HashSet<>();
-    private final VibrationEffect mLowTick;
+
+    // by default, use low tick
+    private int mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
+    private final VibrationEffect mTick;
 
     @VisibleForTesting
     public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
@@ -572,7 +575,7 @@
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
-        mLowTick = lowTick();
+        mTick = lowTick();
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -608,22 +611,31 @@
     }
 
     private VibrationEffect lowTick() {
+        boolean useLowTickDefault = mContext.getResources()
+                .getBoolean(R.bool.config_udfpsUseLowTick);
+        if (Settings.Global.getFloat(
+                mContext.getContentResolver(),
+                "tick-low", useLowTickDefault ? 1 : 0) == 0) {
+            mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK;
+        }
         float tickIntensity = Settings.Global.getFloat(
-                mContext.getContentResolver(), "low-tick-intensity", .5f);
-        VibrationEffect.Composition composition = VibrationEffect.startComposition();
-        composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
-                tickIntensity, 0);
+                mContext.getContentResolver(),
+                "tick-intensity",
+                mContext.getResources().getFloat(R.dimen.config_udfpsTickIntensity));
         int tickDelay = Settings.Global.getInt(
-                mContext.getContentResolver(), "low-tick-delay", 25);
+                mContext.getContentResolver(),
+                "tick-delay",
+                mContext.getResources().getInteger(R.integer.config_udfpsTickDelay));
+
+        VibrationEffect.Composition composition = VibrationEffect.startComposition();
+        composition.addPrimitive(mPrimitiveTick, tickIntensity, 0);
         int primitives = 1000 / tickDelay;
         float[] rampUp = new float[]{.48f, .58f, .69f, .83f};
         for (int i = 0; i < rampUp.length; i++) {
-            composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
-                    tickIntensity * rampUp[i], tickDelay);
+            composition.addPrimitive(mPrimitiveTick, tickIntensity * rampUp[i], tickDelay);
         }
         for (int i = rampUp.length; i < primitives; i++) {
-            composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
-                    tickIntensity, tickDelay);
+            composition.addPrimitive(mPrimitiveTick, tickIntensity, tickDelay);
         }
         return composition.compose();
     }
@@ -637,7 +649,7 @@
             mVibrator.vibrate(
                     Process.myUid(),
                     mContext.getOpPackageName(),
-                    mLowTick,
+                    mTick,
                     "udfps-onStart-tick",
                     VIBRATION_SONIFICATION_ATTRIBUTES);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index fd3fcdd..12786f2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -66,7 +66,6 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -125,7 +124,6 @@
         },
         subcomponents = {
             StatusBarComponent.class,
-            StatusBarFragmentComponent.class,
             NotificationRowComponent.class,
             DozeComponent.class,
             ExpandableNotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index b45dc52..10878dc 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -42,7 +42,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -308,22 +307,22 @@
             return instantiateWithInjections(context, className, arguments);
         }
 
-        private Fragment instantiateWithInjections(Context context, String className,
-                Bundle args) {
-            Method method = mManager.getInjectionMap().get(className);
-            if (method != null) {
+        private Fragment instantiateWithInjections(
+                Context context, String className, Bundle args) {
+            FragmentService.FragmentInstantiationInfo fragmentInstantiationInfo =
+                    mManager.getInjectionMap().get(className);
+            if (fragmentInstantiationInfo != null) {
                 try {
-                    Fragment f = (Fragment) method.invoke(mManager.getFragmentCreator());
+                    Fragment f = (Fragment) fragmentInstantiationInfo
+                            .mMethod
+                            .invoke(fragmentInstantiationInfo.mDaggerComponent);
                     // Setup the args, taken from Fragment#instantiate.
                     if (args != null) {
                         args.setClassLoader(f.getClass().getClassLoader());
                         f.setArguments(args);
                     }
                     return f;
-                } catch (IllegalAccessException e) {
-                    throw new Fragment.InstantiationException("Unable to instantiate " + className,
-                            e);
-                } catch (InvocationTargetException e) {
+                } catch (IllegalAccessException | InvocationTargetException e) {
                     throw new Fragment.InstantiationException("Unable to instantiate " + className,
                             e);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index 7f57fcc..2a5e653 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -18,13 +18,13 @@
 import android.content.res.Configuration;
 import android.os.Handler;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.view.View;
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.qs.QSFragment;
-import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.FileDescriptor;
@@ -46,9 +46,14 @@
     private static final String TAG = "FragmentService";
 
     private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
-    private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>();
+    /**
+     * A map with the means to create fragments via Dagger injection.
+     *
+     * key: the fragment class name.
+     * value: see {@link FragmentInstantiationInfo}.
+     */
+    private final ArrayMap<String, FragmentInstantiationInfo> mInjectionMap = new ArrayMap<>();
     private final Handler mHandler = new Handler();
-    private final FragmentCreator mFragmentCreator;
 
     private ConfigurationController.ConfigurationListener mConfigurationListener =
             new ConfigurationController.ConfigurationListener() {
@@ -65,26 +70,31 @@
             FragmentCreator.Factory fragmentCreatorFactory,
             ConfigurationController configurationController,
             DumpManager dumpManager) {
-        mFragmentCreator = fragmentCreatorFactory.build();
-        initInjectionMap();
+        addFragmentInstantiationProvider(fragmentCreatorFactory.build());
         configurationController.addCallback(mConfigurationListener);
 
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
     }
 
-    ArrayMap<String, Method> getInjectionMap() {
+    ArrayMap<String, FragmentInstantiationInfo> getInjectionMap() {
         return mInjectionMap;
     }
 
-    FragmentCreator getFragmentCreator() {
-        return mFragmentCreator;
-    }
-
-    private void initInjectionMap() {
-        for (Method method : FragmentCreator.class.getDeclaredMethods()) {
+    /**
+     * Adds a new Dagger component object that provides method(s) to create fragments via injection.
+     */
+    public void addFragmentInstantiationProvider(Object daggerComponent) {
+        for (Method method : daggerComponent.getClass().getDeclaredMethods()) {
             if (Fragment.class.isAssignableFrom(method.getReturnType())
                     && (method.getModifiers() & Modifier.PUBLIC) != 0) {
-                mInjectionMap.put(method.getReturnType().getName(), method);
+                String fragmentName = method.getReturnType().getName();
+                if (mInjectionMap.containsKey(fragmentName)) {
+                    Log.w(TAG, "Fragment " + fragmentName + " is already provided by different"
+                            + " Dagger component; Not adding method");
+                    continue;
+                }
+                mInjectionMap.put(
+                        fragmentName, new FragmentInstantiationInfo(method, daggerComponent));
             }
         }
     }
@@ -134,9 +144,6 @@
          * Inject a QSFragment.
          */
         QSFragment createQSFragment();
-
-        /** Inject a CollapsedStatusBarFragment. */
-        CollapsedStatusBarFragment createCollapsedStatusBarFragment();
     }
 
     private class FragmentHostState {
@@ -161,4 +168,16 @@
             mFragmentHostManager.onConfigurationChanged(newConfig);
         }
     }
+
+    /** An object containing the information needed to instantiate a fragment. */
+    static class FragmentInstantiationInfo {
+        /** The method that returns a newly-created fragment of the given class. */
+        final Method mMethod;
+        /** The Dagger component that the method should be invoked on. */
+        final Object mDaggerComponent;
+        FragmentInstantiationInfo(Method method, Object daggerComponent) {
+            this.mMethod = method;
+            this.mDaggerComponent = daggerComponent;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index ad47232..ff14064 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -31,8 +31,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Dialog;
@@ -56,6 +54,7 @@
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -80,8 +79,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -112,7 +109,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.MultiListLayout;
 import com.android.systemui.MultiListLayout.MultiListAdapter;
-import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -123,6 +120,7 @@
 import com.android.systemui.scrim.ScrimDrawable;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.telephony.TelephonyListenerManager;
@@ -239,6 +237,7 @@
     private int mSmallestScreenWidthDp;
     private final Optional<StatusBar> mStatusBarOptional;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final DialogLaunchAnimator mDialogLaunchAnimator;
 
     @VisibleForTesting
     public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -343,10 +342,11 @@
             UiEventLogger uiEventLogger,
             RingerModeTracker ringerModeTracker,
             SysUiState sysUiState,
-             @Main Handler handler,
+            @Main Handler handler,
             PackageManager packageManager,
             Optional<StatusBar> statusBarOptional,
-            KeyguardUpdateMonitor keyguardUpdateMonitor) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            DialogLaunchAnimator dialogLaunchAnimator) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -377,6 +377,7 @@
         mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
         mStatusBarOptional = statusBarOptional;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mDialogLaunchAnimator = dialogLaunchAnimator;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -435,11 +436,14 @@
     }
 
     /**
-     * Show the global actions dialog (creating if necessary)
+     * Show the global actions dialog (creating if necessary) or hide it if it's already showing.
      *
-     * @param keyguardShowing True if keyguard is showing
+     * @param keyguardShowing     True if keyguard is showing
+     * @param isDeviceProvisioned True if device is provisioned
+     * @param view                The view from which we should animate the dialog when showing it
      */
-    public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
+    public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
+            @Nullable View view) {
         mKeyguardShowing = keyguardShowing;
         mDeviceProvisioned = isDeviceProvisioned;
         if (mDialog != null && mDialog.isShowing()) {
@@ -451,7 +455,7 @@
             mDialog.dismiss();
             mDialog = null;
         } else {
-            handleShow();
+            handleShow(view);
         }
     }
 
@@ -483,7 +487,7 @@
         }
     }
 
-    protected void handleShow() {
+    protected void handleShow(@Nullable View view) {
         awakenIfNecessary();
         mDialog = createDialog();
         prepareDialog();
@@ -493,8 +497,13 @@
         attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         mDialog.getWindow().setAttributes(attrs);
         // Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
-        mDialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);
-        mDialog.show();
+        mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
+
+        if (view != null) {
+            mDialogLaunchAnimator.showFromView(mDialog, view);
+        } else {
+            mDialog.show();
+        }
         mWindowManagerFuncs.onGlobalActionsShown();
     }
 
@@ -643,7 +652,7 @@
         }
     }
 
-    protected void onRotate() {
+    protected void onRefresh() {
         // re-allocate actions between main and overflow lists
         this.createActionItems();
     }
@@ -667,7 +676,7 @@
                 com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
                 mAdapter, mOverflowAdapter, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
-                mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
+                mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
                 mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
 
         dialog.setOnDismissListener(this);
@@ -702,14 +711,6 @@
     }
 
     @Override
-    public void onUiModeChanged() {
-        mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
-        if (mDialog != null && mDialog.isShowing()) {
-            mDialog.refreshDialog();
-        }
-    }
-
-    @Override
     public void onConfigChanged(Configuration newConfig) {
         if (mDialog != null && mDialog.isShowing()
                 && (newConfig.smallestScreenWidthDp != mSmallestScreenWidthDp)) {
@@ -717,6 +718,7 @@
             mDialog.refreshDialog();
         }
     }
+
     /**
      * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
      * called when the quick access wallet requests dismissal.
@@ -1363,6 +1365,10 @@
             final Action action = mAdapter.getItem(position);
             if (action instanceof LongPressAction) {
                 if (mDialog != null) {
+                    // Usually clicking an item shuts down the phone, locks, or starts an activity.
+                    // We don't want to animate back into the power button when that happens, so we
+                    // disable the dialog animation before dismissing.
+                    mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
                     mDialog.dismiss();
                 } else {
                     Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1379,6 +1385,10 @@
                 if (mDialog != null) {
                     // don't dismiss the dialog if we're opening the power options menu
                     if (!(item instanceof PowerOptionsAction)) {
+                        // Usually clicking an item shuts down the phone, locks, or starts an
+                        // activity. We don't want to animate back into the power button when that
+                        // happens, so we disable the dialog animation before dismissing.
+                        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
                         mDialog.dismiss();
                     }
                 } else {
@@ -1446,6 +1456,10 @@
             final Action action = getItem(position);
             if (action instanceof LongPressAction) {
                 if (mDialog != null) {
+                    // Usually clicking an item shuts down the phone, locks, or starts an activity.
+                    // We don't want to animate back into the power button when that happens, so we
+                    // disable the dialog animation before dismissing.
+                    mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
                     mDialog.dismiss();
                 } else {
                     Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1459,6 +1473,10 @@
             Action item = getItem(position);
             if (!(item instanceof SilentModeTriStateAction)) {
                 if (mDialog != null) {
+                    // Usually clicking an item shuts down the phone, locks, or starts an activity.
+                    // We don't want to animate back into the power button when that happens, so we
+                    // disable the dialog animation before dismissing.
+                    mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
                     mDialog.dismiss();
                 } else {
                     Log.w(TAG, "Action clicked while mDialog is null.");
@@ -1510,6 +1528,10 @@
             final Action action = getItem(position);
             if (action instanceof LongPressAction) {
                 if (mDialog != null) {
+                    // Usually clicking an item shuts down the phone, locks, or starts an activity.
+                    // We don't want to animate back into the power button when that happens, so we
+                    // disable the dialog animation before dismissing.
+                    mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
                     mDialog.dismiss();
                 } else {
                     Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1523,6 +1545,10 @@
             Action item = getItem(position);
             if (!(item instanceof SilentModeTriStateAction)) {
                 if (mDialog != null) {
+                    // Usually clicking an item shuts down the phone, locks, or starts an activity.
+                    // We don't want to animate back into the power button when that happens, so we
+                    // disable the dialog animation before dismissing.
+                    mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
                     mDialog.dismiss();
                 } else {
                     Log.w(TAG, "Action clicked while mDialog is null.");
@@ -2066,7 +2092,9 @@
                 case MESSAGE_DISMISS:
                     if (mDialog != null) {
                         if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
-                            mDialog.completeDismiss();
+                            // Hide instantly.
+                            mDialog.hide();
+                            mDialog.dismiss();
                         } else {
                             mDialog.dismiss();
                         }
@@ -2113,7 +2141,7 @@
     }
 
     @VisibleForTesting
-    static class ActionsDialogLite extends Dialog implements DialogInterface,
+    static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
             ColorExtractor.OnColorsChangedListener {
 
         protected final Context mContext;
@@ -2126,13 +2154,12 @@
         protected Drawable mBackgroundDrawable;
         protected final SysuiColorExtractor mColorExtractor;
         private boolean mKeyguardShowing;
-        protected boolean mShowing;
         protected float mScrimAlpha;
         protected final NotificationShadeWindowController mNotificationShadeWindowController;
         protected final SysUiState mSysUiState;
         private ListPopupWindow mOverflowPopup;
         private Dialog mPowerOptionsDialog;
-        protected final Runnable mOnRotateCallback;
+        protected final Runnable mOnRefreshCallback;
         private UiEventLogger mUiEventLogger;
         private GestureDetector mGestureDetector;
         private Optional<StatusBar> mStatusBarOptional;
@@ -2151,7 +2178,7 @@
                     }
 
                     @Override
-                    public boolean onSingleTapConfirmed(MotionEvent e) {
+                    public boolean onSingleTapUp(MotionEvent e) {
                         // Close without opening shade
                         mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
                         cancel();
@@ -2189,11 +2216,13 @@
                 MyOverflowAdapter overflowAdapter,
                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
                 NotificationShadeWindowController notificationShadeWindowController,
-                SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
+                SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
                 Optional<StatusBar> statusBarOptional,
                 KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
-            super(context, themeRes);
+            // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
+            // dismiss this dialog when the device is locked.
+            super(context, themeRes, false /* dismissOnDeviceLock */);
             mContext = context;
             mAdapter = adapter;
             mOverflowAdapter = overflowAdapter;
@@ -2202,36 +2231,32 @@
             mStatusBarService = statusBarService;
             mNotificationShadeWindowController = notificationShadeWindowController;
             mSysUiState = sysuiState;
-            mOnRotateCallback = onRotateCallback;
+            mOnRefreshCallback = onRefreshCallback;
             mKeyguardShowing = keyguardShowing;
             mUiEventLogger = uiEventLogger;
             mStatusBarOptional = statusBarOptional;
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
-
             mGestureDetector = new GestureDetector(mContext, mGestureListener);
+        }
 
-            // Window initialization
-            Window window = getWindow();
-            window.requestFeature(Window.FEATURE_NO_TITLE);
-            // Inflate the decor view, so the attributes below are not overwritten by the theme.
-            window.getDecorView();
-            window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE
-                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
-            window.setLayout(MATCH_PARENT, MATCH_PARENT);
-            window.addFlags(
-                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                            | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-                            | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                            | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
-            window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
-            window.getAttributes().setFitInsetsTypes(0 /* types */);
-            setTitle(R.string.global_actions);
-
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
             initializeLayout();
         }
 
         @Override
+        protected int getWidth() {
+            return MATCH_PARENT;
+        }
+
+        @Override
+        protected int getHeight() {
+            return MATCH_PARENT;
+        }
+
+        @Override
         public boolean onTouchEvent(MotionEvent event) {
             return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
         }
@@ -2371,7 +2396,8 @@
                             message.animate()
                                     .alpha(0f)
                                     .setDuration(TOAST_FADE_TIME)
-                                    .setStartDelay(visibleTime);
+                                    .setStartDelay(visibleTime)
+                                    .setListener(null);
                         }
                     });
         }
@@ -2423,122 +2449,32 @@
         @Override
         public void show() {
             super.show();
-            // split this up so we can override but still call Dialog.show
-            showDialog();
-        }
-
-        protected void showDialog() {
-            mShowing = true;
             mNotificationShadeWindowController.setRequestTopUi(true, TAG);
             mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
                     .commitUpdate(mContext.getDisplayId());
-
-            ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
-            root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
-                root.setPadding(windowInsets.getStableInsetLeft(),
-                        windowInsets.getStableInsetTop(),
-                        windowInsets.getStableInsetRight(),
-                        windowInsets.getStableInsetBottom());
-                return WindowInsets.CONSUMED;
-            });
-
-            mBackgroundDrawable.setAlpha(0);
-            float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
-            ObjectAnimator alphaAnimator =
-                    ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f);
-            alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-            alphaAnimator.setDuration(183);
-            alphaAnimator.addUpdateListener((animation) -> {
-                float animatedValue = animation.getAnimatedFraction();
-                int alpha = (int) (animatedValue * mScrimAlpha * 255);
-                mBackgroundDrawable.setAlpha(alpha);
-            });
-
-            ObjectAnimator xAnimator =
-                    ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f);
-            xAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-            xAnimator.setDuration(350);
-
-            AnimatorSet animatorSet = new AnimatorSet();
-            animatorSet.playTogether(alphaAnimator, xAnimator);
-            animatorSet.start();
         }
 
         @Override
         public void dismiss() {
-            dismissWithAnimation(() -> {
-                dismissInternal();
-            });
-        }
+            dismissOverflow();
+            dismissPowerOptions();
 
-        protected void dismissInternal() {
-            mContainer.setTranslationX(0);
-            ObjectAnimator alphaAnimator =
-                    ObjectAnimator.ofFloat(mContainer, "alpha", 1f, 0f);
-            alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-            alphaAnimator.setDuration(233);
-            alphaAnimator.addUpdateListener((animation) -> {
-                float animatedValue = 1f - animation.getAnimatedFraction();
-                int alpha = (int) (animatedValue * mScrimAlpha * 255);
-                mBackgroundDrawable.setAlpha(alpha);
-            });
-
-            float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
-            ObjectAnimator xAnimator =
-                    ObjectAnimator.ofFloat(mContainer, "translationX", 0f, xOffset);
-            xAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-            xAnimator.setDuration(350);
-
-            AnimatorSet animatorSet = new AnimatorSet();
-            animatorSet.playTogether(alphaAnimator, xAnimator);
-            animatorSet.addListener(new AnimatorListenerAdapter() {
-                public void onAnimationEnd(Animator animation) {
-                    completeDismiss();
-                }
-            });
-
-            animatorSet.start();
-
-            // close first, as popup windows will not fade during the animation
-            dismissOverflow(false);
-            dismissPowerOptions(false);
-        }
-
-        void dismissWithAnimation(Runnable animation) {
-            if (!mShowing) {
-                return;
-            }
-            mShowing = false;
-            animation.run();
-        }
-
-        protected void completeDismiss() {
-            mShowing = false;
-            dismissOverflow(true);
-            dismissPowerOptions(true);
             mNotificationShadeWindowController.setRequestTopUi(false, TAG);
             mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
                     .commitUpdate(mContext.getDisplayId());
+
             super.dismiss();
         }
 
-        protected final void dismissOverflow(boolean immediate) {
+        protected final void dismissOverflow() {
             if (mOverflowPopup != null) {
-                if (immediate) {
-                    mOverflowPopup.dismissImmediate();
-                } else {
-                    mOverflowPopup.dismiss();
-                }
+                mOverflowPopup.dismiss();
             }
         }
 
-        protected final void dismissPowerOptions(boolean immediate) {
+        protected final void dismissPowerOptions() {
             if (mPowerOptionsDialog != null) {
-                if (immediate) {
-                    mPowerOptionsDialog.dismiss();
-                } else {
-                    mPowerOptionsDialog.dismiss();
-                }
+                mPowerOptionsDialog.dismiss();
             }
         }
 
@@ -2574,20 +2510,18 @@
         }
 
         public void refreshDialog() {
-            // ensure dropdown menus are dismissed before re-initializing the dialog
-            dismissOverflow(true);
-            dismissPowerOptions(true);
+            mOnRefreshCallback.run();
 
-            // re-create dialog
-            initializeLayout();
+            // Dismiss the dropdown menus.
+            dismissOverflow();
+            dismissPowerOptions();
+
+            // Update the list as the max number of items per row has probably changed.
             mGlobalActionsLayout.updateList();
         }
 
         public void onRotate(int from, int to) {
-            if (mShowing) {
-                mOnRotateCallback.run();
-                refreshDialog();
-            }
+            refreshDialog();
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c4508e0..96ae646 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -82,7 +82,7 @@
         if (mDisabled) return;
         mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
         mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
-                mDeviceProvisionedController.isDeviceProvisioned());
+                mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 47ef5e4..d54b151 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -400,7 +400,7 @@
         }
         updatePageIndicator()
         mediaCarouselScrollHandler.onPlayersChanged()
-        mediaCarousel.requiresRemeasuring = true
+        mediaFrame.requiresRemeasuring = true
         // Check postcondition: mediaContent should have the same number of children as there are
         // elements in mediaPlayers.
         if (MediaPlayerData.players().size != mediaContent.childCount) {
@@ -439,7 +439,7 @@
         updatePlayerToState(newRecs, noAnimation = true)
         reorderAllPlayers(curVisibleMediaKey)
         updatePageIndicator()
-        mediaCarousel.requiresRemeasuring = true
+        mediaFrame.requiresRemeasuring = true
         // Check postcondition: mediaContent should have the same number of children as there are
         // elements in mediaPlayers.
         if (MediaPlayerData.players().size != mediaContent.childCount) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 868193b..54e40f1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -289,6 +289,9 @@
                         public void onAnimationEnd(Animator animation) {
                             to.requireViewById(R.id.volume_indeterminate_progress).setVisibility(
                                     View.VISIBLE);
+                            // Unset the listener, otherwise this may persist for another view
+                            // property animation
+                            toTitleText.animate().setListener(null);
                         }
                     });
             // Animation for seek bar
@@ -312,8 +315,14 @@
                                         public void onAnimationEnd(Animator animation) {
                                             mIsAnimating = false;
                                             notifyDataSetChanged();
+                                            // Unset the listener, otherwise this may persist for
+                                            // another view property animation
+                                            fromTitleText.animate().setListener(null);
                                         }
                                     });
+                            // Unset the listener, otherwise this may persist for another view
+                            // property animation
+                            fromSeekBar.animate().setListener(null);
                         }
                     });
         }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 52103d3..23062d8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -72,7 +72,7 @@
     private final UserTracker mUserTracker;
     private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
     private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
-    private Context mContext;
+    private final Context mContext;
     private ContentResolver mContentResolver;
     private boolean mAssistantAvailable;
     private boolean mLongPressHomeEnabled;
@@ -87,8 +87,13 @@
         }
     };
 
+    /**
+     * @param context This is not display specific, then again neither is any of the code in
+     *                this class. Once there's display specific code, we may want to create an
+     *                instance of this class per navbar vs having it be a singleton.
+     */
     @Inject
-    public NavBarHelper(AccessibilityManager accessibilityManager,
+    public NavBarHelper(Context context, AccessibilityManager accessibilityManager,
             AccessibilityManagerWrapper accessibilityManagerWrapper,
             AccessibilityButtonModeObserver accessibilityButtonModeObserver,
             OverviewProxyService overviewProxyService,
@@ -96,6 +101,7 @@
             NavigationModeController navigationModeController,
             UserTracker userTracker,
             DumpManager dumpManager) {
+        mContext = context;
         mAccessibilityManager = accessibilityManager;
         mAssistManagerLazy = assistManagerLazy;
         mUserTracker = userTracker;
@@ -109,8 +115,7 @@
         dumpManager.registerDumpable(this);
     }
 
-    public void init(Context context) {
-        mContext = context;
+    public void init() {
         mContentResolver = mContext.getContentResolver();
         mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5141f25..1219c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -563,7 +563,7 @@
         mCommandQueue.addCallback(this);
         mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
         mContentResolver = mContext.getContentResolver();
-        mNavBarHelper.init(mContext);
+        mNavBarHelper.init();
         mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
                 R.bool.allow_force_nav_bar_handle_opaque);
         mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 3398778..0429c02 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.FileDescriptor;
@@ -104,7 +105,8 @@
             TaskbarDelegate taskbarDelegate,
             NavigationBar.Factory navigationBarFactory,
             DumpManager dumpManager,
-            AutoHideController autoHideController) {
+            AutoHideController autoHideController,
+            LightBarController lightBarController) {
         mContext = context;
         mHandler = mainHandler;
         mNavigationBarFactory = navigationBarFactory;
@@ -116,7 +118,7 @@
         mTaskbarDelegate = taskbarDelegate;
         mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
-                dumpManager, autoHideController);
+                dumpManager, autoHideController, lightBarController);
         mIsTablet = isTablet(mContext);
         dumpManager.registerDumpable(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 680cc5c..364a8ae 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -110,6 +110,7 @@
     private final int mNavColorSampleMargin;
     private final SysUiState mSysUiFlagContainer;
 
+    // The current view is one of mHorizontal or mVertical depending on the current configuration
     View mCurrentView = null;
     private View mVertical;
     private View mHorizontal;
@@ -397,12 +398,6 @@
         }
     }
 
-    @Override
-    protected boolean onSetAlpha(int alpha) {
-        Log.e(TAG, "onSetAlpha", new Throwable());
-        return super.onSetAlpha(alpha);
-    }
-
     public void setAutoHideController(AutoHideController autoHideController) {
         mAutoHideController = autoHideController;
     }
@@ -505,6 +500,18 @@
         return mCurrentView;
     }
 
+    /**
+     * Applies {@param consumer} to each of the nav bar views.
+     */
+    public void forEachView(Consumer<View> consumer) {
+        if (mVertical != null) {
+            consumer.accept(mVertical);
+        }
+        if (mHorizontal != null) {
+            consumer.accept(mHorizontal);
+        }
+    }
+
     public RotationButtonController getRotationButtonController() {
         return mRotationButtonController;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 428d9d6..fb9b8eb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -64,6 +64,9 @@
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -85,6 +88,8 @@
     private NavigationModeController mNavigationModeController;
     private SysUiState mSysUiState;
     private AutoHideController mAutoHideController;
+    private LightBarController mLightBarController;
+    private LightBarTransitionsController mLightBarTransitionsController;
     private int mDisplayId;
     private int mNavigationIconHints;
     private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
@@ -141,7 +146,8 @@
             NavBarHelper navBarHelper,
             NavigationModeController navigationModeController,
             SysUiState sysUiState, DumpManager dumpManager,
-            AutoHideController autoHideController) {
+            AutoHideController autoHideController,
+            LightBarController lightBarController) {
         // TODO: adding this in the ctor results in a dagger dependency cycle :(
         mCommandQueue = commandQueue;
         mOverviewProxyService = overviewProxyService;
@@ -150,6 +156,30 @@
         mSysUiState = sysUiState;
         dumpManager.registerDumpable(this);
         mAutoHideController = autoHideController;
+        mLightBarController = lightBarController;
+        mLightBarTransitionsController = createLightBarTransitionsController();
+    }
+
+    // Separated into a method to keep setDependencies() clean/readable.
+    private LightBarTransitionsController createLightBarTransitionsController() {
+        return new LightBarTransitionsController(mContext,
+                new LightBarTransitionsController.DarkIntensityApplier() {
+                    @Override
+                    public void applyDarkIntensity(float darkIntensity) {
+                        mOverviewProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
+                    }
+
+                    @Override
+                    public int getTintAnimationDuration() {
+                        return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
+                    }
+                }, mCommandQueue) {
+            @Override
+            public boolean supportsIconTintForNavMode(int navigationMode) {
+                // Always tint taskbar nav buttons (region sampling handles gesture bar separately).
+                return true;
+            }
+        };
     }
 
     public void init(int displayId) {
@@ -162,7 +192,7 @@
         mEdgeBackGestureHandler.onNavigationModeChanged(
                 mNavigationModeController.addListener(this));
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
-        mNavBarHelper.init(mContext);
+        mNavBarHelper.init();
         mEdgeBackGestureHandler.onNavBarAttached();
         // Initialize component callback
         Display display = mDisplayManager.getDisplay(displayId);
@@ -171,6 +201,7 @@
         // Set initial state for any listeners
         updateSysuiFlags();
         mAutoHideController.setNavigationBar(mAutoHideUiElement);
+        mLightBarController.setNavigationBar(mLightBarTransitionsController);
         mInitialized = true;
     }
 
@@ -189,6 +220,8 @@
             mWindowContext = null;
         }
         mAutoHideController.setNavigationBar(null);
+        mLightBarTransitionsController.destroy(mContext);
+        mLightBarController.setNavigationBar(null);
         mInitialized = false;
     }
 
@@ -268,6 +301,10 @@
             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
             InsetsVisibilities requestedVisibilities, String packageName) {
         mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
+        if (mLightBarController != null && displayId == mDisplayId) {
+            mLightBarController.onNavigationBarAppearanceChanged(appearance, false/*nbModeChanged*/,
+                    BarTransitions.MODE_TRANSPARENT /*navigationBarMode*/, navbarColorManagedByIme);
+        }
         if (mBehavior != behavior) {
             mBehavior = behavior;
             updateSysuiFlags();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 98b9146..bc3a4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -116,7 +116,7 @@
             }
         } else if (v === powerMenuLite) {
             uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
-            globalActionsDialog.showOrHideDialog(false, true)
+            globalActionsDialog.showOrHideDialog(false, true, v)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
index 2f189be..768598a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -133,10 +133,7 @@
                     x += width + mHorizontalSpacing;
                 }
             }
-            y += maxHeight;
-            if (row > 0) {
-                y += mVerticalSpacing;
-            }
+            y += maxHeight + mVerticalSpacing;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e42c47b..ee59ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -770,6 +770,8 @@
         public void onAnimationEnd(Animator animation) {
             mHeaderAnimating = false;
             updateQsState();
+            // Unset the listener, otherwise this may persist for another view property animation
+            getView().animate().setListener(null);
         }
     };
 
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 77b9cc1..6bba1ed 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
@@ -56,6 +56,7 @@
 
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
@@ -120,6 +121,7 @@
     private TextView mMobileTitleText;
     private TextView mMobileSummaryText;
     private Switch mMobileDataToggle;
+    private View mMobileToggleDivider;
     private Switch mWiFiToggle;
     private FrameLayout mDoneLayout;
     private Drawable mBackgroundOn;
@@ -128,6 +130,7 @@
     private boolean mCanConfigMobileData;
 
     // Wi-Fi entries
+    private int mWifiNetworkHeight;
     @VisibleForTesting
     protected WifiEntry mConnectedWifiEntry;
     @VisibleForTesting
@@ -185,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);
@@ -207,6 +213,7 @@
         mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
         mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
         mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+        mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
         mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
         mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
         mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
@@ -332,9 +339,6 @@
         mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
         mWiFiToggle.setOnCheckedChangeListener(
                 (buttonView, isChecked) -> {
-                    if (isChecked) {
-                        mWifiScanNotifyLayout.setVisibility(View.GONE);
-                    }
                     buttonView.setChecked(isChecked);
                     mWifiManager.setWifiEnabled(isChecked);
                 });
@@ -378,6 +382,14 @@
             mMobileNetworkLayout.setBackground(
                     isCarrierNetworkConnected ? mBackgroundOn : mBackgroundOff);
 
+            TypedArray array = mContext.obtainStyledAttributes(
+                    R.style.InternetDialog_Divider_Active, new int[]{android.R.attr.background});
+            int dividerColor = Utils.getColorAttrDefaultColor(mContext,
+                    android.R.attr.textColorSecondary);
+            mMobileToggleDivider.setBackgroundColor(isCarrierNetworkConnected
+                    ? array.getColor(0, dividerColor) : dividerColor);
+            array.recycle();
+
             mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
         }
     }
@@ -416,9 +428,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/qs/user/UserDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
deleted file mode 100644
index 26d1bbd..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.user
-
-import android.content.Context
-import android.os.Bundle
-import android.view.Gravity
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowInsets
-import android.view.WindowManager
-import com.android.systemui.qs.PseudoGridView
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.R
-
-/**
- * Dialog for switching users or creating new ones.
- */
-class UserDialog(
-    context: Context
-) : SystemUIDialog(context) {
-
-    // create() is no-op after creation
-    private lateinit var _doneButton: View
-    /**
-     * Button with text "Done" in dialog.
-     */
-    val doneButton: View
-        get() {
-            create()
-            return _doneButton
-        }
-
-    private lateinit var _settingsButton: View
-    /**
-     * Button with text "User Settings" in dialog.
-     */
-    val settingsButton: View
-        get() {
-            create()
-            return _settingsButton
-        }
-
-    private lateinit var _grid: PseudoGridView
-    /**
-     * Grid to populate with user avatar from adapter
-     */
-    val grid: ViewGroup
-        get() {
-            create()
-            return _grid
-        }
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        window?.apply {
-            setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
-            attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
-            attributes.receiveInsetsIgnoringZOrder = true
-            setGravity(Gravity.CENTER)
-        }
-        setContentView(R.layout.qs_user_dialog_content)
-
-        _doneButton = requireViewById(R.id.done)
-        _settingsButton = requireViewById(R.id.settings)
-        _grid = requireViewById(R.id.grid)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index d74a50e..00e0454 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -21,13 +21,16 @@
 import android.content.DialogInterface
 import android.content.Intent
 import android.provider.Settings
+import android.view.LayoutInflater
 import android.view.View
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.R
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.phone.SystemUIDialog
 import javax.inject.Inject
 import javax.inject.Provider
 
@@ -40,7 +43,7 @@
     private val activityStarter: ActivityStarter,
     private val falsingManager: FalsingManager,
     private val dialogLaunchAnimator: DialogLaunchAnimator,
-    private val dialogFactory: (Context) -> UserDialog
+    private val dialogFactory: (Context) -> SystemUIDialog
 ) {
 
     @Inject
@@ -54,7 +57,7 @@
         activityStarter,
         falsingManager,
         dialogLaunchAnimator,
-        { UserDialog(it) }
+        { SystemUIDialog(it) }
     )
 
     companion object {
@@ -71,9 +74,10 @@
         with(dialogFactory(view.context)) {
             setShowForAllUsers(true)
             setCanceledOnTouchOutside(true)
-            create() // Needs to be called before we can retrieve views
 
-            settingsButton.setOnClickListener {
+            setTitle(R.string.qs_user_switch_dialog_title)
+            setPositiveButton(R.string.quick_settings_done, null)
+            setNeutralButton(R.string.quick_settings_more_user_settings) { _, _ ->
                 if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                     dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
                     activityStarter.postStartActivityDismissingKeyguard(
@@ -81,12 +85,14 @@
                         0
                     )
                 }
-                dismiss()
             }
-            doneButton.setOnClickListener { dismiss() }
+            val gridFrame = LayoutInflater.from(this.context)
+                .inflate(R.layout.qs_user_dialog_content, null)
+            setView(gridFrame)
 
             val adapter = userDetailViewAdapterProvider.get()
-            adapter.linkToViewGroup(grid)
+
+            adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
 
             val hostDialog = dialogLaunchAnimator.showFromView(this, view)
             adapter.injectDialogShower(DialogShowerImpl(hostDialog, dialogLaunchAnimator))
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index fa874b1..3ed7e84 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -986,6 +986,18 @@
         }
     }
 
+    public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+        try {
+            if (mOverviewProxy != null) {
+                mOverviewProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
+            } else {
+                Log.e(TAG_OPS, "Failed to get overview proxy to update nav buttons dark intensity");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG_OPS, "Failed to call onNavButtonsDarkIntensityChanged()", e);
+        }
+    }
+
     private void updateEnabledState() {
         final int currentUser = ActivityManagerWrapper.getInstance().getCurrentUserId();
         mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 5bb3413..d64c05f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -32,7 +32,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
 import android.widget.Toast;
 
@@ -62,7 +61,6 @@
     private static final String EXTRA_RESULT_CODE = "extra_resultCode";
     private static final String EXTRA_PATH = "extra_path";
     private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
-    private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
 
     private static final String ACTION_START = "com.android.systemui.screenrecord.START";
     private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
@@ -74,8 +72,6 @@
     private final RecordingController mController;
     private final KeyguardDismissUtil mKeyguardDismissUtil;
     private ScreenRecordingAudioSource mAudioSource;
-    private boolean mShowTaps;
-    private boolean mOriginalShowTaps;
     private ScreenMediaRecorder mRecorder;
     private final Executor mLongExecutor;
     private final UiEventLogger mUiEventLogger;
@@ -102,15 +98,12 @@
      *                   android.content.Intent)}
      * @param audioSource   The ordinal value of the audio source
      *                      {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource}
-     * @param showTaps   True to make touches visible while recording
      */
-    public static Intent getStartIntent(Context context, int resultCode,
-            int audioSource, boolean showTaps) {
+    public static Intent getStartIntent(Context context, int resultCode, int audioSource) {
         return new Intent(context, RecordingService.class)
                 .setAction(ACTION_START)
                 .putExtra(EXTRA_RESULT_CODE, resultCode)
-                .putExtra(EXTRA_AUDIO_SOURCE, audioSource)
-                .putExtra(EXTRA_SHOW_TAPS, showTaps);
+                .putExtra(EXTRA_AUDIO_SOURCE, audioSource);
     }
 
     @Override
@@ -128,13 +121,6 @@
                 mAudioSource = ScreenRecordingAudioSource
                         .values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)];
                 Log.d(TAG, "recording with audio source" + mAudioSource);
-                mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);
-
-                mOriginalShowTaps = Settings.System.getInt(
-                        getApplicationContext().getContentResolver(),
-                        Settings.System.SHOW_TOUCHES, 0) != 0;
-
-                setTapsVisible(mShowTaps);
 
                 mRecorder = new ScreenMediaRecorder(
                         mUserContextTracker.getUserContext(),
@@ -379,7 +365,6 @@
     }
 
     private void stopRecording(int userId) {
-        setTapsVisible(mOriginalShowTaps);
         if (getRecorder() != null) {
             getRecorder().end();
             saveRecording(userId);
@@ -411,11 +396,6 @@
         });
     }
 
-    private void setTapsVisible(boolean turnOn) {
-        int value = turnOn ? 1 : 0;
-        Settings.System.putInt(getContentResolver(), Settings.System.SHOW_TOUCHES, value);
-    }
-
     /**
      * Get an intent to stop the recording service.
      * @param context Context from the requesting activity
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 1fb88df..582cc76 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -55,7 +55,6 @@
     private final UserContextProvider mUserContextProvider;
     @Nullable
     private final Runnable mOnStartRecordingClicked;
-    private Switch mTapsSwitch;
     private Switch mAudioSwitch;
     private Spinner mOptions;
 
@@ -96,7 +95,6 @@
         });
 
         mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
-        mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
         mOptions = findViewById(R.id.screen_recording_options);
         ArrayAdapter a = new ScreenRecordingAdapter(getContext().getApplicationContext(),
                 android.R.layout.simple_spinner_dropdown_item,
@@ -110,7 +108,6 @@
 
     private void requestScreenCapture() {
         Context userContext = mUserContextProvider.getUserContext();
-        boolean showTaps = mTapsSwitch.isChecked();
         ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
                 ? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
                 : NONE;
@@ -118,7 +115,7 @@
                 RecordingService.REQUEST_CODE,
                 RecordingService.getStartIntent(
                         userContext, Activity.RESULT_OK,
-                        audioMode.ordinal(), showTaps),
+                        audioMode.ordinal()),
                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
         PendingIntent stopIntent = PendingIntent.getService(userContext,
                 RecordingService.REQUEST_CODE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 190c773e..f23a7ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -717,6 +717,9 @@
                                         textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
                                         ViewClippingUtil.setClippingDeactivated(textView, false,
                                                 mClippingParams);
+                                        // Unset the listener, otherwise this may persist for
+                                        // another view property animation
+                                        textView.animate().setListener(null);
                                     }
                                 });
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java
new file mode 100644
index 0000000..8d2e3c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Used to let views that have an alpha not apply the HARDWARE layer type directly, and instead
+ * delegate that to specific children.  This is useful if we want to fake not having overlapping
+ * rendering to avoid layer trashing, when fading out a view that is also changing.
+ */
+public interface NotificationFadeAware {
+    /**
+     * Calls {@link View#setLayerType} with {@link View#LAYER_TYPE_HARDWARE} if faded and
+     * {@link View#LAYER_TYPE_NONE} otherwise.
+     */
+    static void setLayerTypeForFaded(@Nullable View view, boolean faded) {
+        if (view != null) {
+            int newLayerType = faded ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE;
+            view.setLayerType(newLayerType, null);
+        }
+    }
+
+    /**
+     * Used like {@link View#setLayerType} with {@link View#LAYER_TYPE_HARDWARE} or
+     * {@link View#LAYER_TYPE_NONE} except that instead of necessarily affecting this view
+     * specifically, this may delegate the call to child views.
+     *
+     * When set to <code>true</code>, the view has two possible paths:
+     *  1. If a hardware layer is required to ensure correct appearance of this view, then
+     *    set that layer type.
+     *  2. Otherwise, delegate this call to children, who might make that call for themselves.
+     *
+     * When set to <code>false</code>, the view should undo the above, typically by calling
+     *  {@link View#setLayerType} with {@link View#LAYER_TYPE_NONE} on itself and children, and
+     *  delegating to this method on children where implemented.
+     *
+     * When this delegates to {@link View#setLayerType} on this view or a subview, `null` will be
+     * passed for the `paint` argument of that call.
+     */
+    void setNotificationFaded(boolean faded);
+
+    /**
+     * Interface for the top level notification view that fades and optimizes that through deep
+     * awareness of individual components.
+     */
+    interface FadeOptimizedNotification extends NotificationFadeAware {
+        /** Top-level feature switch */
+        boolean FADE_LAYER_OPTIMIZATION_ENABLED = true;
+
+        /** Determine if the notification is currently faded. */
+        boolean isNotificationFaded();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index a2d1040..23a0a75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -87,6 +87,7 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -125,7 +126,8 @@
  * the group summary (which contains 1 or more child notifications).
  */
 public class ExpandableNotificationRow extends ActivatableNotificationView
-        implements PluginListener<NotificationMenuRowPlugin>, SwipeableView {
+        implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
+        NotificationFadeAware.FadeOptimizedNotification {
 
     private static final boolean DEBUG = false;
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
@@ -138,6 +140,7 @@
     private boolean mUpdateBackgroundOnUpdate;
     private boolean mNotificationTranslationFinished = false;
     private boolean mIsSnoozed;
+    private boolean mIsFaded;
 
     /**
      * Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -2753,17 +2756,112 @@
             // alphas are reset
             if (mChildrenContainer != null) {
                 mChildrenContainer.setAlpha(1.0f);
-                mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
             }
             for (NotificationContentView l : mLayouts) {
                 l.setAlpha(1.0f);
-                l.setLayerType(LAYER_TYPE_NONE, null);
+            }
+            if (FADE_LAYER_OPTIMIZATION_ENABLED) {
+                setNotificationFaded(false);
+            } else {
+                setNotificationFadedOnChildren(false);
             }
         } else {
             setHeadsUpAnimatingAway(false);
         }
     }
 
+    /** Gets the last value set with {@link #setNotificationFaded(boolean)} */
+    @Override
+    public boolean isNotificationFaded() {
+        return mIsFaded;
+    }
+
+    /**
+     * This class needs to delegate the faded state set on it by
+     * {@link com.android.systemui.statusbar.notification.stack.ViewState} to its children.
+     * Having each notification use layerType of HARDWARE anytime it fades in/out can result in
+     * extremely large layers (in the case of groups, it can even exceed the device height).
+     * Because these large renders can cause serious jank when rendering, we instead have
+     * notifications return false from {@link #hasOverlappingRendering()} and delegate the
+     * layerType to child views which really need it in order to render correctly, such as icon
+     * views or the conversation face pile.
+     *
+     * Another compounding factor for notifications is that we change clipping on each frame of the
+     * animation, so the hardware layer isn't able to do any caching at the top level, but the
+     * individual elements we render with hardware layers (e.g. icons) cache wonderfully because we
+     * never invalidate them.
+     */
+    @Override
+    public void setNotificationFaded(boolean faded) {
+        mIsFaded = faded;
+        if (childrenRequireOverlappingRendering()) {
+            // == Simple Scenario ==
+            // If a child (like remote input) needs this to have overlapping rendering, then set
+            // the layerType of this view and reset the children to render as if the notification is
+            // not fading.
+            NotificationFadeAware.setLayerTypeForFaded(this, faded);
+            setNotificationFadedOnChildren(false);
+        } else {
+            // == Delegating Scenario ==
+            // This is the new normal for alpha: Explicitly reset this view's layer type to NONE,
+            // and require that all children use their own hardware layer if they have bad
+            // overlapping rendering.
+            NotificationFadeAware.setLayerTypeForFaded(this, false);
+            setNotificationFadedOnChildren(faded);
+        }
+    }
+
+    /** Private helper for iterating over the layouts and children containers to set faded state */
+    private void setNotificationFadedOnChildren(boolean faded) {
+        delegateNotificationFaded(mChildrenContainer, faded);
+        for (NotificationContentView layout : mLayouts) {
+            delegateNotificationFaded(layout, faded);
+        }
+    }
+
+    private static void delegateNotificationFaded(@Nullable View view, boolean faded) {
+        if (FADE_LAYER_OPTIMIZATION_ENABLED && view instanceof NotificationFadeAware) {
+            ((NotificationFadeAware) view).setNotificationFaded(faded);
+        } else {
+            NotificationFadeAware.setLayerTypeForFaded(view, faded);
+        }
+    }
+
+    /**
+     * Only declare overlapping rendering if independent children of the view require it.
+     */
+    @Override
+    public boolean hasOverlappingRendering() {
+        return super.hasOverlappingRendering() && childrenRequireOverlappingRendering();
+    }
+
+    /**
+     * Because RemoteInputView is designed to be an opaque view that overlaps the Actions row, the
+     * row should require overlapping rendering to ensure that the overlapped view doesn't bleed
+     * through when alpha fading.
+     *
+     * Note that this currently works for top-level notifications which squish their height down
+     * while collapsing the shade, but does not work for children inside groups, because the
+     * accordion affect does not apply to those views, so super.hasOverlappingRendering() will
+     * always return false to avoid the clipping caused when the view's measured height is less than
+     * the 'actual height'.
+     */
+    private boolean childrenRequireOverlappingRendering() {
+        if (!FADE_LAYER_OPTIMIZATION_ENABLED) {
+            return true;
+        }
+        // The colorized background is another layer with which all other elements overlap
+        if (getEntry().getSbn().getNotification().isColorized()) {
+            return true;
+        }
+        // Check if the showing layout has a need for overlapping rendering.
+        // NOTE: We could check both public and private layouts here, but becuause these states
+        //  don't animate well, there are bigger visual artifacts if we start changing the shown
+        //  layout during shade expansion.
+        NotificationContentView showingLayout = getShowingLayout();
+        return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
+    }
+
     @Override
     public int getExtraBottomPadding() {
         if (mIsSummaryWithChildren && isGroupExpanded()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index caba3ac..c661408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -28,6 +28,7 @@
 
 import com.android.internal.widget.ConversationLayout;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
 
 /**
  * A hybrid view which may contain information about one ore more conversations.
@@ -138,4 +139,14 @@
         lp.height = size;
         view.setLayoutParams(lp);
     }
+
+    /**
+     * Apply the faded state as a layer type change to the face pile view which needs to have
+     * overlapping contents render precisely.
+     */
+    @Override
+    public void setNotificationFaded(boolean faded) {
+        super.setNotificationFaded(faded);
+        NotificationFadeAware.setLayerTypeForFaded(mConversationFacePile, faded);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index bc2adac3..c0d85a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -28,13 +28,14 @@
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.ViewTransformationHelper;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
 import com.android.systemui.statusbar.notification.TransformState;
 
 /**
  * A hybrid view which may contain information about one ore more notifications.
  */
 public class HybridNotificationView extends AlphaOptimizedLinearLayout
-        implements TransformableView {
+        implements TransformableView, NotificationFadeAware {
 
     protected final ViewTransformationHelper mTransformationHelper = new ViewTransformationHelper();
     protected TextView mTitleView;
@@ -148,4 +149,7 @@
         setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
         mTransformationHelper.setVisible(visible);
     }
+
+    @Override
+    public void setNotificationFaded(boolean faded) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0ffca30..9cc484c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -71,7 +72,7 @@
  * expanded and heads up layout. This class is responsible for clipping the content and and
  * switching between the expanded, contracted and the heads up view depending on its clipped size.
  */
-public class NotificationContentView extends FrameLayout {
+public class NotificationContentView extends FrameLayout implements NotificationFadeAware {
 
     private static final String TAG = "NotificationContentView";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -1994,4 +1995,39 @@
         }
         return Notification.COLOR_INVALID;
     }
+
+    /**
+     * Delegate the faded state to the notification content views which actually
+     * need to have overlapping contents render precisely.
+     */
+    @Override
+    public void setNotificationFaded(boolean faded) {
+        if (mContractedWrapper != null) {
+            mContractedWrapper.setNotificationFaded(faded);
+        }
+        if (mHeadsUpWrapper != null) {
+            mHeadsUpWrapper.setNotificationFaded(faded);
+        }
+        if (mExpandedWrapper != null) {
+            mExpandedWrapper.setNotificationFaded(faded);
+        }
+        if (mSingleLineView != null) {
+            mSingleLineView.setNotificationFaded(faded);
+        }
+    }
+
+    /**
+     * @return true if a visible view has a remote input active, as this requires that the entire
+     * row report that it has overlapping rendering.
+     */
+    public boolean requireRowToHaveOverlappingRendering() {
+        // This inexpensive check is done on both states to avoid state invalidating the result.
+        if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
+            return true;
+        }
+        if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
index 12e94cb..bb43b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -21,6 +21,7 @@
 import com.android.internal.widget.CachingIconView
 import com.android.internal.widget.CallLayout
 import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationFadeAware
 import com.android.systemui.statusbar.notification.NotificationUtils
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 
@@ -37,6 +38,7 @@
             NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
     private val callLayout: CallLayout = view as CallLayout
 
+    private lateinit var conversationIconContainer: View
     private lateinit var conversationIconView: CachingIconView
     private lateinit var conversationBadgeBg: View
     private lateinit var expandBtn: View
@@ -45,6 +47,8 @@
 
     private fun resolveViews() {
         with(callLayout) {
+            conversationIconContainer =
+                    requireViewById(com.android.internal.R.id.conversation_icon_container)
             conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
             conversationBadgeBg =
                     requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
@@ -82,4 +86,14 @@
     }
 
     override fun getMinLayoutHeight(): Int = minHeightWithActions
+
+    /**
+     * Apply the faded state as a layer type change to the face pile view which needs to have
+     * overlapping contents render precisely.
+     */
+    override fun setNotificationFaded(faded: Boolean) {
+        // Do not call super
+        NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
+        NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 3ef5b665..e136055 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -23,6 +23,7 @@
 import com.android.internal.widget.ConversationLayout
 import com.android.internal.widget.MessagingLinearLayout
 import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationFadeAware
 import com.android.systemui.statusbar.notification.NotificationUtils
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform
@@ -42,6 +43,7 @@
     )
     private val conversationLayout: ConversationLayout = view as ConversationLayout
 
+    private lateinit var conversationIconContainer: View
     private lateinit var conversationIconView: CachingIconView
     private lateinit var conversationBadgeBg: View
     private lateinit var expandBtn: View
@@ -59,6 +61,8 @@
         messagingLinearLayout = conversationLayout.messagingLinearLayout
         imageMessageContainer = conversationLayout.imageMessageContainer
         with(conversationLayout) {
+            conversationIconContainer =
+                    requireViewById(com.android.internal.R.id.conversation_icon_container)
             conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
             conversationBadgeBg =
                     requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
@@ -136,4 +140,10 @@
                 minHeightWithActions
             else
                 super.getMinLayoutHeight()
+
+    override fun setNotificationFaded(faded: Boolean) {
+        // Do not call super
+        NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
+        NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index 4c9c2f9..fdff12d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -22,6 +22,7 @@
 
 import com.android.internal.graphics.ColorUtils;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -86,4 +87,14 @@
     public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
         return true;
     }
+
+    /**
+     * Apply the faded state as a layer type change to the custom view which needs to have
+     * overlapping contents render precisely.
+     */
+    @Override
+    public void setNotificationFaded(boolean faded) {
+        super.setNotificationFaded(faded);
+        NotificationFadeAware.setLayerTypeForFaded(mView, faded);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 8c6fa02..3159539 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -20,6 +20,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -67,4 +68,14 @@
         }
         super.onContentUpdated(row);
     }
+
+    /**
+     * Apply the faded state as a layer type change to the custom view which needs to have
+     * overlapping contents render precisely.
+     */
+    @Override
+    public void setNotificationFaded(boolean faded) {
+        super.setNotificationFaded(faded);
+        NotificationFadeAware.setLayerTypeForFaded(mWrappedView, faded);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 7630191..6c3e0d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -42,6 +42,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
 import com.android.systemui.statusbar.notification.TransformState;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
@@ -395,4 +396,13 @@
      */
     public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
     }
+
+    /**
+     * Apply the faded state as a layer type change to the views which need to have overlapping
+     * contents render precisely.
+     */
+    public void setNotificationFaded(boolean faded) {
+        NotificationFadeAware.setLayerTypeForFaded(getIcon(), faded);
+        NotificationFadeAware.setLayerTypeForFaded(getExpandButton(), faded);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index c2716b9..7ece3d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -38,6 +38,7 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.NotificationGroupingUtil;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -51,7 +52,8 @@
 /**
  * A container containing child notifications
  */
-public class NotificationChildrenContainer extends ViewGroup {
+public class NotificationChildrenContainer extends ViewGroup
+        implements NotificationFadeAware {
 
     @VisibleForTesting
     static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
@@ -111,6 +113,7 @@
     private int mCurrentHeaderTranslation = 0;
     private float mHeaderVisibleAmount = 1.0f;
     private int mUntruncatedChildCount;
+    private boolean mContainingNotificationIsFaded = false;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -277,6 +280,7 @@
         mDividers.add(newIndex, divider);
 
         row.setContentTransformationAmount(0, false /* isLastChild */);
+        row.setNotificationFaded(mContainingNotificationIsFaded);
         // It doesn't make sense to keep old animations around, lets cancel them!
         ExpandableViewState viewState = row.getViewState();
         if (viewState != null) {
@@ -301,6 +305,7 @@
         });
 
         row.setSystemChildExpanded(false);
+        row.setNotificationFaded(false);
         row.setUserLocked(false);
         if (!row.isRemoved()) {
             mGroupingUtil.restoreChildNotification(row);
@@ -1308,4 +1313,18 @@
             mNotificationHeaderWrapperLowPriority.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
         }
     }
+
+    @Override
+    public void setNotificationFaded(boolean faded) {
+        mContainingNotificationIsFaded = faded;
+        if (mNotificationHeaderWrapper != null) {
+            mNotificationHeaderWrapper.setNotificationFaded(faded);
+        }
+        if (mNotificationHeaderWrapperLowPriority != null) {
+            mNotificationHeaderWrapperLowPriority.setNotificationFaded(faded);
+        }
+        for (ExpandableNotificationRow child : mAttachedChildren) {
+            child.setNotificationFaded(faded);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6a12710..ab08865 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -6092,6 +6092,14 @@
         return mExpandHelperCallback;
     }
 
+    float getAppearFraction() {
+        return mLastSentAppear;
+    }
+
+    float getExpandedHeight() {
+        return mLastSentExpandedHeight;
+    }
+
     /** Enum for selecting some or all notification rows (does not included non-notif views). */
     @Retention(SOURCE)
     @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 03fc767..f14cc93c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -858,6 +858,14 @@
         mView.setHeadsUpAppearanceController(controller);
     }
 
+    public float getAppearFraction() {
+        return mView.getAppearFraction();
+    }
+
+    public float getExpandedHeight() {
+        return mView.getExpandedHeight();
+    }
+
     public void requestLayout() {
         mView.requestLayout();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 6d82a45..83bea84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -29,6 +29,7 @@
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -206,14 +207,26 @@
         } else if (view.getAlpha() != this.alpha) {
             // apply layer type
             boolean becomesFullyVisible = this.alpha == 1.0f;
-            boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
-                    && view.hasOverlappingRendering();
-            int layerType = view.getLayerType();
-            int newLayerType = newLayerTypeIsHardware
-                    ? View.LAYER_TYPE_HARDWARE
-                    : View.LAYER_TYPE_NONE;
-            if (layerType != newLayerType) {
-                view.setLayerType(newLayerType, null);
+            boolean becomesFaded = !becomesInvisible && !becomesFullyVisible;
+            if (FadeOptimizedNotification.FADE_LAYER_OPTIMIZATION_ENABLED
+                    && view instanceof FadeOptimizedNotification) {
+                // NOTE: A view that's going to utilize this interface to avoid having a hardware
+                //  layer will have to return false from hasOverlappingRendering(), so we
+                //  intentionally do not check that value in this if, even though we do in the else.
+                FadeOptimizedNotification fadeOptimizedView = (FadeOptimizedNotification) view;
+                boolean isFaded = fadeOptimizedView.isNotificationFaded();
+                if (isFaded != becomesFaded) {
+                    fadeOptimizedView.setNotificationFaded(becomesFaded);
+                }
+            } else {
+                boolean newLayerTypeIsHardware = becomesFaded && view.hasOverlappingRendering();
+                int layerType = view.getLayerType();
+                int newLayerType = newLayerTypeIsHardware
+                        ? View.LAYER_TYPE_HARDWARE
+                        : View.LAYER_TYPE_NONE;
+                if (layerType != newLayerType) {
+                    view.setLayerType(newLayerType, null);
+                }
             }
 
             // apply alpha
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 927b4c8..8a7cf36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -24,6 +24,7 @@
 import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -35,23 +36,29 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.ViewController;
 
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
+import javax.inject.Inject;
+
 /**
  * Controls the appearance of heads up notifications in the icon area and the header itself.
  */
-public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
-        DarkIconDispatcher.DarkReceiver, NotificationWakeUpCoordinator.WakeUpListener {
+@StatusBarFragmentScope
+public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView>
+        implements OnHeadsUpChangedListener,
+        DarkIconDispatcher.DarkReceiver,
+        NotificationWakeUpCoordinator.WakeUpListener {
     public static final int CONTENT_FADE_DURATION = 110;
     public static final int CONTENT_FADE_DELAY = 100;
     private final NotificationIconAreaController mNotificationIconAreaController;
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final NotificationStackScrollLayoutController mStackScrollerController;
-    private final HeadsUpStatusBarView mHeadsUpStatusBarView;
     private final View mCenteredIconView;
     private final View mClockView;
     private final View mOperatorNameView;
@@ -67,8 +74,6 @@
     @VisibleForTesting
     float mExpandedHeight;
     @VisibleForTesting
-    boolean mIsExpanded;
-    @VisibleForTesting
     float mAppearFraction;
     private ExpandableNotificationRow mTrackedChild;
     private boolean mShown;
@@ -83,7 +88,7 @@
     Point mPoint;
     private KeyguardStateController mKeyguardStateController;
 
-
+    @Inject
     public HeadsUpAppearanceController(
             NotificationIconAreaController notificationIconAreaController,
             HeadsUpManagerPhone headsUpManager,
@@ -92,11 +97,15 @@
             KeyguardBypassController keyguardBypassController,
             KeyguardStateController keyguardStateController,
             NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue,
-            NotificationPanelViewController notificationPanelViewController, View statusBarView) {
+            NotificationPanelViewController notificationPanelViewController,
+            @RootView PhoneStatusBarView statusBarView) {
         this(notificationIconAreaController, headsUpManager, statusBarStateController,
                 keyguardBypassController, wakeUpCoordinator, keyguardStateController,
                 commandQueue, notificationStackScrollLayoutController,
                 notificationPanelViewController,
+                // TODO(b/205609837): We should have the StatusBarFragmentComponent provide these
+                //  four views, and then we can delete this constructor and just use the one below
+                //  (which also removes the undesirable @VisibleForTesting).
                 statusBarView.findViewById(R.id.heads_up_status_bar_view),
                 statusBarView.findViewById(R.id.clock),
                 statusBarView.findViewById(R.id.operator_name_frame),
@@ -118,25 +127,27 @@
             View clockView,
             View operatorNameView,
             View centeredIconView) {
+        super(headsUpStatusBarView);
         mNotificationIconAreaController = notificationIconAreaController;
         mHeadsUpManager = headsUpManager;
-        mHeadsUpManager.addListener(this);
-        mHeadsUpStatusBarView = headsUpStatusBarView;
         mCenteredIconView = centeredIconView;
-        headsUpStatusBarView.setOnDrawingRectChangedListener(
-                () -> updateIsolatedIconLocation(true /* requireUpdate */));
+
+        // We may be mid-HUN-expansion when this controller is re-created (for example, if the user
+        // has started pulling down the notification shade from the HUN and then the font size
+        // changes). We need to re-fetch these values since they're used to correctly display the
+        // HUN during this shade expansion.
+        mTrackedChild = notificationPanelViewController.getTrackedHeadsUpNotification();
+        mAppearFraction = stackScrollerController.getAppearFraction();
+        mExpandedHeight = stackScrollerController.getExpandedHeight();
+
         mStackScrollerController = stackScrollerController;
         mNotificationPanelViewController = notificationPanelViewController;
-        notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
-        notificationPanelViewController.setHeadsUpAppearanceController(this);
-        mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
         mStackScrollerController.setHeadsUpAppearanceController(this);
         mClockView = clockView;
         mOperatorNameView = operatorNameView;
         mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
-        mDarkIconDispatcher.addDarkReceiver(this);
 
-        mHeadsUpStatusBarView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+        mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
             @Override
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
@@ -146,21 +157,32 @@
                     // trigger scroller to notify the latest panel translation
                     mStackScrollerController.requestLayout();
                 }
-                mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
+                mView.removeOnLayoutChangeListener(this);
             }
         });
         mBypassController = bypassController;
         mStatusBarStateController = stateController;
         mWakeUpCoordinator = wakeUpCoordinator;
-        wakeUpCoordinator.addListener(this);
         mCommandQueue = commandQueue;
         mKeyguardStateController = keyguardStateController;
     }
 
+    @Override
+    protected void onViewAttached() {
+        mHeadsUpManager.addListener(this);
+        mView.setOnDrawingRectChangedListener(
+                () -> updateIsolatedIconLocation(true /* requireUpdate */));
+        mWakeUpCoordinator.addListener(this);
+        mNotificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+        mNotificationPanelViewController.setHeadsUpAppearanceController(this);
+        mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
+        mDarkIconDispatcher.addDarkReceiver(this);
+    }
 
-    public void destroy() {
+    @Override
+    protected void onViewDetached() {
         mHeadsUpManager.removeListener(this);
-        mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
+        mView.setOnDrawingRectChangedListener(null);
         mWakeUpCoordinator.removeListener(this);
         mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
         mNotificationPanelViewController.setHeadsUpAppearanceController(null);
@@ -170,7 +192,7 @@
 
     private void updateIsolatedIconLocation(boolean requireStateUpdate) {
         mNotificationIconAreaController.setIsolatedIconLocation(
-                mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
+                mView.getIconDrawingRect(), requireStateUpdate);
     }
 
     @Override
@@ -184,20 +206,20 @@
         if (shouldBeVisible()) {
             newEntry = mHeadsUpManager.getTopEntry();
         }
-        NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
-        mHeadsUpStatusBarView.setEntry(newEntry);
+        NotificationEntry previousEntry = mView.getShowingEntry();
+        mView.setEntry(newEntry);
         if (newEntry != previousEntry) {
             boolean animateIsolation = false;
             if (newEntry == null) {
                 // no heads up anymore, lets start the disappear animation
 
                 setShown(false);
-                animateIsolation = !mIsExpanded;
+                animateIsolation = !isExpanded();
             } else if (previousEntry == null) {
                 // We now have a headsUp and didn't have one before. Let's start the disappear
                 // animation
                 setShown(true);
-                animateIsolation = !mIsExpanded;
+                animateIsolation = !isExpanded();
             }
             updateIsolatedIconLocation(false /* requireUpdate */);
             mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
@@ -210,8 +232,8 @@
             mShown = isShown;
             if (isShown) {
                 updateParentClipping(false /* shouldClip */);
-                mHeadsUpStatusBarView.setVisibility(View.VISIBLE);
-                show(mHeadsUpStatusBarView);
+                mView.setVisibility(View.VISIBLE);
+                show(mView);
                 hide(mClockView, View.INVISIBLE);
                 if (mCenteredIconView.getVisibility() != View.GONE) {
                     hide(mCenteredIconView, View.INVISIBLE);
@@ -227,21 +249,21 @@
                 if (mOperatorNameView != null) {
                     show(mOperatorNameView);
                 }
-                hide(mHeadsUpStatusBarView, View.GONE, () -> {
+                hide(mView, View.GONE, () -> {
                     updateParentClipping(true /* shouldClip */);
                 });
             }
             // Show the status bar icons when the view gets shown / hidden
             if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
                 mCommandQueue.recomputeDisableFlags(
-                        mHeadsUpStatusBarView.getContext().getDisplayId(), false);
+                        mView.getContext().getDisplayId(), false);
             }
         }
     }
 
     private void updateParentClipping(boolean shouldClip) {
         ViewClippingUtil.setClippingDeactivated(
-                mHeadsUpStatusBarView, !shouldClip, mParentClippingParams);
+                mView, !shouldClip, mParentClippingParams);
     }
 
     /**
@@ -310,7 +332,7 @@
      */
     public boolean shouldBeVisible() {
         boolean notificationsShown = !mWakeUpCoordinator.getNotificationsFullyHidden();
-        boolean canShow = !mIsExpanded && notificationsShown;
+        boolean canShow = !isExpanded() && notificationsShown;
         if (mBypassController.getBypassEnabled() &&
                 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD
                         || mKeyguardStateController.isKeyguardGoingAway())
@@ -328,17 +350,17 @@
 
     public void setAppearFraction(float expandedHeight, float appearFraction) {
         boolean changed = expandedHeight != mExpandedHeight;
+        boolean oldIsExpanded = isExpanded();
+
         mExpandedHeight = expandedHeight;
         mAppearFraction = appearFraction;
-        boolean isExpanded = expandedHeight > 0;
         // We only notify if the expandedHeight changed and not on the appearFraction, since
         // otherwise we may run into an infinite loop where the panel and this are constantly
         // updating themselves over just a small fraction
         if (changed) {
             updateHeadsUpHeaders();
         }
-        if (isExpanded != mIsExpanded) {
-            mIsExpanded = isExpanded;
+        if (isExpanded() != oldIsExpanded) {
             updateTopEntry();
         }
     }
@@ -358,6 +380,10 @@
         }
     }
 
+    private boolean isExpanded() {
+        return mExpandedHeight > 0;
+    }
+
     private void updateHeadsUpHeaders() {
         mHeadsUpManager.getAllEntries().forEach(entry -> {
             updateHeader(entry);
@@ -376,22 +402,13 @@
 
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
-        mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
+        mView.onDarkChanged(area, darkIntensity, tint);
     }
 
     public void onStateChanged() {
         updateTopEntry();
     }
 
-    void readFrom(HeadsUpAppearanceController oldController) {
-        if (oldController != null) {
-            mTrackedChild = oldController.mTrackedChild;
-            mExpandedHeight = oldController.mExpandedHeight;
-            mIsExpanded = oldController.mIsExpanded;
-            mAppearFraction = oldController.mAppearFraction;
-        }
-    }
-
     @Override
     public void onFullyHiddenChanged(boolean isFullyHidden) {
         updateTopEntry();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 570b0ca..88ae0db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -37,7 +37,6 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.policy.BatteryController;
 
 import java.io.FileDescriptor;
@@ -251,7 +250,7 @@
 
     private void updateNavigation() {
         if (mNavigationBarController != null
-                && !QuickStepContract.isGesturalMode(mNavigationMode)) {
+                && mNavigationBarController.supportsIconTintForNavMode(mNavigationMode)) {
             mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 9021b74..415fb92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -28,6 +28,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -230,6 +231,14 @@
     }
 
     /**
+     * Return whether to use the tint calculated in this class for nav icons.
+     */
+    public boolean supportsIconTintForNavMode(int navigationMode) {
+        // In gesture mode, we already do region sampling to update tint based on content beneath.
+        return !QuickStepContract.isGesturalMode(navigationMode);
+    }
+
+    /**
      * Interface to apply a specific dark intensity.
      */
     public interface DarkIntensityApplier {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 3f33281..68ab077 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -124,6 +124,9 @@
                         public void onAnimationEnd(Animator a) {
                             mLightsOutNotifView.setAlpha(showDot ? 1 : 0);
                             mLightsOutNotifView.setVisibility(showDot ? View.VISIBLE : View.GONE);
+                            // Unset the listener, otherwise this may persist for
+                            // another view property animation
+                            mLightsOutNotifView.animate().setListener(null);
                         }
                     })
                     .start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 9b5fa2a..0312c30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -88,6 +88,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 
+import androidx.annotation.Nullable;
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -451,9 +452,13 @@
     private boolean mUserSetupComplete;
     private boolean mHideIconsDuringLaunchAnimation = true;
     private int mStackScrollerMeasuringPass;
-    private ArrayList<Consumer<ExpandableNotificationRow>>
-            mTrackingHeadsUpListeners =
-            new ArrayList<>();
+    /**
+     * Non-null if there's a heads-up notification that we're currently tracking the position of.
+     */
+    @Nullable
+    private ExpandableNotificationRow mTrackedHeadsUpNotification;
+    private final ArrayList<Consumer<ExpandableNotificationRow>>
+            mTrackingHeadsUpListeners = new ArrayList<>();
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
 
     private int mPanelAlpha;
@@ -3050,18 +3055,24 @@
         mQsExpandImmediate = false;
         mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false);
         mTwoFingerQsExpandPossible = false;
-        notifyListenersTrackingHeadsUp(null);
+        updateTrackingHeadsUp(null);
         mExpandingFromHeadsUp = false;
         setPanelScrimMinFraction(0.0f);
     }
 
-    private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+    private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) {
+        mTrackedHeadsUpNotification = pickedChild;
         for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
             Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
             listener.accept(pickedChild);
         }
     }
 
+    @Nullable
+    public ExpandableNotificationRow getTrackedHeadsUpNotification() {
+        return mTrackedHeadsUpNotification;
+    }
+
     private void setListening(boolean listening) {
         mKeyguardStatusBarViewController.setBatteryListening(listening);
         if (mQs == null) return;
@@ -3298,7 +3309,7 @@
 
     public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
         if (pickedChild != null) {
-            notifyListenersTrackingHeadsUp(pickedChild);
+            updateTrackingHeadsUp(pickedChild);
             mExpandingFromHeadsUp = true;
         }
         // otherwise we update the state when the expansion is finished
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 9cefded..bf54677 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -120,6 +120,9 @@
                         setAlpha(1f);
                         setTranslationX(0);
                         cancelLongClick();
+                        // Unset the listener, otherwise this may persist for
+                        // another view property animation
+                        animate().setListener(null);
                     }
 
                     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index c814622..a4feeab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.qs.carrier.QSCarrierGroupController
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
 import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
 import javax.inject.Inject
 import javax.inject.Named
@@ -35,7 +36,7 @@
     private val statusBarIconController: StatusBarIconController,
     qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
     featureFlags: FeatureFlags,
-    batteryMeterViewController: BatteryMeterViewController
+    @Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController
 ) {
 
     companion object {
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 e033c47..50a320c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -150,6 +150,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.ExtensionFragmentListener;
 import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.KeyguardService;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -489,7 +490,6 @@
     private final DozeParameters mDozeParameters;
     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
     private final StatusBarComponent.Factory mStatusBarComponentFactory;
-    private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
     private final PluginManager mPluginManager;
     private final Optional<LegacySplitScreen> mSplitScreenOptional;
     private final StatusBarNotificationActivityStarter.Builder
@@ -538,7 +538,7 @@
     protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final BrightnessSliderController.Factory mBrightnessSliderFactory;
     private final FeatureFlags mFeatureFlags;
-
+    private final FragmentService mFragmentService;
     private final WallpaperController mWallpaperController;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private final MessageRouter mMessageRouter;
@@ -546,6 +546,8 @@
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     private final TunerService mTunerService;
 
+    private StatusBarComponent mStatusBarComponent;
+
     // Flags for disabling the status bar
     // Two variables becaseu the first one evidently ran out of room for new flags.
     private int mDisabled1 = 0;
@@ -662,7 +664,6 @@
     private boolean mNoAnimationOnNextBarModeChange;
     private final SysuiStatusBarStateController mStatusBarStateController;
 
-    private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
     private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     protected StatusBarNotificationPresenter mPresenter;
@@ -690,6 +691,7 @@
     public StatusBar(
             Context context,
             NotificationsController notificationsController,
+            FragmentService fragmentService,
             LightBarController lightBarController,
             AutoHideController autoHideController,
             StatusBarWindowController statusBarWindowController,
@@ -747,7 +749,6 @@
             CommandQueue commandQueue,
             CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
             StatusBarComponent.Factory statusBarComponentFactory,
-            StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
             PluginManager pluginManager,
             Optional<LegacySplitScreen> splitScreenOptional,
             LightsOutNotifController lightsOutNotifController,
@@ -791,6 +792,7 @@
             ActivityLaunchAnimator activityLaunchAnimator) {
         super(context);
         mNotificationsController = notificationsController;
+        mFragmentService = fragmentService;
         mLightBarController = lightBarController;
         mAutoHideController = autoHideController;
         mStatusBarWindowController = statusBarWindowController;
@@ -852,7 +854,6 @@
         mCommandQueue = commandQueue;
         mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
         mStatusBarComponentFactory = statusBarComponentFactory;
-        mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
         mPluginManager = pluginManager;
         mSplitScreenOptional = splitScreenOptional;
         mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
@@ -1162,21 +1163,6 @@
                     mNotificationPanelViewController.updatePanelExpansionAndVisibility();
                     setBouncerShowingForStatusBarComponents(mBouncerShowing);
 
-                    HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
-                    if (mHeadsUpAppearanceController != null) {
-                        // This view is being recreated, let's destroy the old one
-                        mHeadsUpAppearanceController.destroy();
-                    }
-                    // TODO (b/136993073) Separate notification shade and status bar
-                    // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
-                    mHeadsUpAppearanceController = new HeadsUpAppearanceController(
-                            mNotificationIconAreaController, mHeadsUpManager,
-                            mStackScrollerController,
-                            mStatusBarStateController, mKeyguardBypassController,
-                            mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
-                            mNotificationPanelViewController, mStatusBarView);
-                    mHeadsUpAppearanceController.readFrom(oldController);
-
                     mLightsOutNotifController.setLightsOutNotifView(
                             mStatusBarView.findViewById(R.id.notification_lights_out));
                     mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
@@ -1184,24 +1170,7 @@
                 }).getFragmentManager()
                 .beginTransaction()
                 .replace(R.id.status_bar_container,
-                        new CollapsedStatusBarFragment(
-                                mStatusBarFragmentComponentFactory,
-                                mOngoingCallController,
-                                mAnimationScheduler,
-                                mStatusBarLocationPublisher,
-                                mNotificationIconAreaController,
-                                mPanelExpansionStateManager,
-                                mFeatureFlags,
-                                () -> Optional.of(this),
-                                mStatusBarIconController,
-                                mStatusBarHideIconsForBouncerManager,
-                                mKeyguardStateController,
-                                mNetworkController,
-                                mStatusBarStateController,
-                                mCommandQueue,
-                                mCollapsedStatusBarFragmentLogger,
-                                mOperatorNameViewControllerFactory
-                        ),
+                        mStatusBarComponent.createCollapsedStatusBarFragment(),
                         CollapsedStatusBarFragment.TAG)
                 .commit();
 
@@ -1557,32 +1526,34 @@
     }
 
     private void inflateStatusBarWindow() {
-        StatusBarComponent statusBarComponent = mStatusBarComponentFactory.create();
-        mNotificationShadeWindowView = statusBarComponent.getNotificationShadeWindowView();
-        mNotificationShadeWindowViewController = statusBarComponent
+        mStatusBarComponent = mStatusBarComponentFactory.create();
+        mFragmentService.addFragmentInstantiationProvider(mStatusBarComponent);
+
+        mNotificationShadeWindowView = mStatusBarComponent.getNotificationShadeWindowView();
+        mNotificationShadeWindowViewController = mStatusBarComponent
                 .getNotificationShadeWindowViewController();
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowViewController.setupExpandedStatusBar();
-        mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
-        statusBarComponent.getLockIconViewController().init();
-        mStackScrollerController = statusBarComponent.getNotificationStackScrollLayoutController();
+        mNotificationPanelViewController = mStatusBarComponent.getNotificationPanelViewController();
+        mStatusBarComponent.getLockIconViewController().init();
+        mStackScrollerController = mStatusBarComponent.getNotificationStackScrollLayoutController();
         mStackScroller = mStackScrollerController.getView();
 
-        mNotificationShelfController = statusBarComponent.getNotificationShelfController();
-        mAuthRippleController = statusBarComponent.getAuthRippleController();
+        mNotificationShelfController = mStatusBarComponent.getNotificationShelfController();
+        mAuthRippleController = mStatusBarComponent.getAuthRippleController();
         mAuthRippleController.init();
 
-        mHeadsUpManager.addListener(statusBarComponent.getStatusBarHeadsUpChangeListener());
+        mHeadsUpManager.addListener(mStatusBarComponent.getStatusBarHeadsUpChangeListener());
 
-        mHeadsUpManager.addListener(statusBarComponent.getStatusBarHeadsUpChangeListener());
+        mHeadsUpManager.addListener(mStatusBarComponent.getStatusBarHeadsUpChangeListener());
 
         // Listen for demo mode changes
-        mDemoModeController.addCallback(statusBarComponent.getStatusBarDemoMode());
+        mDemoModeController.addCallback(mStatusBarComponent.getStatusBarDemoMode());
 
         if (mCommandQueueCallbacks != null) {
             mCommandQueue.removeCallback(mCommandQueueCallbacks);
         }
-        mCommandQueueCallbacks = statusBarComponent.getStatusBarCommandQueueCallbacks();
+        mCommandQueueCallbacks = mStatusBarComponent.getStatusBarCommandQueueCallbacks();
         // Connect in to the status bar manager service
         mCommandQueue.addCallback(mCommandQueueCallbacks);
     }
@@ -1917,10 +1888,6 @@
         mScrimController.setKeyguardOccluded(occluded);
     }
 
-    public boolean headsUpShouldBeVisible() {
-        return mHeadsUpAppearanceController.shouldBeVisible();
-    }
-
     /** A launch animation was cancelled. */
     //TODO: These can / should probably be moved to NotificationPresenter or ShadeController
     public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
@@ -2000,7 +1967,7 @@
         }
     }
 
-    public void maybeEscalateHeadsUp() {
+    private void maybeEscalateHeadsUp() {
         mHeadsUpManager.getAllEntries().forEach(entry -> {
             final StatusBarNotification sbn = entry.getSbn();
             final Notification notification = sbn.getNotification();
@@ -2011,6 +1978,7 @@
                 try {
                     EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
                             sbn.getKey());
+                    wakeUpForFullScreenIntent();
                     notification.fullScreenIntent.send();
                     entry.notifyFullScreenIntentLaunched();
                 } catch (PendingIntent.CanceledException e) {
@@ -2020,6 +1988,17 @@
         mHeadsUpManager.releaseAllImmediately();
     }
 
+    void wakeUpForFullScreenIntent() {
+        if (isGoingToSleep() || mDozing) {
+            mPowerManager.wakeUp(
+                    SystemClock.uptimeMillis(),
+                    PowerManager.WAKE_REASON_APPLICATION,
+                    "com.android.systemui:full_screen_intent");
+            mWakeUpComingFromTouch = false;
+            mWakeUpTouchLocation = null;
+        }
+    }
+
     void makeExpandedVisible(boolean force) {
         if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
         if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
@@ -3549,7 +3528,7 @@
             DejankUtils.startDetectingBlockingIpcs(tag);
             updateRevealEffect(false /* wakingUp */);
             updateNotificationPanelTouchState();
-            notifyHeadsUpGoingToSleep();
+            maybeEscalateHeadsUp();
             dismissVolumeDialog();
             mWakeUpCoordinator.setFullyAwake(false);
             mBypassHeadsUpNotifier.setFullyAwake(false);
@@ -4094,10 +4073,6 @@
         }
     }
 
-    protected void notifyHeadsUpGoingToSleep() {
-        maybeEscalateHeadsUp();
-    }
-
     /**
      * @return Whether the security bouncer from Keyguard is showing.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0d23d66..e2bf0db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -555,12 +555,13 @@
     public void onStartedWakingUp() {
         mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
                 .setAnimationsDisabled(false);
-        View currentView = getCurrentNavBarView();
-        if (currentView != null) {
-            currentView.animate()
-                    .alpha(1f)
-                    .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
-                    .start();
+        NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+        if (navBarView != null) {
+            navBarView.forEachView(view ->
+                    view.animate()
+                            .alpha(1f)
+                            .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+                            .start());
         }
     }
 
@@ -568,12 +569,13 @@
     public void onStartedGoingToSleep() {
         mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
                 .setAnimationsDisabled(true);
-        View currentView = getCurrentNavBarView();
-        if (currentView != null) {
-            currentView.animate()
-                    .alpha(0f)
-                    .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
-                    .start();
+        NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+        if (navBarView != null) {
+            navBarView.forEachView(view ->
+                    view.animate()
+                            .alpha(0f)
+                            .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+                            .start());
         }
     }
 
@@ -1015,17 +1017,6 @@
         mStatusBar.onKeyguardViewManagerStatesUpdated();
     }
 
-    /**
-     * Updates the visibility of the nav bar content views.
-     */
-    private void updateNavigationBarContentVisibility(boolean navBarContentVisible) {
-        final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
-        if (navBarView != null && navBarView.getCurrentView() != null) {
-            final View currentView = navBarView.getCurrentView();
-            currentView.setVisibility(navBarContentVisible ? View.VISIBLE : View.INVISIBLE);
-        }
-    }
-
     private View getCurrentNavBarView() {
         final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
         return navBarView != null ? navBarView.getCurrentView() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index dba3b24..ce38111 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -41,6 +41,8 @@
 import android.util.EventLog;
 import android.view.View;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -593,7 +595,8 @@
         }
     }
 
-    private void handleFullScreenIntent(NotificationEntry entry) {
+    @VisibleForTesting
+    void handleFullScreenIntent(NotificationEntry entry) {
         if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
             if (shouldSuppressFullScreenIntent(entry)) {
                 mLogger.logFullScreenIntentSuppressedByDnD(entry.getKey());
@@ -617,6 +620,7 @@
                 try {
                     EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
                             entry.getKey());
+                    mStatusBar.wakeUpForFullScreenIntent();
                     fullscreenIntent.send();
                     entry.notifyFullScreenIntentLaunched();
                     mMetricsLogger.count("note_fullscreen", 1);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index e06605e..375641f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
 import com.android.systemui.statusbar.phone.StatusBarDemoMode;
 import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
@@ -38,7 +39,13 @@
 import dagger.Subcomponent;
 
 /**
- * Dagger subcomponent tied to the lifecycle of StatusBar views.
+ * Dagger subcomponent for classes (semi-)related to the status bar. The component is created once
+ * inside {@link com.android.systemui.statusbar.phone.StatusBar} and never re-created.
+ *
+ * TODO(b/197137564): This should likely be re-factored a bit. It includes classes that aren't
+ * directly related to status bar functionality, like multiple notification classes. And, the fact
+ * that it has many getter methods indicates that we need to access many of these classes from
+ * outside the component. Should more items be moved *into* this component to avoid so many getters?
  */
 @Subcomponent(modules = {StatusBarViewModule.class})
 @StatusBarComponent.StatusBarScope
@@ -121,4 +128,10 @@
      */
     @StatusBarScope
     SplitShadeHeaderController getSplitShadeHeaderController();
+
+    /**
+     * Creates a new {@link CollapsedStatusBarFragment} each time it's called. See
+     * {@link StatusBarViewModule#createCollapsedStatusBarFragment}.
+     */
+    CollapsedStatusBarFragment createCollapsedStatusBarFragment();
 }
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 27ece84..50e6151 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
@@ -39,6 +39,7 @@
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
@@ -100,7 +101,6 @@
 import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -143,6 +143,7 @@
     static StatusBar provideStatusBar(
             Context context,
             NotificationsController notificationsController,
+            FragmentService fragmentService,
             LightBarController lightBarController,
             AutoHideController autoHideController,
             StatusBarWindowController statusBarWindowController,
@@ -200,7 +201,6 @@
             CommandQueue commandQueue,
             CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
             StatusBarComponent.Factory statusBarComponentFactory,
-            StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
             PluginManager pluginManager,
             Optional<LegacySplitScreen> splitScreenOptional,
             LightsOutNotifController lightsOutNotifController,
@@ -245,6 +245,7 @@
         return new StatusBar(
                 context,
                 notificationsController,
+                fragmentService,
                 lightBarController,
                 autoHideController,
                 statusBarWindowController,
@@ -302,7 +303,6 @@
                 commandQueue,
                 collapsedStatusBarFragmentLogger,
                 statusBarComponentFactory,
-                statusBarFragmentComponentFactory,
                 pluginManager,
                 splitScreenOptional,
                 lightsOutNotifController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 2765fe3..32f3ba4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.phone.dagger;
 
 import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewStub;
@@ -24,26 +26,54 @@
 import com.android.keyguard.LockIconView;
 import com.android.systemui.R;
 import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.biometrics.AuthRippleView;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.OperatorNameViewController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
 import com.android.systemui.statusbar.phone.TapAgainView;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.tuner.TunerService;
+
+import java.util.Optional;
 
 import javax.inject.Named;
 
+import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
-@Module
+@Module(subcomponents = StatusBarFragmentComponent.class)
 public abstract class StatusBarViewModule {
 
     public static final String SPLIT_SHADE_HEADER = "split_shade_header";
+    private static final String SPLIT_SHADE_BATTERY_VIEW = "split_shade_battery_view";
+    public static final String SPLIT_SHADE_BATTERY_CONTROLLER = "split_shade_battery_controller";
 
     /** */
     @Provides
@@ -143,10 +173,34 @@
     /** */
     @Provides
     @StatusBarComponent.StatusBarScope
+    @Named(SPLIT_SHADE_BATTERY_VIEW)
     static BatteryMeterView getBatteryMeterView(@Named(SPLIT_SHADE_HEADER) View view) {
         return view.findViewById(R.id.batteryRemainingIcon);
     }
 
+    @Provides
+    @StatusBarComponent.StatusBarScope
+    @Named(SPLIT_SHADE_BATTERY_CONTROLLER)
+    static BatteryMeterViewController getBatteryMeterViewController(
+            @Named(SPLIT_SHADE_BATTERY_VIEW) BatteryMeterView batteryMeterView,
+            ConfigurationController configurationController,
+            TunerService tunerService,
+            BroadcastDispatcher broadcastDispatcher,
+            @Main Handler mainHandler,
+            ContentResolver contentResolver,
+            BatteryController batteryController
+    ) {
+        return new BatteryMeterViewController(
+                batteryMeterView,
+                configurationController,
+                tunerService,
+                broadcastDispatcher,
+                mainHandler,
+                contentResolver,
+                batteryController);
+
+    }
+
     /** */
     @Provides
     @StatusBarComponent.StatusBarScope
@@ -161,4 +215,54 @@
             NotificationShadeWindowView notificationShadeWindowView) {
         return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
     }
+
+    /**
+     * Creates a new {@link CollapsedStatusBarFragment}.
+     *
+     * **IMPORTANT**: This method intentionally does not have
+     * {@link StatusBarComponent.StatusBarScope}, which means a new fragment *will* be created each
+     * time this method is called. This is intentional because we need fragments to re-created in
+     * certain lifecycle scenarios.
+     *
+     * **IMPORTANT**: This method also intentionally does not have a {@link Provides} annotation. If
+     * you need to get access to a {@link CollapsedStatusBarFragment}, go through
+     * {@link StatusBarFragmentComponent} instead.
+     */
+    public static CollapsedStatusBarFragment createCollapsedStatusBarFragment(
+            StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
+            OngoingCallController ongoingCallController,
+            SystemStatusAnimationScheduler animationScheduler,
+            StatusBarLocationPublisher locationPublisher,
+            NotificationIconAreaController notificationIconAreaController,
+            PanelExpansionStateManager panelExpansionStateManager,
+            FeatureFlags featureFlags,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+            StatusBarIconController statusBarIconController,
+            StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
+            KeyguardStateController keyguardStateController,
+            NotificationPanelViewController notificationPanelViewController,
+            NetworkController networkController,
+            StatusBarStateController statusBarStateController,
+            CommandQueue commandQueue,
+            CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
+            OperatorNameViewController.Factory operatorNameViewControllerFactory
+    ) {
+        return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
+                ongoingCallController,
+                animationScheduler,
+                locationPublisher,
+                notificationIconAreaController,
+                panelExpansionStateManager,
+                featureFlags,
+                statusBarOptionalLazy,
+                statusBarIconController,
+                statusBarHideIconsForBouncerManager,
+                keyguardStateController,
+                notificationPanelViewController,
+                networkController,
+                statusBarStateController,
+                commandQueue,
+                collapsedStatusBarFragmentLogger,
+                operatorNameViewControllerFactory);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 9bcf4a5..d4d8907 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
@@ -94,6 +95,7 @@
     private PhoneStatusBarView mStatusBar;
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardStateController mKeyguardStateController;
+    private final NotificationPanelViewController mNotificationPanelViewController;
     private final NetworkController mNetworkController;
     private LinearLayout mSystemIconArea;
     private View mClockView;
@@ -147,6 +149,7 @@
             StatusBarIconController statusBarIconController,
             StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
             KeyguardStateController keyguardStateController,
+            NotificationPanelViewController notificationPanelViewController,
             NetworkController networkController,
             StatusBarStateController statusBarStateController,
             CommandQueue commandQueue,
@@ -164,6 +167,7 @@
         mStatusBarIconController = statusBarIconController;
         mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
         mKeyguardStateController = keyguardStateController;
+        mNotificationPanelViewController = notificationPanelViewController;
         mNetworkController = networkController;
         mStatusBarStateController = statusBarStateController;
         mCommandQueue = commandQueue;
@@ -325,8 +329,8 @@
     }
 
     protected int adjustDisableFlags(int state) {
-        boolean headsUpVisible = mStatusBarOptionalLazy.get()
-                .map(StatusBar::headsUpShouldBeVisible).orElse(false);
+        boolean headsUpVisible =
+                mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
         if (headsUpVisible) {
             state |= DISABLE_CLOCK;
         }
@@ -354,8 +358,7 @@
         // The shelf will be hidden when dozing with a custom clock, we must show notification
         // icons in this occasion.
         if (mStatusBarStateController.isDozing()
-                && mStatusBarOptionalLazy.get().map(
-                        sb -> sb.getPanelController().hasCustomClock()).orElse(false)) {
+                && mNotificationPanelViewController.hasCustomClock()) {
             state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
         }
 
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 47c1875..7e39664 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
@@ -18,6 +18,7 @@
 
 import com.android.systemui.battery.BatteryMeterViewController;
 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.fragment.CollapsedStatusBarFragment;
 
@@ -56,6 +57,7 @@
         // No one accesses this controller, so we need to make sure we reference it here so it does
         // get initialized.
         getBatteryMeterViewController().init();
+        getHeadsUpAppearanceController().init();
     }
 
     /** */
@@ -66,4 +68,8 @@
     @StatusBarFragmentScope
     @RootView
     PhoneStatusBarView getPhoneStatusBarView();
+
+    /** */
+    @StatusBarFragmentScope
+    HeadsUpAppearanceController getHeadsUpAppearanceController();
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
new file mode 100644
index 0000000..77c837b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
@@ -0,0 +1,82 @@
+package com.android.systemui.fragments
+
+import android.app.Fragment
+import android.os.Looper
+import android.test.suitebuilder.annotation.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.qs.QSFragment
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class FragmentServiceTest : SysuiTestCase() {
+    private val fragmentCreator = TestFragmentCreator()
+    private val fragmentCreatorFactory = FragmentService.FragmentCreator.Factory { fragmentCreator }
+
+    private lateinit var fragmentService: FragmentService
+
+    @Before
+    fun setUp() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare()
+        }
+
+        fragmentService = FragmentService(fragmentCreatorFactory, mock(), DumpManager())
+    }
+
+    @Test
+    fun constructor_addsFragmentCreatorMethodsToMap() {
+        val map = fragmentService.injectionMap
+        assertThat(map).hasSize(2)
+        assertThat(map.keys).contains(QSFragment::class.java.name)
+        assertThat(map.keys).contains(TestFragmentInCreator::class.java.name)
+    }
+
+    @Test
+    fun addFragmentInstantiationProvider_objectHasNoFragmentMethods_nothingAdded() {
+        fragmentService.addFragmentInstantiationProvider(Object())
+
+        assertThat(fragmentService.injectionMap).hasSize(2)
+    }
+
+    @Test
+    fun addFragmentInstantiationProvider_objectHasFragmentMethods_methodsAdded() {
+        fragmentService.addFragmentInstantiationProvider(
+            @Suppress("unused")
+            object : Any() {
+                fun createTestFragment2() = TestFragment2()
+                fun createTestFragment3() = TestFragment3()
+            }
+        )
+
+        val map = fragmentService.injectionMap
+        assertThat(map).hasSize(4)
+        assertThat(map.keys).contains(TestFragment2::class.java.name)
+        assertThat(map.keys).contains(TestFragment3::class.java.name)
+    }
+
+    @Test
+    fun addFragmentInstantiationProvider_objectFragmentMethodsAlreadyProvided_nothingAdded() {
+        fragmentService.addFragmentInstantiationProvider(
+            @Suppress("unused")
+            object : Any() {
+                fun createTestFragment() = TestFragmentInCreator()
+            }
+        )
+
+        assertThat(fragmentService.injectionMap).hasSize(2)
+    }
+
+    class TestFragmentCreator : FragmentService.FragmentCreator {
+        override fun createQSFragment(): QSFragment = mock()
+        @Suppress("unused")
+        fun createTestFragment(): TestFragmentInCreator = TestFragmentInCreator()
+    }
+
+    class TestFragmentInCreator : Fragment()
+    class TestFragment2 : Fragment()
+    class TestFragment3 : Fragment()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 3b1c5f3..bf5522c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -54,6 +54,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.model.SysUiState;
@@ -115,6 +116,7 @@
     @Mock private UserContextProvider mUserContextProvider;
     @Mock private StatusBar mStatusBar;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
 
     private TestableLooper mTestableLooper;
 
@@ -159,8 +161,8 @@
                 mHandler,
                 mPackageManager,
                 Optional.of(mStatusBar),
-                mKeyguardUpdateMonitor
-        );
+                mKeyguardUpdateMonitor,
+                mDialogLaunchAnimator);
         mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
 
         ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -218,7 +220,7 @@
         GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
 
         GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
-        gestureListener.onSingleTapConfirmed(null);
+        gestureListener.onSingleTapUp(null);
         verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
     }
 
@@ -444,12 +446,12 @@
 
         // When entering power menu from lockscreen, with smart lock enabled
         when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
-        mGlobalActionsDialogLite.showOrHideDialog(true, true);
+        mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
 
         // Then smart lock will be disabled
         verify(mLockPatternUtils).requireCredentialEntry(eq(user));
 
         // hide dialog again
-        mGlobalActionsDialogLite.showOrHideDialog(true, true);
+        mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
     }
 }
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 734faec..ed5b8cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -80,7 +80,7 @@
         when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent);
         when(mUserTracker.getUserId()).thenReturn(1);
 
-        mNavBarHelper = new NavBarHelper(mAccessibilityManager,
+        mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
                 mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver,
                 mOverviewProxyService, mAssistManagerLazy, mNavigationModeController,
                 mUserTracker, mDumpManager);
@@ -96,13 +96,13 @@
 
     @Test
     public void registerAssistantContentObserver() {
-        mNavBarHelper.init(mContext);
+        mNavBarHelper.init();
         verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
     }
 
     @Test
     public void callbacksFiredWhenRegistering() {
-        mNavBarHelper.init(mContext);
+        mNavBarHelper.init();
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
         verify(mNavbarTaskbarStateUpdater, times(1))
                 .updateAccessibilityServicesState();
@@ -112,7 +112,7 @@
 
     @Test
     public void assistantCallbacksFiredAfterConnecting() {
-        mNavBarHelper.init(mContext);
+        mNavBarHelper.init();
         // 1st set of callbacks get called when registering
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
 
@@ -133,7 +133,7 @@
 
     @Test
     public void a11yCallbacksFiredAfterModeChange() {
-        mNavBarHelper.init(mContext);
+        mNavBarHelper.init();
         // 1st set of callbacks get called when registering
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
 
@@ -146,7 +146,7 @@
 
     @Test
     public void assistantCallbacksFiredAfterNavModeChange() {
-        mNavBarHelper.init(mContext);
+        mNavBarHelper.init();
         // 1st set of callbacks get called when registering
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
 
@@ -159,7 +159,7 @@
 
     @Test
     public void removeListenerNoCallbacksFired() {
-        mNavBarHelper.init(mContext);
+        mNavBarHelper.init();
         // 1st set of callbacks get called when registering
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 31d8830..9d2541c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import org.junit.After;
@@ -86,7 +87,8 @@
                         mock(TaskbarDelegate.class),
                         mNavigationBarFactory,
                         mock(DumpManager.class),
-                        mock(AutoHideController.class)));
+                        mock(AutoHideController.class),
+                        mock(LightBarController.class)));
         initializeNavigationBars();
     }
 
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/qs/user/UserDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
deleted file mode 100644
index d5fe588..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.user
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.View
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class UserDialogTest : SysuiTestCase() {
-
-    private lateinit var dialog: UserDialog
-
-    @Before
-    fun setUp() {
-        dialog = UserDialog(mContext)
-    }
-
-    @After
-    fun tearDown() {
-        dialog.dismiss()
-    }
-
-    @Test
-    fun doneButtonExists() {
-        assertThat(dialog.doneButton).isInstanceOf(View::class.java)
-    }
-
-    @Test
-    fun settingsButtonExists() {
-        assertThat(dialog.settingsButton).isInstanceOf(View::class.java)
-    }
-
-    @Test
-    fun gridExistsAndIsViewGroup() {
-        assertThat(dialog.grid).isInstanceOf(ViewGroup::class.java)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index ea3a42c..3c4a557 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.user
 
 import android.app.Dialog
+import android.content.DialogInterface
 import android.content.Intent
 import android.provider.Settings
 import android.testing.AndroidTestingRunner
@@ -28,6 +29,7 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.PseudoGridView
 import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -43,7 +45,6 @@
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.argThat
-import org.mockito.Mockito.inOrder
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -53,27 +54,21 @@
 class UserSwitchDialogControllerTest : SysuiTestCase() {
 
     @Mock
-    private lateinit var dialog: UserDialog
+    private lateinit var dialog: SystemUIDialog
     @Mock
     private lateinit var falsingManager: FalsingManager
     @Mock
-    private lateinit var settingsView: View
-    @Mock
-    private lateinit var doneView: View
-    @Mock
     private lateinit var activityStarter: ActivityStarter
     @Mock
     private lateinit var userDetailViewAdapter: UserDetailView.Adapter
     @Mock
     private lateinit var launchView: View
     @Mock
-    private lateinit var gridView: PseudoGridView
-    @Mock
     private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
     @Mock
     private lateinit var hostDialog: Dialog
     @Captor
-    private lateinit var clickCaptor: ArgumentCaptor<View.OnClickListener>
+    private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
 
     private lateinit var controller: UserSwitchDialogController
 
@@ -81,11 +76,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        `when`(dialog.settingsButton).thenReturn(settingsView)
-        `when`(dialog.doneButton).thenReturn(doneView)
-        `when`(dialog.grid).thenReturn(gridView)
-
         `when`(launchView.context).thenReturn(mContext)
+        `when`(dialog.context).thenReturn(mContext)
         `when`(dialogLaunchAnimator.showFromView(any(), any(), anyBoolean()))
                 .thenReturn(hostDialog)
 
@@ -105,30 +97,6 @@
     }
 
     @Test
-    fun createCalledBeforeDoneButton() {
-        controller.showDialog(launchView)
-        val inOrder = inOrder(dialog)
-        inOrder.verify(dialog).create()
-        inOrder.verify(dialog).doneButton
-    }
-
-    @Test
-    fun createCalledBeforeSettingsButton() {
-        controller.showDialog(launchView)
-        val inOrder = inOrder(dialog)
-        inOrder.verify(dialog).create()
-        inOrder.verify(dialog).settingsButton
-    }
-
-    @Test
-    fun createCalledBeforeGrid() {
-        controller.showDialog(launchView)
-        val inOrder = inOrder(dialog)
-        inOrder.verify(dialog).create()
-        inOrder.verify(dialog).grid
-    }
-
-    @Test
     fun dialog_showForAllUsers() {
         controller.showDialog(launchView)
         verify(dialog).setShowForAllUsers(true)
@@ -143,51 +111,44 @@
     @Test
     fun adapterAndGridLinked() {
         controller.showDialog(launchView)
-        verify(userDetailViewAdapter).linkToViewGroup(gridView)
+        verify(userDetailViewAdapter).linkToViewGroup(any<PseudoGridView>())
     }
 
     @Test
-    fun clickDoneButton_dismiss() {
+    fun doneButtonSetWithNullHandler() {
         controller.showDialog(launchView)
 
-        verify(doneView).setOnClickListener(capture(clickCaptor))
-
-        clickCaptor.value.onClick(doneView)
-
-        verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
-        verify(dialog).dismiss()
+        verify(dialog).setPositiveButton(anyInt(), eq(null))
     }
 
     @Test
-    fun clickSettingsButton_noFalsing_opensSettingsAndDismisses() {
+    fun clickSettingsButton_noFalsing_opensSettings() {
         `when`(falsingManager.isFalseTap(anyInt())).thenReturn(false)
 
         controller.showDialog(launchView)
 
-        verify(settingsView).setOnClickListener(capture(clickCaptor))
+        verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
 
-        clickCaptor.value.onClick(settingsView)
+        clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
 
         verify(activityStarter)
                 .postStartActivityDismissingKeyguard(
                         argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
                         eq(0)
                 )
-        verify(dialog).dismiss()
     }
 
     @Test
-    fun clickSettingsButton_Falsing_notOpensSettingsAndDismisses() {
+    fun clickSettingsButton_Falsing_notOpensSettings() {
         `when`(falsingManager.isFalseTap(anyInt())).thenReturn(true)
 
         controller.showDialog(launchView)
 
-        verify(settingsView).setOnClickListener(capture(clickCaptor))
+        verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
 
-        clickCaptor.value.onClick(settingsView)
+        clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
 
         verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
-        verify(dialog).dismiss()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 91cafea..4073bb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -104,7 +104,7 @@
 
     @Test
     public void testLogStartRecording() {
-        Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false);
+        Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0);
         mRecordingService.onStartCommand(startIntent, 0, 0);
 
         verify(mUiEventLogger, times(1)).log(Events.ScreenRecordEvent.SCREEN_RECORD_START);
@@ -137,7 +137,7 @@
         // When the screen recording does not start properly
         doThrow(new RuntimeException("fail")).when(mScreenMediaRecorder).start();
 
-        Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false);
+        Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0);
         mRecordingService.onStartCommand(startIntent, 0, 0);
 
         // Then the state is set to not recording
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index bafbccd..db5fd26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -162,8 +162,11 @@
     }
 
     @Test
-    public void testHeaderReadFromOldController() {
-        mHeadsUpAppearanceController.setAppearFraction(1.0f, 1.0f);
+    public void constructor_animationValuesUpdated() {
+        float appearFraction = .75f;
+        float expandedHeight = 400f;
+        when(mStackScrollerController.getAppearFraction()).thenReturn(appearFraction);
+        when(mStackScrollerController.getExpandedHeight()).thenReturn(expandedHeight);
 
         HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
                 mock(NotificationIconAreaController.class),
@@ -179,14 +182,9 @@
                 new View(mContext),
                 new View(mContext),
                 new View(mContext));
-        newController.readFrom(mHeadsUpAppearanceController);
 
-        Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
-                newController.mExpandedHeight, 0.0f);
-        Assert.assertEquals(mHeadsUpAppearanceController.mAppearFraction,
-                newController.mAppearFraction, 0.0f);
-        Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
-                newController.mIsExpanded);
+        Assert.assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f);
+        Assert.assertEquals(appearFraction, newController.mAppearFraction, 0.0f);
     }
 
     @Test
@@ -195,7 +193,9 @@
         reset(mDarkIconDispatcher);
         reset(mPanelView);
         reset(mStackScrollerController);
-        mHeadsUpAppearanceController.destroy();
+
+        mHeadsUpAppearanceController.onViewDetached();
+
         verify(mHeadsUpManager).removeListener(any());
         verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
         verify(mPanelView).removeTrackingHeadsUpListener(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 72a3d66..cec5877 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -34,6 +34,7 @@
 
 import android.app.KeyguardManager;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.os.Handler;
@@ -62,7 +63,6 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -114,6 +114,8 @@
     @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
+    private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    @Mock
     private Handler mHandler;
     @Mock
     private BubblesManager mBubblesManager;
@@ -133,7 +135,7 @@
     @Mock
     private OnUserInteractionCallback mOnUserInteractionCallback;
     @Mock
-    private NotificationActivityStarter mNotificationActivityStarter;
+    private StatusBarNotificationActivityStarter mNotificationActivityStarter;
     @Mock
     private ActivityLaunchAnimator mActivityLaunchAnimator;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -209,7 +211,7 @@
                         mock(NotificationLockscreenUserManager.class),
                         mShadeController,
                         mKeyguardStateController,
-                        mock(NotificationInterruptStateProvider.class),
+                        mNotificationInterruptStateProvider,
                         mock(LockPatternUtils.class),
                         mock(StatusBarRemoteInputCallback.class),
                         mActivityIntentHelper,
@@ -365,4 +367,27 @@
         // Notification should not be cancelled.
         verify(mEntryManager, never()).performRemoveNotification(eq(sbn), any(), anyInt());
     }
+
+    @Test
+    public void testOnFullScreenIntentWhenDozing_wakeUpDevice() {
+        // GIVEN entry that can has a full screen intent that can show
+        Notification.Builder nb = new Notification.Builder(mContext, "a")
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setFullScreenIntent(mock(PendingIntent.class), true);
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0,
+                "tag" + System.currentTimeMillis(), 0, 0,
+                nb.build(), new UserHandle(0), null, 0);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH);
+        when(entry.getSbn()).thenReturn(sbn);
+        when(mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(eq(entry)))
+                .thenReturn(true);
+
+        // WHEN
+        mNotificationActivityStarter.handleFullScreenIntent(entry);
+
+        // THEN display should try wake up for the full screen intent
+        verify(mStatusBar).wakeUpForFullScreenIntent();
+    }
 }
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 88d36b0..d58e13c 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
@@ -86,6 +86,7 @@
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
@@ -132,7 +133,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -244,8 +244,6 @@
     @Mock private CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
     @Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
     @Mock private StatusBarComponent mStatusBarComponent;
-    @Mock private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
-    @Mock private StatusBarFragmentComponent mStatusBarFragmentComponent;
     @Mock private PluginManager mPluginManager;
     @Mock private LegacySplitScreen mLegacySplitScreen;
     @Mock private LightsOutNotifController mLightsOutNotifController;
@@ -350,8 +348,6 @@
         when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
 
         when(mStatusBarComponentFactory.create()).thenReturn(mStatusBarComponent);
-        when(mStatusBarFragmentComponentFactory.create(any()))
-                .thenReturn(mStatusBarFragmentComponent);
         when(mStatusBarComponent.getNotificationShadeWindowViewController()).thenReturn(
                 mNotificationShadeWindowViewController);
 
@@ -366,6 +362,7 @@
         mStatusBar = new StatusBar(
                 mContext,
                 mNotificationsController,
+                mock(FragmentService.class),
                 mLightBarController,
                 mAutoHideController,
                 mStatusBarWindowController,
@@ -422,7 +419,6 @@
                 mCommandQueue,
                 mCollapsedStatusBarFragmentLogger,
                 mStatusBarComponentFactory,
-                mStatusBarFragmentComponentFactory,
                 mPluginManager,
                 Optional.of(mLegacySplitScreen),
                 mLightsOutNotifController,
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 ec35edf..26e0387 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
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -65,7 +66,9 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 
 import java.util.Optional;
 
@@ -90,8 +93,12 @@
     private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     private OperatorNameViewController mOperatorNameViewController;
 
+    @Mock
     private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
+    @Mock
     private StatusBarFragmentComponent mStatusBarFragmentComponent;
+    @Mock
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
 
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
@@ -254,6 +261,32 @@
     }
 
     @Test
+    public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
+        mFragments.dispatchResume();
+        processAllMessages();
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+
+        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+    }
+
+    @Test
+    public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
+        mFragments.dispatchResume();
+        processAllMessages();
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+
+        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+    }
+
+    @Test
     public void setUp_fragmentCreatesDaggerComponent() {
         mFragments.dispatchResume();
         processAllMessages();
@@ -264,11 +297,8 @@
 
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
-        mStatusBarFragmentComponentFactory =
-                mock(StatusBarFragmentComponent.Factory.class);
-        mStatusBarFragmentComponent = mock(StatusBarFragmentComponent.class);
-        when(mStatusBarFragmentComponentFactory.create(any()))
-                .thenReturn(mStatusBarFragmentComponent);
+        MockitoAnnotations.initMocks(this);
+        setUpDaggerComponent();
         mOngoingCallController = mock(OngoingCallController.class);
         mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
         mLocationPublisher = mock(StatusBarLocationPublisher.class);
@@ -295,6 +325,7 @@
                 new StatusBarHideIconsForBouncerManager(
                         mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
                 mKeyguardStateController,
+                mock(NotificationPanelViewController.class),
                 mNetworkController,
                 mStatusBarStateController,
                 mCommandQueue,
@@ -305,6 +336,13 @@
                 mOperatorNameViewControllerFactory);
     }
 
+    private void setUpDaggerComponent() {
+        when(mStatusBarFragmentComponentFactory.create(any()))
+                .thenReturn(mStatusBarFragmentComponent);
+        when(mStatusBarFragmentComponent.getHeadsUpAppearanceController())
+                .thenReturn(mHeadsUpAppearanceController);
+    }
+
     private void setUpNotificationIconAreaController() {
         mMockNotificationAreaController = mock(NotificationIconAreaController.class);
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5cd66aa..dde1ed9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15091,6 +15091,16 @@
     }
 
     @Override
+    public String getSwitchingFromUserMessage() {
+        return mUserController.getSwitchingFromSystemUserMessage();
+    }
+
+    @Override
+    public String getSwitchingToUserMessage() {
+        return mUserController.getSwitchingToSystemUserMessage();
+    }
+
+    @Override
     public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
         mUserController.setStopUserOnSwitch(value);
     }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 429696f..b28b1a6 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1792,7 +1792,8 @@
     private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
         // The dialog will show and then initiate the user switch by calling startUserInForeground
         mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
-                getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage());
+                getSwitchingFromSystemUserMessageUnchecked(),
+                getSwitchingToSystemUserMessageUnchecked());
     }
 
     private void dispatchForegroundProfileChanged(@UserIdInt int userId) {
@@ -2564,18 +2565,40 @@
         }
     }
 
-    private String getSwitchingFromSystemUserMessage() {
+    // Called by AMS, must check permission
+    String getSwitchingFromSystemUserMessage() {
+        checkHasManageUsersPermission("getSwitchingFromSystemUserMessage()");
+
+        return getSwitchingFromSystemUserMessageUnchecked();
+    }
+
+    // Called by AMS, must check permission
+    String getSwitchingToSystemUserMessage() {
+        checkHasManageUsersPermission("getSwitchingToSystemUserMessage()");
+
+        return getSwitchingToSystemUserMessageUnchecked();
+    }
+
+    private String getSwitchingFromSystemUserMessageUnchecked() {
         synchronized (mLock) {
             return mSwitchingFromSystemUserMessage;
         }
     }
 
-    private String getSwitchingToSystemUserMessage() {
+    private String getSwitchingToSystemUserMessageUnchecked() {
         synchronized (mLock) {
             return mSwitchingToSystemUserMessage;
         }
     }
 
+    private void checkHasManageUsersPermission(String operation) {
+        if (mInjector.checkCallingPermission(
+                android.Manifest.permission.MANAGE_USERS) == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException(
+                    "You need MANAGE_USERS permission to call " + operation);
+        }
+    }
+
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         synchronized (mLock) {
             long token = proto.start(fieldId);
@@ -2648,6 +2671,12 @@
             pw.println("  mMaxRunningUsers:" + mMaxRunningUsers);
             pw.println("  mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
             pw.println("  mInitialized:" + mInitialized);
+            if (mSwitchingFromSystemUserMessage != null) {
+                pw.println("  mSwitchingFromSystemUserMessage: " + mSwitchingFromSystemUserMessage);
+            }
+            if (mSwitchingToSystemUserMessage != null) {
+                pw.println("  mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 979b77f..7d68f8d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15355,7 +15355,8 @@
                     mResolveActivity.processName = "system:ui";
                     mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
                     mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
-                    mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+                    mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
+                            | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
                     mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
                     mResolveActivity.exported = true;
                     mResolveActivity.enabled = true;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 95a286c..d97c845 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1387,9 +1387,7 @@
     /** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */
     private boolean shouldStartChangeTransition(
             @Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) {
-        if (mWmService.mDisableTransitionAnimation
-                || mDisplayContent == null || newParent == null || oldParent == null
-                || getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) {
+        if (newParent == null || oldParent == null || !canStartChangeTransition()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 8788225..721907c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -176,6 +176,9 @@
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
+        // TODO(b/205335975): Remove window which stuck in animatingExit status. Find actual cause.
+        mDisplayContent.forAllWindows(WindowState::cleanupAnimatingExitWindow,
+                true /* traverseTopToBottom */);
         // TODO(new-app-transition): Remove code using appTransition.getAppTransition()
         final AppTransition appTransition = mDisplayContent.mAppTransition;
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 36579d9..c3c8318 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -44,6 +44,8 @@
 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static android.view.Display.STATE_UNKNOWN;
+import static android.view.Display.isSuspendedState;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -322,11 +324,6 @@
      */
     private Rect mLastMirroredDisplayAreaBounds = null;
 
-    /**
-     * The last state of the display.
-     */
-    private int mLastDisplayState;
-
     // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
     // on the IME target. We mainly have this container grouping so we can keep track of all the IME
     // window containers together and move them in-sync if/when needed. We use a subclass of
@@ -1658,11 +1655,6 @@
             // to cover the activity configuration change.
             return false;
         }
-        if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) {
-            // Currently it is unknown that when will IME window be ready. Reject the case to
-            // avoid flickering by showing IME in inconsistent orientation.
-            return false;
-        }
         if (checkOpening) {
             if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
                 // Apply normal rotation animation in case of the activity set different requested
@@ -4723,12 +4715,9 @@
         mWmService.requestTraversal();
     }
 
+    @Override
     boolean okToDisplay() {
-        return okToDisplay(false);
-    }
-
-    boolean okToDisplay(boolean ignoreFrozen) {
-        return okToDisplay(ignoreFrozen, false /* ignoreScreenOn */);
+        return okToDisplay(false /* ignoreFrozen */, false /* ignoreScreenOn */);
     }
 
     boolean okToDisplay(boolean ignoreFrozen, boolean ignoreScreenOn) {
@@ -4740,18 +4729,12 @@
         return mDisplayInfo.state == Display.STATE_ON;
     }
 
-    boolean okToAnimate() {
-        return okToAnimate(false);
-    }
-
-    boolean okToAnimate(boolean ignoreFrozen) {
-        return okToAnimate(ignoreFrozen, false /* ignoreScreenOn */);
-    }
-
+    @Override
     boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
         return okToDisplay(ignoreFrozen, ignoreScreenOn)
                 && (mDisplayId != DEFAULT_DISPLAY
-                || mWmService.mPolicy.okToAnimate(ignoreScreenOn));
+                || mWmService.mPolicy.okToAnimate(ignoreScreenOn))
+                && getDisplayPolicy().isScreenOnFully();
     }
 
     static final class TaskForResizePointSearchResult {
@@ -4884,7 +4867,10 @@
             // WindowState#applyImeWindowsIfNeeded} in case of any state mismatch.
             return dc.mImeLayeringTarget != null
                     && (!dc.getDefaultTaskDisplayArea().isSplitScreenModeActivated()
-                             || dc.mImeLayeringTarget.getTask() == null);
+                             || dc.mImeLayeringTarget.getTask() == null)
+                    // Make sure that the IME window won't be skipped to report that it has
+                    // completed the orientation change.
+                    && !dc.mWmService.mDisplayFrozen;
         }
 
         /** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -5623,12 +5609,13 @@
     void onDisplayChanged() {
         mDisplay.getRealSize(mTmpDisplaySize);
         setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+        final int lastDisplayState = mDisplayInfo.state;
         updateDisplayInfo();
 
         // The window policy is responsible for stopping activities on the default display.
         final int displayId = mDisplay.getDisplayId();
+        final int displayState = mDisplayInfo.state;
         if (displayId != DEFAULT_DISPLAY) {
-            final int displayState = mDisplay.getState();
             if (displayState == Display.STATE_OFF) {
                 mOffTokenAcquirer.acquire(mDisplayId);
             } else if (displayState == Display.STATE_ON) {
@@ -5637,12 +5624,18 @@
             ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
                     "Display %d state is now (%d), so update layer mirroring?",
                     mDisplayId, displayState);
-            if (mLastDisplayState != displayState) {
+            if (lastDisplayState != displayState) {
                 // If state is on due to surface being added, then start layer mirroring.
                 // If state is off due to surface being removed, then stop layer mirroring.
                 updateMirroring();
             }
-            mLastDisplayState = displayState;
+        }
+        // Dispatch pending Configuration to WindowContext if the associated display changes to
+        // un-suspended state from suspended.
+        if (isSuspendedState(lastDisplayState)
+                && !isSuspendedState(displayState) && displayState != STATE_UNKNOWN) {
+            mWmService.mWindowContextListenerController
+                    .dispatchPendingConfigurationIfNeeded(mDisplayId);
         }
         mWmService.requestTraversal();
     }
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 1e7b676..6afd335 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -51,7 +51,6 @@
 import static java.lang.Integer.MAX_VALUE;
 
 import android.annotation.Nullable;
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Handler;
 import android.os.IBinder;
@@ -303,9 +302,6 @@
                 && !mDisableWallpaperTouchEvents;
         inputWindowHandle.setHasWallpaper(hasWallpaper);
 
-        final Rect frame = w.getFrame();
-        inputWindowHandle.setFrame(frame.left, frame.top, frame.right, frame.bottom);
-
         // Surface insets are hardcoded to be the same in all directions
         // and we could probably deprecate the "left/right/top/bottom" concept.
         // we avoid reintroducing this concept by just choosing one of them here.
@@ -315,11 +311,19 @@
         // what is on screen to what is actually being touched in the UI.
         inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f);
 
-        final int flags = w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs.flags);
-        inputWindowHandle.setTouchableRegion(mTmpRegion);
+        // Update layout params flags to force the window to be not touch modal. We do this to
+        // restrict the window's touchable region to the task even if it request touches outside its
+        // window bounds. An example is a dialog in primary split should get touches outside its
+        // window within the primary task but should not get any touches going to the secondary
+        // task.
+        int flags = w.mAttrs.flags;
+        if (w.mAttrs.isModal()) {
+            flags = flags | FLAG_NOT_TOUCH_MODAL;
+        }
         inputWindowHandle.setLayoutParamsFlags(flags);
 
-        boolean useSurfaceCrop = false;
+        boolean useSurfaceBoundsAsTouchRegion = false;
+        SurfaceControl touchableRegionCrop = null;
         final Task task = w.getTask();
         if (task != null) {
             // TODO(b/165794636): Remove the special case for freeform window once drag resizing is
@@ -331,20 +335,22 @@
                 // we need to make sure that these changes in crop are reflected in the input
                 // windows, and so ensure this flag is set so that the input crop always reflects
                 // the surface hierarchy.
-                // TODO(b/168252846): we have some issues with modal-windows, so we need to cross
-                // that bridge now that we organize full-screen Tasks.
-                inputWindowHandle.setTouchableRegionCrop(null /* Use this surfaces crop */);
-                inputWindowHandle.setReplaceTouchableRegionWithCrop(true);
-                useSurfaceCrop = true;
+                useSurfaceBoundsAsTouchRegion = true;
+
+                if (w.mAttrs.isModal()) {
+                    TaskFragment parent = w.getTaskFragment();
+                    touchableRegionCrop = parent != null ? parent.getSurfaceControl() : null;
+                }
             } else if (task.cropWindowsToRootTaskBounds() && !w.inFreeformWindowingMode()) {
-                inputWindowHandle.setTouchableRegionCrop(task.getRootTask().getSurfaceControl());
-                inputWindowHandle.setReplaceTouchableRegionWithCrop(false);
-                useSurfaceCrop = true;
+                touchableRegionCrop = task.getRootTask().getSurfaceControl();
             }
         }
-        if (!useSurfaceCrop) {
-            inputWindowHandle.setReplaceTouchableRegionWithCrop(false);
-            inputWindowHandle.setTouchableRegionCrop(null);
+        inputWindowHandle.setReplaceTouchableRegionWithCrop(useSurfaceBoundsAsTouchRegion);
+        inputWindowHandle.setTouchableRegionCrop(touchableRegionCrop);
+
+        if (!useSurfaceBoundsAsTouchRegion) {
+            w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs);
+            inputWindowHandle.setTouchableRegion(mTmpRegion);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1b7a012..d2eea76 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2163,10 +2163,7 @@
     }
 
     private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
-        if (mWmService.mDisableTransitionAnimation
-                || !isVisible()
-                || getSurfaceControl() == null
-                || !isLeafTask()) {
+        if (!isLeafTask() || !canStartChangeTransition()) {
             return false;
         }
         // Only do an animation into and out-of freeform mode for now. Other mode
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 1e367dc..ec3554a 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -867,6 +867,10 @@
         // Place root home tasks to the bottom.
         layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer);
         layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer);
+        // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
+        //                    make app pair split only have single root then we can just attach the
+        //                    divider to the single root task in shell.
+        layer = Math.max(layer, SPLIT_DIVIDER_LAYER + 1);
         adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer);
         t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
     }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 51cede0..f32ab1e 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2125,13 +2125,7 @@
 
     /** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
     private boolean shouldStartChangeTransition(Rect startBounds) {
-        if (mWmService.mDisableTransitionAnimation
-                || mDisplayContent == null
-                || mTaskFragmentOrganizer == null
-                || getSurfaceControl() == null
-                // The change transition will be covered by display.
-                || mDisplayContent.inTransition()
-                || !isVisible()) {
+        if (mTaskFragmentOrganizer == null || !canStartChangeTransition()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 6d83fb6..29c27f9 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -31,10 +31,8 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.RemoteAnimationDefinition;
-import android.view.SurfaceControl;
 import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskFragmentOrganizerController;
-import android.window.TaskFragmentAppearedInfo;
 import android.window.TaskFragmentInfo;
 
 import com.android.internal.protolog.common.ProtoLog;
@@ -135,11 +133,8 @@
         void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
             final TaskFragmentInfo info = tf.getTaskFragmentInfo();
-            final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(),
-                    "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared");
             try {
-                organizer.onTaskFragmentAppeared(
-                        new TaskFragmentAppearedInfo(info, outSurfaceControl));
+                organizer.onTaskFragmentAppeared(info);
                 mLastSentTaskFragmentInfos.put(tf, info);
                 tf.mTaskFragmentAppearedSent = true;
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 58bc244..5af9147 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2600,6 +2600,13 @@
         mSurfaceFreezer.unfreeze(getPendingTransaction());
     }
 
+    /** Whether we can start change transition with this window and current display status. */
+    boolean canStartChangeTransition() {
+        return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
+                && getSurfaceControl() != null && !mDisplayContent.inTransition()
+                && isVisible() && isVisibleRequested() && okToAnimate();
+    }
+
     /**
      * Initializes a change transition. See {@link SurfaceFreezer} for more information.
      *
@@ -2948,12 +2955,7 @@
     }
 
     boolean okToAnimate() {
-        return okToAnimate(false /* ignoreFrozen */);
-    }
-
-    boolean okToAnimate(boolean ignoreFrozen) {
-        final DisplayContent dc = getDisplayContent();
-        return dc != null && dc.okToAnimate(ignoreFrozen);
+        return okToAnimate(false /* ignoreFrozen */, false /* ignoreScreenOn */);
     }
 
     boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 86e356a..cc52713 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -17,7 +17,9 @@
 package com.android.server.wm;
 
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.isSuspendedState;
 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.window.WindowProviderService.isWindowProviderService;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
@@ -80,7 +82,7 @@
      * @param options a bundle used to pass window-related options.
      */
     void registerWindowContainerListener(@NonNull IBinder clientToken,
-            @NonNull WindowContainer container, int ownerUid, @WindowType int type,
+            @NonNull WindowContainer<?> container, int ownerUid, @WindowType int type,
             @Nullable Bundle options) {
         WindowContextListenerImpl listener = mListeners.get(clientToken);
         if (listener == null) {
@@ -103,6 +105,16 @@
         listener.unregister();
     }
 
+    void dispatchPendingConfigurationIfNeeded(int displayId) {
+        for (int i = mListeners.size() - 1; i >= 0; --i) {
+            final WindowContextListenerImpl listener = mListeners.valueAt(i);
+            if (listener.getWindowContainer().getDisplayContent().getDisplayId() == displayId
+                    && listener.mHasPendingConfiguration) {
+                listener.reportConfigToWindowTokenClient();
+            }
+        }
+    }
+
     /**
      * Verifies if the caller is allowed to do the operation to the listener specified by
      * {@code clientToken}.
@@ -138,7 +150,7 @@
         return listener != null ? listener.mOptions : null;
     }
 
-    @Nullable WindowContainer getContainer(IBinder clientToken) {
+    @Nullable WindowContainer<?> getContainer(IBinder clientToken) {
         final WindowContextListenerImpl listener = mListeners.get(clientToken);
         return listener != null ? listener.mContainer : null;
     }
@@ -163,7 +175,7 @@
     class WindowContextListenerImpl implements WindowContainerListener {
         @NonNull private final IBinder mClientToken;
         private final int mOwnerUid;
-        @NonNull private WindowContainer mContainer;
+        @NonNull private WindowContainer<?> mContainer;
         /**
          * The options from {@link Context#createWindowContext(int, Bundle)}.
          * <p>It can be used for choosing the {@link DisplayArea} where the window context
@@ -177,7 +189,9 @@
         private int mLastReportedDisplay = INVALID_DISPLAY;
         private Configuration mLastReportedConfig;
 
-        private WindowContextListenerImpl(IBinder clientToken, WindowContainer container,
+        private boolean mHasPendingConfiguration;
+
+        private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container,
                 int ownerUid, @WindowType int type, @Nullable Bundle options) {
             mClientToken = clientToken;
             mContainer = Objects.requireNonNull(container);
@@ -197,11 +211,11 @@
 
         /** TEST ONLY: returns the {@link WindowContainer} of the listener */
         @VisibleForTesting
-        WindowContainer getWindowContainer() {
+        WindowContainer<?> getWindowContainer() {
             return mContainer;
         }
 
-        private void updateContainer(@NonNull WindowContainer newContainer) {
+        private void updateContainer(@NonNull WindowContainer<?> newContainer) {
             Objects.requireNonNull(newContainer);
 
             if (mContainer.equals(newContainer)) {
@@ -246,12 +260,20 @@
             if (mDeathRecipient == null) {
                 throw new IllegalStateException("Invalid client token: " + mClientToken);
             }
-
-            if (mLastReportedConfig == null) {
-                mLastReportedConfig = new Configuration();
+            // If the display of window context associated window container is suspended, don't
+            // report the configuration update. Note that we still dispatch the configuration update
+            // to WindowProviderService to make it compatible with Service#onConfigurationChanged.
+            // Service always receives #onConfigurationChanged callback regardless of display state.
+            if (!isWindowProviderService(mOptions)
+                    && isSuspendedState(mContainer.getDisplayContent().getDisplayInfo().state)) {
+                mHasPendingConfiguration = true;
+                return;
             }
             final Configuration config = mContainer.getConfiguration();
             final int displayId = mContainer.getDisplayContent().getDisplayId();
+            if (mLastReportedConfig == null) {
+                mLastReportedConfig = new Configuration();
+            }
             if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
                 // No changes since last reported time.
                 return;
@@ -266,6 +288,7 @@
             } catch (RemoteException e) {
                 ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
             }
+            mHasPendingConfiguration = false;
         }
 
         @Override
@@ -283,7 +306,7 @@
                 // If we cannot obtain the DisplayContent, the DisplayContent may also be removed.
                 // We should proceed the removal process.
                 if (dc != null) {
-                    final DisplayArea da = dc.findAreaForToken(windowToken);
+                    final DisplayArea<?> da = dc.findAreaForToken(windowToken);
                     updateContainer(da);
                     return;
                 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e0c3cf9..44d2a7f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -51,7 +51,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -2882,10 +2881,9 @@
         }
     }
 
-    int getSurfaceTouchableRegion(Region region, int flags) {
-        final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+    void getSurfaceTouchableRegion(Region region, WindowManager.LayoutParams attrs) {
+        final boolean modal = attrs.isModal();
         if (modal) {
-            flags |= FLAG_NOT_TOUCH_MODAL;
             if (mActivityRecord != null) {
                 // Limit the outer touch to the activity root task region.
                 updateRegionForModalActivityWindow(region);
@@ -2917,8 +2915,6 @@
         if (mInvGlobalScale != 1.f) {
             region.scale(mInvGlobalScale);
         }
-
-        return flags;
     }
 
     /**
@@ -3766,10 +3762,9 @@
      * {@link WindowManager.LayoutParams#FLAG_NOT_TOUCH_MODAL touch modality.}
      */
     void getEffectiveTouchableRegion(Region outRegion) {
-        final boolean modal = (mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
         final DisplayContent dc = getDisplayContent();
 
-        if (modal && dc != null) {
+        if (mAttrs.isModal() && dc != null) {
             outRegion.set(dc.getBounds());
             cropRegionToRootTaskBoundsIfNeeded(outRegion);
             subtractTouchExcludeRegionIfNeeded(outRegion);
@@ -5069,6 +5064,48 @@
         return false;
     }
 
+    private boolean shouldFinishAnimatingExit() {
+        // Exit animation might be applied soon.
+        if (inTransition()) {
+            ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isTransition: %s",
+                    this);
+            return false;
+        }
+        if (!mDisplayContent.okToAnimate()) {
+            return true;
+        }
+        // Exit animation is running.
+        if (isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES)) {
+            ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isAnimating: %s",
+                    this);
+            return false;
+        }
+        // If the wallpaper is currently behind this app window, we need to change both of
+        // them inside of a transaction to avoid artifacts.
+        if (mDisplayContent.mWallpaperController.isWallpaperTarget(this)) {
+            ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
+                    "shouldWaitAnimatingExit: isWallpaperTarget: %s", this);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * If this is window is stuck in the animatingExit status, resume clean up procedure blocked
+     * by the exit animation.
+     */
+    void cleanupAnimatingExitWindow() {
+        // TODO(b/205335975): WindowManagerService#tryStartExitingAnimation starts an exit animation
+        // and set #mAnimationExit. After the exit animation finishes, #onExitAnimationDone shall
+        // be called, but there seems to be a case that #onExitAnimationDone is not triggered, so
+        // a windows stuck in the animatingExit status.
+        if (mAnimatingExit && shouldFinishAnimatingExit()) {
+            ProtoLog.w(WM_DEBUG_APP_TRANSITIONS, "Clear window stuck on animatingExit status: %s",
+                    this);
+            onExitAnimationDone();
+        }
+    }
+
     void onExitAnimationDone() {
         if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
                 + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index fc2c162..707e463 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1779,11 +1779,6 @@
                 anyInt() /* orientation */, anyInt() /* lastRotation */);
         // Set to visible so the activity can freeze the screen.
         activity.setVisibility(true);
-        // Update the display policy to make the screen fully turned on so the freeze is allowed
-        display.getDisplayPolicy().screenTurnedOn(null);
-        display.getDisplayPolicy().finishKeyguardDrawn();
-        display.getDisplayPolicy().finishWindowsDrawn();
-        display.getDisplayPolicy().finishScreenTurningOn();
 
         display.rotateInDifferentOrientationIfNeeded(activity);
         display.setFixedRotationLaunchingAppUnchecked(activity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 6fa306b..5062706 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -334,6 +334,18 @@
     }
 
     @Test
+    public void testExitAnimationDone_beforeAppTransition() {
+        final Task task = createTask(mDisplayContent);
+        final WindowState win = createAppWindow(task, ACTIVITY_TYPE_STANDARD, "Win");
+        spyOn(win);
+        win.mAnimatingExit = true;
+        mDisplayContent.mAppTransition.setTimeout();
+        mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+        verify(win).onExitAnimationDone();
+    }
+
+    @Test
     public void testGetAnimationTargets_windowsAreBeingReplaced() {
         // [DisplayContent] -+- [Task1] - [ActivityRecord1] (opening, visible)
         //                                       +- [AppWindow1] (being-replaced)
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index d7daa57..407f9cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_ON;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -70,7 +71,7 @@
 
     @Before
     public void setUp() throws Exception {
-        // Let the Display to be created with the DualDisplay policy.
+        // Let the Display be created with the DualDisplay policy.
         final DisplayAreaPolicy.Provider policyProvider =
                 new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
         Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
@@ -78,6 +79,7 @@
         mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
         mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
                 .Builder(mAtm, 1000, 1000).build();
+        mSecondaryDisplay.getDisplayInfo().state = STATE_ON;
 
         // Mock addWindowTokenWithOptions to create a test window token.
         mIWindowManager = WindowManagerGlobal.getWindowManagerService();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 6737b1a..730275c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -16,11 +16,14 @@
 
 package com.android.server.wm;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.clearInvocations;
 
 import android.graphics.Rect;
@@ -91,6 +94,7 @@
         final Rect endBounds = new Rect(500, 500, 1000, 1000);
         mTaskFragment.setBounds(startBounds);
         doReturn(true).when(mTaskFragment).isVisible();
+        doReturn(true).when(mTaskFragment).isVisibleRequested();
 
         clearInvocations(mTransaction);
         mTaskFragment.setBounds(endBounds);
@@ -108,6 +112,25 @@
         verify(mTransaction).setWindowCrop(mLeash, 500, 500);
     }
 
+    @Test
+    public void testNotOkToAnimate_doNotStartChangeTransition() {
+        mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer);
+        final Rect startBounds = new Rect(0, 0, 1000, 1000);
+        final Rect endBounds = new Rect(500, 500, 1000, 1000);
+        mTaskFragment.setBounds(startBounds);
+        doReturn(true).when(mTaskFragment).isVisible();
+        doReturn(true).when(mTaskFragment).isVisibleRequested();
+
+        final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+        displayPolicy.screenTurnedOff();
+
+        assertFalse(mTaskFragment.okToAnimate());
+
+        mTaskFragment.setBounds(endBounds);
+
+        verify(mTaskFragment, never()).initializeChangeTransition(any());
+    }
+
     /**
      * Tests that when a {@link TaskFragmentInfo} is generated from a {@link TaskFragment}, an
      * activity that has not yet been attached to a process because it is being initialized but
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 0e504d3..e0f69d4f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -165,6 +165,11 @@
                 doReturn(false).when(displayPolicy).hasStatusBar();
                 doReturn(false).when(newDisplay).supportsSystemDecorations();
             }
+            // Update the display policy to make the screen fully turned on so animation is allowed
+            displayPolicy.screenTurnedOn(null /* screenOnListener */);
+            displayPolicy.finishKeyguardDrawn();
+            displayPolicy.finishWindowsDrawn();
+            displayPolicy.finishScreenTurningOn();
             if (mStatusBarHeight > 0) {
                 doReturn(true).when(displayPolicy).hasStatusBar();
                 doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index e5eba57..646647f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -17,22 +17,35 @@
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_OFF;
+import static android.view.Display.STATE_ON;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.window.WindowProvider.KEY_IS_WINDOW_PROVIDER_SERVICE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static com.google.common.truth.Truth.assertThat;
 
 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.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import android.app.IWindowToken;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
 
@@ -55,12 +68,15 @@
     private static final int ANOTHER_UID = 1000;
 
     private final IBinder mClientToken = new Binder();
-    private WindowContainer mContainer;
+    private WindowContainer<?> mContainer;
 
     @Before
     public void setUp() {
         mController = new WindowContextListenerController();
         mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+        // Make display on to verify configuration propagation.
+        mDefaultDisplay.getDisplayInfo().state = STATE_ON;
+        mDisplayContent.getDisplayInfo().state = STATE_ON;
     }
 
     @Test
@@ -76,7 +92,7 @@
 
         assertEquals(2, mController.mListeners.size());
 
-        final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+        final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
                 mDefaultDisplay);
         mController.registerWindowContainerListener(mClientToken, container, -1,
                 TYPE_APPLICATION_OVERLAY, null /* options */);
@@ -89,6 +105,7 @@
         assertEquals(container, listener.getWindowContainer());
     }
 
+    @UseTestDisplay
     @Test
     public void testRegisterWindowContextListenerClientConfigPropagation() {
         final TestWindowTokenClient clientToken = new TestWindowTokenClient();
@@ -107,7 +124,7 @@
         assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId);
 
         // Update the WindowContainer.
-        final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+        final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
                 mDefaultDisplay);
         final Configuration config2 = container.getConfiguration();
         final Rect bounds2 = new Rect(0, 0, 20, 20);
@@ -174,7 +191,7 @@
                 .setDisplayContent(mDefaultDisplay)
                 .setFromClientToken(true)
                 .build();
-        final DisplayArea da = windowContextCreatedToken.getDisplayArea();
+        final DisplayArea<?> da = windowContextCreatedToken.getDisplayArea();
 
         mController.registerWindowContainerListener(mClientToken, windowContextCreatedToken,
                 TEST_UID, TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
@@ -192,11 +209,12 @@
         // Let the Display to be created with the DualDisplay policy.
         final DisplayAreaPolicy.Provider policyProvider =
                 new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
-        Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
+        doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
         // Create a DisplayContent with dual RootDisplayArea
         DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent =
                 new DualDisplayAreaGroupPolicyTest.DualDisplayContent
                  .Builder(mAtm, 1000, 1000).build();
+        dualDisplayContent.getDisplayInfo().state = STATE_ON;
         final DisplayArea.Tokens imeContainer = dualDisplayContent.getImeContainer();
         // Put the ImeContainer to the first sub-RootDisplayArea
         dualDisplayContent.mFirstRoot.placeImeContainer(imeContainer);
@@ -222,7 +240,62 @@
         assertThat(mController.getContainer(mClientToken)).isEqualTo(imeContainer);
     }
 
-    private class TestWindowTokenClient extends IWindowToken.Stub {
+    @Test
+    public void testConfigUpdateForSuspendedWindowContext() {
+        final TestWindowTokenClient mockToken = new TestWindowTokenClient();
+        spyOn(mockToken);
+
+        mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;
+
+        final Configuration config1 = mContainer.getConfiguration();
+        final Rect bounds1 = new Rect(0, 0, 10, 10);
+        config1.windowConfiguration.setBounds(bounds1);
+        config1.densityDpi = 100;
+        mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+        mController.registerWindowContainerListener(mockToken, mContainer, -1,
+                TYPE_APPLICATION_OVERLAY, null /* options */);
+
+        verify(mockToken, never()).onConfigurationChanged(any(), anyInt());
+
+        // Turn on the display and verify if the client receive the callback
+        Display display = mContainer.getDisplayContent().getDisplay();
+        spyOn(display);
+        Mockito.doAnswer(invocation -> {
+            final DisplayInfo info = mContainer.getDisplayContent().getDisplayInfo();
+            info.state = STATE_ON;
+            ((DisplayInfo) invocation.getArgument(0)).copyFrom(info);
+            return null;
+        }).when(display).getDisplayInfo(any(DisplayInfo.class));
+
+        mContainer.getDisplayContent().onDisplayChanged();
+
+        assertThat(mockToken.mConfiguration).isEqualTo(config1);
+        assertThat(mockToken.mDisplayId).isEqualTo(mContainer.getDisplayContent().getDisplayId());
+    }
+
+    @Test
+    public void testReportConfigUpdateForSuspendedWindowProviderService() {
+        final TestWindowTokenClient clientToken = new TestWindowTokenClient();
+        final Bundle options = new Bundle();
+        options.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true);
+
+        mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;
+
+        final Configuration config1 = mContainer.getConfiguration();
+        final Rect bounds1 = new Rect(0, 0, 10, 10);
+        config1.windowConfiguration.setBounds(bounds1);
+        config1.densityDpi = 100;
+        mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+        mController.registerWindowContainerListener(clientToken, mContainer, -1,
+                TYPE_APPLICATION_OVERLAY, options);
+
+        assertThat(clientToken.mConfiguration).isEqualTo(config1);
+        assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId);
+    }
+
+    private static class TestWindowTokenClient extends IWindowToken.Stub {
         private Configuration mConfiguration;
         private int mDisplayId;
         private boolean mRemoved;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 88d8ba3..b997acf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -200,6 +200,13 @@
         SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
 
         mDefaultDisplay = mWm.mRoot.getDefaultDisplay();
+        // Update the display policy to make the screen fully turned on so animation is allowed
+        final DisplayPolicy displayPolicy = mDefaultDisplay.getDisplayPolicy();
+        displayPolicy.screenTurnedOn(null /* screenOnListener */);
+        displayPolicy.finishKeyguardDrawn();
+        displayPolicy.finishWindowsDrawn();
+        displayPolicy.finishScreenTurningOn();
+
         mTransaction = mSystemServicesTestRule.mTransaction;
         mMockSession = mock(Session.class);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index b3ba217..2d3e5c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -501,8 +501,6 @@
         mDisplayContent.assignChildLayers(mTransaction);
 
         assertWindowHigher(splitWindow1, belowTaskWindow);
-        assertWindowHigher(splitWindow1, belowTaskWindow);
-        assertWindowHigher(splitWindow2, belowTaskWindow);
         assertWindowHigher(splitWindow2, belowTaskWindow);
         assertWindowHigher(mDockedDividerWindow, splitWindow1);
         assertWindowHigher(mDockedDividerWindow, splitWindow2);
@@ -510,6 +508,39 @@
         assertWindowHigher(pinnedWindow, aboveTaskWindow);
     }
 
+
+    @Test
+    public void testDockedDividerPosition_noAboveTask() {
+        final Task pinnedTask =
+                createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+        final WindowState pinnedWindow =
+                createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow");
+
+        final Task belowTask =
+                createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final WindowState belowTaskWindow =
+                createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow");
+
+        final Task splitScreenTask1 =
+                createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+        final WindowState splitWindow1 =
+                createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1");
+        final Task splitScreenTask2 =
+                createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+        final WindowState splitWindow2 =
+                createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
+        splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2);
+        splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1);
+
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowHigher(splitWindow1, belowTaskWindow);
+        assertWindowHigher(splitWindow2, belowTaskWindow);
+        assertWindowHigher(mDockedDividerWindow, splitWindow1);
+        assertWindowHigher(mDockedDividerWindow, splitWindow2);
+        assertWindowHigher(pinnedWindow, mDockedDividerWindow);
+    }
+
     @Test
     public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() {
         // create RecentsAnimationController