Merge "Prevent double-applying transactions" into tm-qpr-dev
diff --git a/core/java/android/app/search/SearchAction.java b/core/java/android/app/search/SearchAction.java
index 9e40e7e..0c4508a 100644
--- a/core/java/android/app/search/SearchAction.java
+++ b/core/java/android/app/search/SearchAction.java
@@ -67,7 +67,7 @@
     private final UserHandle mUserHandle;
 
     @Nullable
-    private Bundle mExtras;
+    private final Bundle mExtras;
 
     SearchAction(Parcel in) {
         mId = in.readString();
@@ -99,7 +99,7 @@
         mPendingIntent = pendingIntent;
         mIntent = intent;
         mUserHandle = userHandle;
-        mExtras = extras;
+        mExtras = extras != null ? extras : new Bundle();
 
         if (mPendingIntent == null && mIntent == null) {
             throw new IllegalStateException("At least one type of intent should be available.");
diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java
index a590a5d..a3874f7 100644
--- a/core/java/android/app/search/SearchTarget.java
+++ b/core/java/android/app/search/SearchTarget.java
@@ -185,7 +185,7 @@
         mShortcutInfo = shortcutInfo;
         mAppWidgetProviderInfo = appWidgetProviderInfo;
         mSliceUri = sliceUri;
-        mExtras = extras;
+        mExtras = extras != null ? extras : new Bundle();
 
         int published = 0;
         if (mSearchAction != null) published++;
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index 57a0330..5ed9d2f 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -118,11 +118,11 @@
             }
             if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
                 if (displayFrame.width() < displayFrame.height()) {
-                    displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
-                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                    displayCutoutSafeExceptMaybeBars.top = MIN_Y;
+                    displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
                 } else {
-                    displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
-                    displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+                    displayCutoutSafeExceptMaybeBars.left = MIN_X;
+                    displayCutoutSafeExceptMaybeBars.right = MAX_X;
                 }
             }
             final boolean layoutInsetDecor = (attrs.flags & FLAG_LAYOUT_INSET_DECOR) != 0;
@@ -132,23 +132,23 @@
                 final Insets systemBarsInsets = state.calculateInsets(
                         displayFrame, WindowInsets.Type.systemBars(), requestedVisibilities);
                 if (systemBarsInsets.left > 0) {
-                    displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+                    displayCutoutSafeExceptMaybeBars.left = MIN_X;
                 }
                 if (systemBarsInsets.top > 0) {
-                    displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+                    displayCutoutSafeExceptMaybeBars.top = MIN_Y;
                 }
                 if (systemBarsInsets.right > 0) {
-                    displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+                    displayCutoutSafeExceptMaybeBars.right = MAX_X;
                 }
                 if (systemBarsInsets.bottom > 0) {
-                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                    displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
                 }
             }
             if (type == TYPE_INPUT_METHOD) {
                 final InsetsSource navSource = state.peekSource(ITYPE_NAVIGATION_BAR);
                 if (navSource != null && navSource.calculateInsets(displayFrame, true).bottom > 0) {
                     // The IME can always extend under the bottom cutout if the navbar is there.
-                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                    displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
                 }
             }
             final boolean attachedInParent = attachedWindowFrame != null && !layoutInScreen;
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 22340c6..2d669ab 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -53,6 +53,7 @@
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
@@ -210,6 +211,7 @@
     public static final int CUJ_USER_DIALOG_OPEN = 59;
     public static final int CUJ_TASKBAR_EXPAND = 60;
     public static final int CUJ_TASKBAR_COLLAPSE = 61;
+    public static final int CUJ_SHADE_CLEAR_ALL = 62;
 
     private static final int NO_STATSD_LOGGING = -1;
 
@@ -280,6 +282,7 @@
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL,
     };
 
     private static volatile InteractionJankMonitor sInstance;
@@ -361,7 +364,8 @@
             CUJ_SHADE_DIALOG_OPEN,
             CUJ_USER_DIALOG_OPEN,
             CUJ_TASKBAR_EXPAND,
-            CUJ_TASKBAR_COLLAPSE
+            CUJ_TASKBAR_COLLAPSE,
+            CUJ_SHADE_CLEAR_ALL
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
@@ -804,6 +808,8 @@
                 return "TASKBAR_EXPAND";
             case CUJ_TASKBAR_COLLAPSE:
                 return "TASKBAR_COLLAPSE";
+            case CUJ_SHADE_CLEAR_ALL:
+                return "SHADE_CLEAR_ALL";
         }
         return "UNKNOWN";
     }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index 4b0c62b..cc7d23e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -106,7 +106,7 @@
             ephemeral: Boolean
         ): Boolean {
             if (
-                !isVisible(
+                !occupiesSpace(
                     rootView.visibility,
                     rootView.left,
                     rootView.top,
@@ -177,7 +177,7 @@
             fadeInInterpolator: Interpolator = DEFAULT_FADE_IN_INTERPOLATOR
         ): Boolean {
             if (
-                isVisible(
+                occupiesSpace(
                     rootView.visibility,
                     rootView.left,
                     rootView.top,
@@ -295,7 +295,7 @@
 
                     (view.getTag(R.id.tag_animator) as? ObjectAnimator)?.cancel()
 
-                    if (!isVisible(view.visibility, left, top, right, bottom)) {
+                    if (!occupiesSpace(view.visibility, left, top, right, bottom)) {
                         setBound(view, Bound.LEFT, left)
                         setBound(view, Bound.TOP, top)
                         setBound(view, Bound.RIGHT, right)
@@ -362,7 +362,7 @@
             duration: Long = DEFAULT_DURATION
         ): Boolean {
             if (
-                !isVisible(
+                !occupiesSpace(
                     rootView.visibility,
                     rootView.left,
                     rootView.top,
@@ -530,17 +530,17 @@
         }
 
         /**
-         * Returns whether the given [visibility] and bounds are consistent with a view being
-         * currently visible on screen.
+         * Returns whether the given [visibility] and bounds are consistent with a view being a
+         * contributing part of the hierarchy.
          */
-        private fun isVisible(
+        private fun occupiesSpace(
             visibility: Int,
             left: Int,
             top: Int,
             right: Int,
             bottom: Int
         ): Boolean {
-            return visibility == View.VISIBLE && left != right && top != bottom
+            return visibility != View.GONE && left != right && top != bottom
         }
 
         /**
diff --git a/packages/SystemUI/docs/user-file-manager.md b/packages/SystemUI/docs/user-file-manager.md
new file mode 100644
index 0000000..64f1694
--- /dev/null
+++ b/packages/SystemUI/docs/user-file-manager.md
@@ -0,0 +1,13 @@
+# UserFileManager
+
+This class is used to generate file paths and SharedPreferences that is compatible for multiple
+users in SystemUI. Due to constraints in SystemUI, we can only read/write files as the system user.
+Therefore, for secondary users, we want to store secondary user specific files into the system user
+directory.
+
+## Handling User Removal
+
+This class will listen for Intent.ACTION_USER_REMOVED and remove directories that no longer
+corresponding to active users. Additionally, upon start up, the class will run the same query for
+deletion to ensure that there is no stale data.
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ef672f3..176e581 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -802,6 +802,8 @@
     <!-- Message shown when lock screen is unlocked (ie: by trust agent) and the user taps the empty space on the lock screen and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
     <string name="keyguard_unlock_press">Press the unlock icon to open</string>
 
+    <!-- Message shown when non-bypass face authentication succeeds. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
+    <string name="keyguard_face_successful_unlock_swipe">Unlocked by face. Swipe up to open.</string>
     <!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
     <string name="keyguard_face_successful_unlock_press">Unlocked by face. Press the unlock icon to open.</string>
     <!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 12fa401..d32219a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -336,6 +336,11 @@
         mKeyguardSecurityContainerController.onStartingToHide();
     }
 
+    /** Called when bouncer visibility changes. */
+    public void onBouncerVisibilityChanged(@View.Visibility int visibility) {
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(visibility);
+    }
+
     public boolean hasDismissActions() {
         return mDismissAction != null || mCancelAction != null;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 61e2624..5ee659b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
+import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
 import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
@@ -32,11 +33,13 @@
 import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricSourceType;
 import android.metrics.LogMaker;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
 import android.view.MotionEvent;
+import android.view.View;
 
 import androidx.annotation.Nullable;
 
@@ -55,6 +58,7 @@
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
+import com.android.systemui.biometrics.SidefpsController;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -67,6 +71,8 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.settings.GlobalSettings;
 
+import java.util.Optional;
+
 import javax.inject.Inject;
 
 /** Controller for {@link KeyguardSecurityContainer} */
@@ -93,6 +99,7 @@
     private final GlobalSettings mGlobalSettings;
     private final FeatureFlags mFeatureFlags;
     private final SessionTracker mSessionTracker;
+    private final Optional<SidefpsController> mSidefpsController;
 
     private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
 
@@ -236,13 +243,27 @@
                     reloadColors();
                 }
             };
+    private boolean mBouncerVisible = false;
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
-        @Override
-        public void onDevicePolicyManagerStateChanged() {
-            showPrimarySecurityScreen(false);
-        }
-    };
+                @Override
+                public void onDevicePolicyManagerStateChanged() {
+                    showPrimarySecurityScreen(false);
+                }
+
+                @Override
+                public void onBiometricRunningStateChanged(boolean running,
+                        BiometricSourceType biometricSourceType) {
+                    if (biometricSourceType == FINGERPRINT) {
+                        updateSideFpsVisibility();
+                    }
+                }
+
+                @Override
+                public void onStrongAuthStateChanged(int userId) {
+                    updateSideFpsVisibility();
+                }
+            };
 
     private KeyguardSecurityContainerController(KeyguardSecurityContainer view,
             AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
@@ -260,7 +281,8 @@
             UserSwitcherController userSwitcherController,
             FeatureFlags featureFlags,
             GlobalSettings globalSettings,
-            SessionTracker sessionTracker) {
+            SessionTracker sessionTracker,
+            Optional<SidefpsController> sidefpsController) {
         super(view);
         mLockPatternUtils = lockPatternUtils;
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -280,6 +302,7 @@
         mFeatureFlags = featureFlags;
         mGlobalSettings = globalSettings;
         mSessionTracker = sessionTracker;
+        mSidefpsController = sidefpsController;
     }
 
     @Override
@@ -311,8 +334,23 @@
             getCurrentSecurityController().onPause();
         }
         mView.onPause();
+        // It might happen that onStartingToHide is not called when the device is locked while on
+        // bouncer.
+        setBouncerVisible(false);
     }
 
+    private void updateSideFpsVisibility() {
+        if (!mSidefpsController.isPresent()) {
+            return;
+        }
+        if (mBouncerVisible && mView.isSidedSecurityMode()
+                && mUpdateMonitor.isFingerprintDetectionRunning()
+                && !mUpdateMonitor.userNeedsStrongAuth()) {
+            mSidefpsController.get().show();
+        } else {
+            mSidefpsController.get().hide();
+        }
+    }
 
     /**
      * Shows the primary security screen for the user. This will be either the multi-selector
@@ -397,6 +435,17 @@
         if (mCurrentSecurityMode != SecurityMode.None) {
             getCurrentSecurityController().onStartingToHide();
         }
+        setBouncerVisible(false);
+    }
+
+    /** Called when the bouncer changes visibility. */
+    public void onBouncerVisibilityChanged(@View.Visibility int visibility) {
+        setBouncerVisible(visibility == View.VISIBLE);
+    }
+
+    private void setBouncerVisible(boolean visible) {
+        mBouncerVisible = visible;
+        updateSideFpsVisibility();
     }
 
     /**
@@ -655,6 +704,7 @@
         private final FeatureFlags mFeatureFlags;
         private final UserSwitcherController mUserSwitcherController;
         private final SessionTracker mSessionTracker;
+        private final Optional<SidefpsController> mSidefpsController;
 
         @Inject
         Factory(KeyguardSecurityContainer view,
@@ -673,7 +723,8 @@
                 UserSwitcherController userSwitcherController,
                 FeatureFlags featureFlags,
                 GlobalSettings globalSettings,
-                SessionTracker sessionTracker) {
+                SessionTracker sessionTracker,
+                Optional<SidefpsController> sidefpsController) {
             mView = view;
             mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
             mLockPatternUtils = lockPatternUtils;
@@ -690,6 +741,7 @@
             mGlobalSettings = globalSettings;
             mUserSwitcherController = userSwitcherController;
             mSessionTracker = sessionTracker;
+            mSidefpsController = sidefpsController;
         }
 
         public KeyguardSecurityContainerController create(
@@ -699,7 +751,8 @@
                     mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                     mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
                     mConfigurationController, mFalsingCollector, mFalsingManager,
-                    mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker);
+                    mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker,
+                    mSidefpsController);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ede6243..cdb3abf8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2052,17 +2052,6 @@
                     .getServiceStateForSubscriber(subId);
             mHandler.sendMessage(
                     mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
-
-            // Get initial state. Relying on Sticky behavior until API for getting info.
-            if (mBatteryStatus == null) {
-                Intent intent = mContext.registerReceiver(
-                        null,
-                        new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
-                );
-                if (intent != null && mBatteryStatus == null) {
-                    mBroadcastReceiver.onReceive(mContext, intent);
-                }
-            }
         });
 
         final IntentFilter allUserFilter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index b3c1158..49e9783 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -16,6 +16,10 @@
 
 package com.android.keyguard.dagger;
 
+import static com.android.systemui.biometrics.SidefpsControllerKt.hasSideFpsSensor;
+
+import android.annotation.Nullable;
+import android.hardware.fingerprint.FingerprintManager;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
@@ -23,9 +27,14 @@
 import com.android.keyguard.KeyguardSecurityContainer;
 import com.android.keyguard.KeyguardSecurityViewFlipper;
 import com.android.systemui.R;
+import com.android.systemui.biometrics.SidefpsController;
 import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 
+import java.util.Optional;
+
+import javax.inject.Provider;
+
 import dagger.Module;
 import dagger.Provides;
 
@@ -60,4 +69,16 @@
             KeyguardSecurityContainer containerView) {
         return containerView.findViewById(R.id.view_flipper);
     }
+
+    /** Provides {@link SidefpsController} if the device has the side fingerprint sensor. */
+    @Provides
+    @KeyguardBouncerScope
+    static Optional<SidefpsController> providesOptionalSidefpsController(
+            @Nullable FingerprintManager fingerprintManager,
+            Provider<SidefpsController> sidefpsControllerProvider) {
+        if (!hasSideFpsSensor(fingerprintManager)) {
+            return Optional.empty();
+        }
+        return Optional.of(sidefpsControllerProvider.get());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index b05582e..ca94b8c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui;
 
-import android.app.ActivityThread;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -28,14 +27,13 @@
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
+import com.android.systemui.util.InitializationChecker;
 import com.android.wm.shell.dagger.WMShellConcurrencyModule;
 import com.android.wm.shell.transition.ShellTransitions;
 
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
 
 import javax.inject.Provider;
 
@@ -49,7 +47,7 @@
     private GlobalRootComponent mRootComponent;
     private WMComponent mWMComponent;
     private SysUIComponent mSysUIComponent;
-    private boolean mInitializeComponents;
+    private InitializationChecker mInitializationChecker;
 
     public static <T extends SystemUIFactory> T getInstance() {
         return (T) mFactory;
@@ -91,15 +89,17 @@
     @VisibleForTesting
     public void init(Context context, boolean fromTest)
             throws ExecutionException, InterruptedException {
-        // Only initialize components for the main system ui process running as the primary user
-        mInitializeComponents = !fromTest
-                && android.os.Process.myUserHandle().isSystem()
-                && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName());
-        mRootComponent = buildGlobalRootComponent(context);
+        mRootComponent = getGlobalRootComponentBuilder()
+                .context(context)
+                .instrumentationTest(fromTest)
+                .build();
+
+        mInitializationChecker = mRootComponent.getInitializationChecker();
+        boolean initializeComponents = mInitializationChecker.initializeComponents();
 
         // Stand up WMComponent
         setupWmComponent(context);
-        if (mInitializeComponents) {
+        if (initializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
             mWMComponent.init();
@@ -107,7 +107,7 @@
 
         // And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
         SysUIComponent.Builder builder = mRootComponent.getSysUIComponent();
-        if (mInitializeComponents) {
+        if (initializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
             builder = prepareSysUIComponentBuilder(builder, mWMComponent)
@@ -147,7 +147,7 @@
                     .setBackAnimation(Optional.ofNullable(null));
         }
         mSysUIComponent = builder.build();
-        if (mInitializeComponents) {
+        if (initializeComponents) {
             mSysUIComponent.init();
         }
 
@@ -165,7 +165,8 @@
      */
     private void setupWmComponent(Context context) {
         WMComponent.Builder wmBuilder = mRootComponent.getWMComponentBuilder();
-        if (!mInitializeComponents || !WMShellConcurrencyModule.enableShellMainThread(context)) {
+        if (!mInitializationChecker.initializeComponents()
+                || !WMShellConcurrencyModule.enableShellMainThread(context)) {
             // If running under tests or shell thread is not enabled, we don't need anything special
             mWMComponent = wmBuilder.build();
             return;
@@ -197,14 +198,8 @@
         return sysUIBuilder;
     }
 
-    protected GlobalRootComponent buildGlobalRootComponent(Context context) {
-        return DaggerGlobalRootComponent.builder()
-                .context(context)
-                .build();
-    }
-
-    protected boolean shouldInitializeComponents() {
-        return mInitializeComponents;
+    protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() {
+        return DaggerGlobalRootComponent.builder();
     }
 
     public GlobalRootComponent getRootComponent() {
@@ -239,14 +234,4 @@
     public Map<Class<?>, Provider<CoreStartable>> getStartableComponentsPerUser() {
         return mSysUIComponent.getPerUserStartables();
     }
-
-    /**
-     * Creates an instance of ScreenshotNotificationSmartActionsProvider.
-     * This method is overridden in vendor specific implementation of Sys UI.
-     */
-    public ScreenshotNotificationSmartActionsProvider
-                createScreenshotNotificationSmartActionsProvider(
-                        Context context, Executor executor, Handler uiHandler) {
-        return new ScreenshotNotificationSmartActionsProvider();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index 04e2dccd..bbffb73 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -34,16 +34,16 @@
 import android.os.Handler
 import android.util.Log
 import android.util.RotationUtils
-import android.view.View.AccessibilityDelegate
-import android.view.accessibility.AccessibilityEvent
 import android.view.Display
 import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.Surface
 import android.view.View
+import android.view.View.AccessibilityDelegate
 import android.view.ViewPropertyAnimator
 import android.view.WindowInsets
 import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
 import androidx.annotation.RawRes
 import com.airbnb.lottie.LottieAnimationView
 import com.airbnb.lottie.LottieProperty
@@ -70,13 +70,12 @@
     private val activityTaskManager: ActivityTaskManager,
     overviewProxyService: OverviewProxyService,
     displayManager: DisplayManager,
-    @Main mainExecutor: DelayableExecutor,
+    @Main private val mainExecutor: DelayableExecutor,
     @Main private val handler: Handler
 ) {
     @VisibleForTesting
     val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager
-        ?.sensorPropertiesInternal
-        ?.firstOrNull { it.isAnySidefpsType }
+        ?.sideFpsSensorProperties
         ?: throw IllegalStateException("no side fingerprint sensor")
 
     @VisibleForTesting
@@ -135,25 +134,34 @@
     }
 
     init {
-        fingerprintManager?.setSidefpsController(object : ISidefpsController.Stub() {
-            override fun show(
-                sensorId: Int,
-                @BiometricOverlayConstants.ShowReason reason: Int
-            ) = if (reason.isReasonToShow(activityTaskManager)) doShow() else hide(sensorId)
+        fingerprintManager?.setSidefpsController(
+            object : ISidefpsController.Stub() {
+                override fun show(
+                    sensorId: Int,
+                    @BiometricOverlayConstants.ShowReason reason: Int
+                ) = if (reason.isReasonToShow(activityTaskManager)) show() else hide()
 
-            private fun doShow() = mainExecutor.execute {
-                if (overlayView == null) {
-                    createOverlayForDisplay()
-                } else {
-                    Log.v(TAG, "overlay already shown")
-                }
-            }
-
-            override fun hide(sensorId: Int) = mainExecutor.execute { overlayView = null }
-        })
+                override fun hide(sensorId: Int) = hide()
+            })
         overviewProxyService.addCallback(overviewProxyListener)
     }
 
+    /** Shows the side fps overlay if not already shown. */
+    fun show() {
+        mainExecutor.execute {
+            if (overlayView == null) {
+                createOverlayForDisplay()
+            } else {
+                Log.v(TAG, "overlay already shown")
+            }
+        }
+    }
+
+    /** Hides the fps overlay if shown. */
+    fun hide() {
+        mainExecutor.execute { overlayView = null }
+    }
+
     private fun onOrientationChanged() {
         if (overlayView != null) {
             createOverlayForDisplay()
@@ -266,6 +274,12 @@
     }
 }
 
+private val FingerprintManager?.sideFpsSensorProperties: FingerprintSensorPropertiesInternal?
+    get() = this?.sensorPropertiesInternal?.firstOrNull { it.isAnySidefpsType }
+
+/** Returns [True] when the device has a side fingerprint sensor. */
+fun FingerprintManager?.hasSideFpsSensor(): Boolean = this?.sideFpsSensorProperties != null
+
 @BiometricOverlayConstants.ShowReason
 private fun Int.isReasonToShow(activityTaskManager: ActivityTaskManager): Boolean = when (this) {
     REASON_AUTH_KEYGUARD -> false
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 4f55ba4..9e33ee1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 
+import com.android.systemui.dagger.qualifiers.InstrumentationTest;
+import com.android.systemui.util.InitializationChecker;
+
 import javax.inject.Singleton;
 
 import dagger.BindsInstance;
@@ -37,7 +40,8 @@
     interface Builder {
         @BindsInstance
         Builder context(Context context);
-
+        @BindsInstance
+        Builder instrumentationTest(@InstrumentationTest boolean test);
         GlobalRootComponent build();
     }
 
@@ -50,4 +54,9 @@
      * Builder for a {@link SysUIComponent}, which makes it a subcomponent of this class.
      */
     SysUIComponent.Builder getSysUIComponent();
+
+    /**
+     * Returns an {@link InitializationChecker}.
+     */
+    InitializationChecker getInitializationChecker();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index c4fca60..2c1463d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -44,6 +44,7 @@
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.screenshot.ReferenceScreenshotModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -99,6 +100,7 @@
         MediaModule.class,
         PowerModule.class,
         QSModule.class,
+        ReferenceScreenshotModule.class,
         StartCentralSurfacesModule.class,
         VolumeModule.class
 })
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index a9f34085..6db3e82 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.media.RingtonePlayer
 import com.android.systemui.power.PowerUI
 import com.android.systemui.recents.Recents
+import com.android.systemui.settings.dagger.MultiUserUtilsModule
 import com.android.systemui.shortcut.ShortcutKeyDispatcher
 import com.android.systemui.statusbar.notification.InstantAppNotifier
 import com.android.systemui.statusbar.phone.KeyguardLiftController
@@ -51,7 +52,7 @@
 /**
  * Collection of {@link CoreStartable}s that should be run on AOSP.
  */
-@Module
+@Module(includes = [MultiUserUtilsModule::class])
 abstract class SystemUICoreStartableModule {
     /** Inject into AuthController.  */
     @Binds
@@ -205,4 +206,4 @@
     @IntoMap
     @ClassKey(KeyguardLiftController::class)
     abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 137e288..c8747fc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -50,7 +50,7 @@
 import com.android.systemui.privacy.PrivacyModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.screenshot.dagger.ScreenshotModule;
-import com.android.systemui.settings.dagger.SettingsModule;
+import com.android.systemui.settings.dagger.MultiUserUtilsModule;
 import com.android.systemui.smartspace.dagger.SmartspaceModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -127,7 +127,7 @@
             QsFrameTranslateModule.class,
             ScreenshotModule.class,
             SensorModule.class,
-            SettingsModule.class,
+            MultiUserUtilsModule.class,
             SettingsUtilModule.class,
             SmartRepliesInflationModule.class,
             SmartspaceModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/InstrumentationTest.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/InstrumentationTest.java
new file mode 100644
index 0000000..a803a39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/InstrumentationTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+
+/**
+ * An annotation for injecting whether or not we are running in a test environment.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface InstrumentationTest {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index d0ac1c0..245ea21 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -199,7 +199,8 @@
     public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
             new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
 
-    public static final BooleanFlag NEW_BACK_AFFORDANCE = new BooleanFlag(1203, true);
+    public static final BooleanFlag NEW_BACK_AFFORDANCE =
+            new BooleanFlag(1203, false /* default */, true /* teamfood */);
 
     // Pay no attention to the reflection behind the curtain.
     // ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index a833670..28ab83c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -30,6 +30,7 @@
 import android.view.MotionEvent
 import android.view.VelocityTracker
 import android.view.View
+import android.view.ViewConfiguration
 import android.view.WindowManager
 import android.view.animation.DecelerateInterpolator
 import android.view.animation.PathInterpolator
@@ -98,6 +99,7 @@
     context: Context,
     private var backAnimation: BackAnimation?,
     private val windowManager: WindowManager,
+    private val viewConfiguration: ViewConfiguration,
     @Main private val mainHandler: Handler,
     private val vibratorHelper: VibratorHelper,
     private val configurationController: ConfigurationController,
@@ -112,6 +114,7 @@
      */
     class Factory @Inject constructor(
         private val windowManager: WindowManager,
+        private val viewConfiguration: ViewConfiguration,
         @Main private val mainHandler: Handler,
         private val vibratorHelper: VibratorHelper,
         private val configurationController: ConfigurationController,
@@ -123,6 +126,7 @@
                 context,
                 backAnimation,
                 windowManager,
+                viewConfiguration,
                 mainHandler,
                 vibratorHelper,
                 configurationController,
@@ -164,6 +168,10 @@
 
     private var gestureStartTime = 0L
 
+    // Whether the current gesture has moved a sufficiently large amount,
+    // so that we can unambiguously start showing the ENTRY animation
+    private var hasPassedDragSlop = false
+
     private val failsafeRunnable = Runnable { onFailsafe() }
 
     private enum class GestureState {
@@ -304,18 +312,17 @@
                 startX = event.x
                 startY = event.y
                 gestureStartTime = SystemClock.uptimeMillis()
-
-                // Reset the arrow to the side
-                updateArrowState(GestureState.ENTRY)
-
-                windowManager.updateViewLayout(mView, layoutParams)
-                mView.startTrackingShowBackArrowLatency()
             }
-            MotionEvent.ACTION_MOVE -> handleMoveEvent(event)
+            MotionEvent.ACTION_MOVE -> {
+                // only go to the ENTRY state after some minimum motion has occurred
+                if (dragSlopExceeded(event.x, startX)) {
+                    handleMoveEvent(event)
+                }
+            }
             MotionEvent.ACTION_UP -> {
                 if (currentState == GestureState.ACTIVE) {
                     updateArrowState(if (isFlung()) GestureState.FLUNG else GestureState.COMMITTED)
-                } else {
+                } else if (currentState != GestureState.GONE) { // if invisible, skip animation
                     updateArrowState(GestureState.CANCELLED)
                 }
                 velocityTracker = null
@@ -330,6 +337,28 @@
         }
     }
 
+    /**
+     * Returns false until the current gesture exceeds the touch slop threshold,
+     * and returns true thereafter (we reset on the subsequent back gesture).
+     * The moment it switches from false -> true is important,
+     * because that's when we switch state, from GONE -> ENTRY.
+     * @return whether the current gesture has moved past a minimum threshold.
+     */
+    private fun dragSlopExceeded(curX: Float, startX: Float): Boolean {
+        if (hasPassedDragSlop) return true
+
+        if (abs(curX - startX) > viewConfiguration.scaledTouchSlop) {
+            // Reset the arrow to the side
+            updateArrowState(GestureState.ENTRY)
+
+            windowManager.updateViewLayout(mView, layoutParams)
+            mView.startTrackingShowBackArrowLatency()
+
+            hasPassedDragSlop = true
+        }
+        return hasPassedDragSlop
+    }
+
     private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {
         if (!currentState.isInteractive())
             return
@@ -533,6 +562,7 @@
     }
 
     private fun resetOnDown() {
+        hasPassedDragSlop = false
         hasHapticPlayed = false
         totalTouchDelta = 0f
         vibrationTime = 0
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index bd6a5fc..067f4cb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -53,7 +53,6 @@
 import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
-import android.view.WindowMetrics;
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
@@ -133,7 +132,7 @@
                 @Override
                 public void onPrioritizedRotation(@Surface.Rotation int rotation) {
                     mStartingQuickstepRotation = rotation;
-                    updateDisabledForQuickstep(mContext.getResources().getConfiguration());
+                    updateDisabledForQuickstep(mLastReportedConfig);
                 }
             };
 
@@ -183,6 +182,7 @@
     private final IWindowManager mWindowManagerService;
     private final Optional<Pip> mPipOptional;
     private final FalsingManager mFalsingManager;
+    private final Configuration mLastReportedConfig = new Configuration();
     // Activities which should not trigger Back gesture.
     private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>();
 
@@ -338,6 +338,7 @@
         mLatencyTracker = latencyTracker;
         mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
         mFeatureFlags = featureFlags;
+        mLastReportedConfig.setTo(mContext.getResources().getConfiguration());
         ComponentName recentsComponentName = ComponentName.unflattenFromString(
                 context.getString(com.android.internal.R.string.config_recentsComponentName));
         if (recentsComponentName != null) {
@@ -886,12 +887,12 @@
         if (DEBUG_MISSING_GESTURE) {
             Log.d(DEBUG_MISSING_GESTURE_TAG, "Config changed: config=" + newConfig);
         }
+        mLastReportedConfig.updateFrom(newConfig);
         updateDisplaySize();
     }
 
     private void updateDisplaySize() {
-        WindowMetrics metrics = mWindowManager.getMaximumWindowMetrics();
-        Rect bounds = metrics.getBounds();
+        Rect bounds = mLastReportedConfig.windowConfiguration.getMaxBounds();
         mDisplaySize.set(bounds.width(), bounds.height());
         if (DEBUG_MISSING_GESTURE) {
             Log.d(DEBUG_MISSING_GESTURE_TAG, "Update display size: mDisplaySize=" + mDisplaySize);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
index daaa8973..814b8e9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -102,7 +102,7 @@
                     ? ACTION_TYPE_EDIT
                     : ACTION_TYPE_SHARE;
             mScreenshotSmartActions.notifyScreenshotAction(
-                    context, intent.getStringExtra(EXTRA_ID), actionType, false, null);
+                    intent.getStringExtra(EXTRA_ID), actionType, false, null);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
index 8d44205..e0346f2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
@@ -62,7 +62,7 @@
         });
         if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
             mScreenshotSmartActions.notifyScreenshotAction(
-                    context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null);
+                    intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
new file mode 100644
index 0000000..6224e1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ *
+ */
+@Module
+public interface ReferenceScreenshotModule {
+    /** */
+    @Provides
+    static ScreenshotNotificationSmartActionsProvider providesScrnshtNotifSmartActionsProvider() {
+        return new ScreenshotNotificationSmartActionsProvider();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 50ee1f7..f248d69 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -38,7 +38,6 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -49,7 +48,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
 
 import com.google.common.util.concurrent.ListenableFuture;
@@ -89,7 +87,10 @@
     SaveImageInBackgroundTask(Context context, ImageExporter exporter,
             ScreenshotSmartActions screenshotSmartActions,
             ScreenshotController.SaveImageInBackgroundData data,
-            Supplier<ActionTransition> sharedElementTransition) {
+            Supplier<ActionTransition> sharedElementTransition,
+            ScreenshotNotificationSmartActionsProvider
+                    screenshotNotificationSmartActionsProvider
+    ) {
         mContext = context;
         mScreenshotSmartActions = screenshotSmartActions;
         mImageData = new ScreenshotController.SavedImageData();
@@ -103,15 +104,7 @@
         // Initialize screenshot notification smart actions provider.
         mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
-        if (mSmartActionsEnabled) {
-            mSmartActionsProvider =
-                    SystemUIFactory.getInstance()
-                            .createScreenshotNotificationSmartActionsProvider(
-                                    context, THREAD_POOL_EXECUTOR, new Handler());
-        } else {
-            // If smart actions is not enabled use empty implementation.
-            mSmartActionsProvider = new ScreenshotNotificationSmartActionsProvider();
-        }
+        mSmartActionsProvider = screenshotNotificationSmartActionsProvider;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index c213f19..89a15f6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -263,6 +263,8 @@
     private final ScrollCaptureController mScrollCaptureController;
     private final LongScreenshotData mLongScreenshotHolder;
     private final boolean mIsLowRamDevice;
+    private final ScreenshotNotificationSmartActionsProvider
+            mScreenshotNotificationSmartActionsProvider;
     private final TimeoutHandler mScreenshotHandler;
 
     private ScreenshotView mScreenshotView;
@@ -298,7 +300,9 @@
             LongScreenshotData longScreenshotHolder,
             ActivityManager activityManager,
             TimeoutHandler timeoutHandler,
-            BroadcastSender broadcastSender) {
+            BroadcastSender broadcastSender,
+            ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider
+    ) {
         mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
         mScrollCaptureClient = scrollCaptureClient;
@@ -308,6 +312,7 @@
         mScrollCaptureController = scrollCaptureController;
         mLongScreenshotHolder = longScreenshotHolder;
         mIsLowRamDevice = activityManager.isLowRamDevice();
+        mScreenshotNotificationSmartActionsProvider = screenshotNotificationSmartActionsProvider;
         mBgExecutor = Executors.newSingleThreadExecutor();
         mBroadcastSender = broadcastSender;
 
@@ -956,7 +961,8 @@
         }
 
         mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter,
-                mScreenshotSmartActions, data, getActionTransitionSupplier());
+                mScreenshotSmartActions, data, getActionTransitionSupplier(),
+                mScreenshotNotificationSmartActionsProvider);
         mSaveInBgTask.execute();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 0527818..68b46d2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.screenshot;
 
-import static android.os.AsyncTask.THREAD_POOL_EXECUTOR;
-
 import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
 import static com.android.systemui.screenshot.LogConfig.logTag;
 import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType;
@@ -25,17 +23,14 @@
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.Handler;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
@@ -46,6 +41,7 @@
 import java.util.concurrent.TimeoutException;
 
 import javax.inject.Inject;
+import javax.inject.Provider;
 
 /**
  * Collects the static functions for retrieving and acting on smart actions.
@@ -53,9 +49,17 @@
 @SysUISingleton
 public class ScreenshotSmartActions {
     private static final String TAG = logTag(ScreenshotSmartActions.class);
+    private final Provider<ScreenshotNotificationSmartActionsProvider>
+            mScreenshotNotificationSmartActionsProviderProvider;
 
     @Inject
-    public ScreenshotSmartActions() {}
+    public ScreenshotSmartActions(
+            Provider<ScreenshotNotificationSmartActionsProvider>
+                    screenshotNotificationSmartActionsProviderProvider
+    ) {
+        mScreenshotNotificationSmartActionsProviderProvider =
+                screenshotNotificationSmartActionsProviderProvider;
+    }
 
     @VisibleForTesting
     CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
@@ -165,12 +169,11 @@
         }
     }
 
-    void notifyScreenshotAction(Context context, String screenshotId, String action,
+    void notifyScreenshotAction(String screenshotId, String action,
             boolean isSmartAction, Intent intent) {
         try {
             ScreenshotNotificationSmartActionsProvider provider =
-                    SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
-                            context, THREAD_POOL_EXECUTOR, new Handler());
+                    mScreenshotNotificationSmartActionsProviderProvider.get();
             if (DEBUG_ACTIONS) {
                 Log.d(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b",
                         provider.getClass(), action, screenshotId, isSmartAction));
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
index f703058..45af187 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -60,7 +60,7 @@
         }
 
         mScreenshotSmartActions.notifyScreenshotAction(
-                context, intent.getStringExtra(EXTRA_ID), actionType, true,
+                intent.getStringExtra(EXTRA_ID), actionType, true,
                 pendingIntent.getIntent());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt
new file mode 100644
index 0000000..aa218db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings
+
+import android.content.Context
+import android.content.SharedPreferences
+import java.io.File
+
+/**
+ * Interface for retrieving file paths for file storage of system and secondary users.
+ */
+interface UserFileManager {
+    /**
+     * Return the file based on current user.
+     */
+    fun getFile(fileName: String, userId: Int): File
+    /**
+     * Get shared preferences from user.
+     */
+    fun getSharedPreferences(
+        fileName: String,
+        @Context.PreferencesMode mode: Int,
+        userId: Int
+    ): SharedPreferences
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
new file mode 100644
index 0000000..8c8f54f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.SharedPreferences
+import android.os.Environment
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.io.File
+import javax.inject.Inject
+
+/**
+ * Implementation for retrieving file paths for file storage of system and secondary users.
+ * Files lie in {File Directory}/UserFileManager/{User Id} for secondary user.
+ * For system user, we use the conventional {File Directory}
+ */
+@SysUISingleton
+class UserFileManagerImpl @Inject constructor(
+    // Context of system process and system user.
+    val context: Context,
+    val userManager: UserManager,
+    val broadcastDispatcher: BroadcastDispatcher,
+    @Background val backgroundExecutor: DelayableExecutor
+) : UserFileManager, CoreStartable(context) {
+    companion object {
+        private const val FILES = "files"
+        private const val SHARED_PREFS = "shared_prefs"
+        internal const val ID = "UserFileManager"
+    }
+
+   private val broadcastReceiver = object : BroadcastReceiver() {
+        /**
+         * Listen to Intent.ACTION_USER_REMOVED to clear user data.
+         */
+        override fun onReceive(context: Context, intent: Intent) {
+            if (intent.action == Intent.ACTION_USER_REMOVED) {
+                clearDeletedUserData()
+            }
+        }
+    }
+
+    /**
+     * Poll for user-specific directories to delete upon start up.
+     */
+    override fun start() {
+        clearDeletedUserData()
+        val filter = IntentFilter().apply {
+            addAction(Intent.ACTION_USER_REMOVED)
+        }
+        broadcastDispatcher.registerReceiver(broadcastReceiver, filter, backgroundExecutor)
+    }
+
+    /**
+     * Return the file based on current user.
+     */
+    override fun getFile(fileName: String, userId: Int): File {
+        return if (UserHandle(userId).isSystem) {
+            Environment.buildPath(
+                context.filesDir,
+                fileName
+            )
+        } else {
+            Environment.buildPath(
+                context.filesDir,
+                ID,
+                userId.toString(),
+                FILES,
+                fileName
+            )
+        }
+    }
+
+    /**
+     * Get shared preferences from user.
+     */
+    override fun getSharedPreferences(
+        fileName: String,
+        @Context.PreferencesMode mode: Int,
+        userId: Int
+    ): SharedPreferences {
+        if (UserHandle(userId).isSystem) {
+            return context.getSharedPreferences(fileName, mode)
+        }
+        val secondaryUserDir = Environment.buildPath(
+            context.filesDir,
+            ID,
+            userId.toString(),
+            SHARED_PREFS,
+            fileName
+        )
+
+        return context.getSharedPreferences(secondaryUserDir, mode)
+    }
+
+    /**
+     * Remove dirs for deleted users.
+     */
+    @VisibleForTesting
+    internal fun clearDeletedUserData() {
+        backgroundExecutor.execute {
+            val file = Environment.buildPath(context.filesDir, ID)
+            if (!file.exists()) return@execute
+            val aliveUsers = userManager.aliveUsers.map { it.id.toString() }
+            val dirsToDelete = file.list().filter { !aliveUsers.contains(it) }
+
+            dirsToDelete.forEach { dir ->
+                try {
+                    val dirToDelete = Environment.buildPath(
+                        file,
+                        dir,
+                    )
+                    dirToDelete.deleteRecursively()
+                } catch (e: Exception) {
+                    Log.e(ID, "Deletion failed.", e)
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
similarity index 79%
rename from packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
rename to packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index 7084d3f..2f62e44 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -21,25 +21,28 @@
 import android.os.Handler;
 import android.os.UserManager;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.settings.UserContentResolverProvider;
 import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserFileManager;
+import com.android.systemui.settings.UserFileManagerImpl;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.settings.UserTrackerImpl;
 
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
 
 /**
  * Dagger Module for classes found within the com.android.systemui.settings package.
  */
 @Module
-public abstract class SettingsModule {
-
-
+public abstract class MultiUserUtilsModule {
     @Binds
     @SysUISingleton
     abstract UserContextProvider bindUserContextProvider(UserTracker tracker);
@@ -62,4 +65,12 @@
         tracker.initialize(startingUser);
         return tracker;
     }
+
+    @Binds
+    @IntoMap
+    @ClassKey(UserFileManagerImpl.class)
+    abstract CoreStartable bindUserFileManagerCoreStartable(UserFileManagerImpl sysui);
+
+    @Binds
+    abstract UserFileManager bindUserFileManager(UserFileManagerImpl impl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 9e02909..ca14728 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -900,16 +900,36 @@
                 mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
             }
         } else {
-            if (!mAccessibilityManager.isEnabled()
-                    && !mAccessibilityManager.isTouchExplorationEnabled()
-                    && mKeyguardUpdateMonitor.isUdfpsSupported()
-                    && mKeyguardUpdateMonitor.getUserCanSkipBouncer(
-                    KeyguardUpdateMonitor.getCurrentUser())) {
-                final int stringId = mKeyguardUpdateMonitor.getIsFaceAuthenticated()
-                        ? R.string.keyguard_face_successful_unlock_press
-                        : R.string.keyguard_unlock_press;
-                showBiometricMessage(mContext.getString(stringId));
+            final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+                    KeyguardUpdateMonitor.getCurrentUser());
+            if (canSkipBouncer) {
+                final boolean faceAuthenticated = mKeyguardUpdateMonitor.getIsFaceAuthenticated();
+                final boolean udfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
+                final boolean a11yEnabled = mAccessibilityManager.isEnabled()
+                        || mAccessibilityManager.isTouchExplorationEnabled();
+                if (udfpsSupported && faceAuthenticated) { // co-ex
+                    if (a11yEnabled) {
+                        showBiometricMessage(mContext.getString(
+                                R.string.keyguard_face_successful_unlock_swipe));
+                    } else {
+                        showBiometricMessage(mContext.getString(
+                                R.string.keyguard_face_successful_unlock_press));
+                    }
+                } else if (faceAuthenticated) { // face-only
+                    showBiometricMessage(mContext.getString(
+                            R.string.keyguard_face_successful_unlock_swipe));
+                } else if (udfpsSupported) { // udfps-only
+                    if (a11yEnabled) {
+                        showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+                    } else {
+                        showBiometricMessage(mContext.getString(
+                                R.string.keyguard_unlock_press));
+                    }
+                } else { // no security or unlocked by a trust agent
+                    showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+                }
             } else {
+                // suggest swiping up for the primary authentication bouncer
                 showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index a57440c..f662a04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.statusbar.notification.collection
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
@@ -250,4 +249,4 @@
     fun getInternalNotifUpdater(name: String?): InternalNotifUpdater {
         return mNotifCollection.getInternalNotifUpdater(name)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 5a281b1..657c394d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -145,4 +145,4 @@
         private const val TAG = "ViewConfigCoordinator"
         private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 7dd3672..a7719d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -23,6 +23,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
@@ -33,10 +34,13 @@
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
+import javax.inject.Inject;
+
 /**
  * Callback for when a user interacts with a {@see ExpandableNotificationRow}. Sends relevant
  * information about the interaction to the notification pipeline.
  */
+@SysUISingleton
 public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback {
     private final NotificationVisibilityProvider mVisibilityProvider;
     private final NotifCollection mNotifCollection;
@@ -44,6 +48,7 @@
     private final StatusBarStateController mStatusBarStateController;
     private final VisualStabilityCoordinator mVisualStabilityCoordinator;
 
+    @Inject
     public OnUserInteractionCallbackImpl(
             NotificationVisibilityProvider visibilityProvider,
             NotifCollection notifCollection,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
deleted file mode 100644
index bdbb0eb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2010 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.collection.legacy;
-
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SPEW;
-
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
-
-import org.jetbrains.annotations.NotNull;
-
-import javax.inject.Inject;
-
-/**
- * This is some logic extracted from the
- * {@link com.android.systemui.statusbar.phone.StatusBarNotificationPresenter}
- * into a class that implements a new-pipeline interface so that the new pipeline can implement it
- * correctly.
- *
- * Specifically, this is the logic which updates notifications when uiMode and screen properties
- * change, and which closes the shade when the last notification disappears.
- */
-public class LegacyNotificationPresenterExtensions implements NotifShadeEventSource {
-    private static final String TAG = "LegacyNotifPresenter";
-    private final NotificationEntryManager mEntryManager;
-    private boolean mEntryListenerAdded;
-    private Runnable mShadeEmptiedCallback;
-    private Runnable mNotifRemovedByUserCallback;
-
-    @Inject
-    public LegacyNotificationPresenterExtensions(NotificationEntryManager entryManager) {
-        mEntryManager = entryManager;
-    }
-
-    private void ensureEntryListenerAdded() {
-        if (mEntryListenerAdded) return;
-        mEntryListenerAdded = true;
-        mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
-            @Override
-            public void onEntryRemoved(
-                    @NotNull NotificationEntry entry,
-                    NotificationVisibility visibility,
-                    boolean removedByUser,
-                    int reason) {
-                StatusBarNotification old = entry.getSbn();
-                if (SPEW) {
-                    Log.d(TAG, "removeNotification key=" + entry.getKey()
-                            + " old=" + old + " reason=" + reason);
-                }
-
-                if (old != null && !mEntryManager.hasActiveNotifications()) {
-                    if (mShadeEmptiedCallback != null) mShadeEmptiedCallback.run();
-                }
-                if (removedByUser) {
-                    if (mNotifRemovedByUserCallback != null) mNotifRemovedByUserCallback.run();
-                }
-            }
-        });
-    }
-
-    @Override
-    public void setNotifRemovedByUserCallback(@NonNull Runnable callback) {
-        if (mNotifRemovedByUserCallback != null) {
-            throw new IllegalStateException("mNotifRemovedByUserCallback already set");
-        }
-        mNotifRemovedByUserCallback = callback;
-        ensureEntryListenerAdded();
-    }
-
-    @Override
-    public void setShadeEmptiedCallback(@NonNull Runnable callback) {
-        if (mShadeEmptiedCallback != null) {
-            throw new IllegalStateException("mShadeEmptiedCallback already set");
-        }
-        mShadeEmptiedCallback = callback;
-        ensureEntryListenerAdded();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
deleted file mode 100644
index 103b14b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.legacy;
-
-import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-/**
- * Callback for when a user interacts with a {@see ExpandableNotificationRow}.
- */
-public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCallback {
-    private final NotificationEntryManager mNotificationEntryManager;
-    private final NotificationVisibilityProvider mVisibilityProvider;
-    private final HeadsUpManager mHeadsUpManager;
-    private final StatusBarStateController mStatusBarStateController;
-    private final VisualStabilityManager mVisualStabilityManager;
-    private final GroupMembershipManager mGroupMembershipManager;
-
-    public OnUserInteractionCallbackImplLegacy(
-            NotificationEntryManager notificationEntryManager,
-            NotificationVisibilityProvider visibilityProvider,
-            HeadsUpManager headsUpManager,
-            StatusBarStateController statusBarStateController,
-            VisualStabilityManager visualStabilityManager,
-            GroupMembershipManager groupMembershipManager
-    ) {
-        mNotificationEntryManager = notificationEntryManager;
-        mVisibilityProvider = visibilityProvider;
-        mHeadsUpManager = headsUpManager;
-        mStatusBarStateController = statusBarStateController;
-        mVisualStabilityManager = visualStabilityManager;
-        mGroupMembershipManager = groupMembershipManager;
-    }
-
-    /**
-     * Callback triggered when a user:
-     * 1. Manually dismisses a notification {@see ExpandableNotificationRow}.
-     * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}.
-     * {@see StatusBarNotificationActivityStarter}
-     *
-     * @param groupSummaryToDismiss the group summary that should be dismissed
-     *                              along with this dismissal. If null, does not additionally
-     *                              dismiss any notifications.
-     */
-    private void onDismiss(
-            NotificationEntry entry,
-            @NotificationListenerService.NotificationCancelReason int cancellationReason,
-            @Nullable NotificationEntry groupSummaryToDismiss
-    ) {
-        int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
-        if (mHeadsUpManager.isAlerting(entry.getKey())) {
-            dismissalSurface = NotificationStats.DISMISSAL_PEEK;
-        } else if (mStatusBarStateController.isDozing()) {
-            dismissalSurface = NotificationStats.DISMISSAL_AOD;
-        }
-
-        if (groupSummaryToDismiss != null) {
-            onDismiss(groupSummaryToDismiss, cancellationReason, null);
-        }
-
-        mNotificationEntryManager.performRemoveNotification(
-                entry.getSbn(),
-                new DismissedByUserStats(
-                        dismissalSurface,
-                        DISMISS_SENTIMENT_NEUTRAL,
-                        mVisibilityProvider.obtain(entry, true)),
-                cancellationReason
-        );
-
-    }
-
-    @Override
-    public void onImportanceChanged(NotificationEntry entry) {
-        mVisualStabilityManager.temporarilyAllowReordering();
-    }
-
-    /**
-     * @param entry that is being dismissed
-     * @return the group summary to dismiss along with this entry if this is the last entry in
-     * the group. Else, returns null.
-     */
-    @Nullable
-    private NotificationEntry getGroupSummaryToDismiss(NotificationEntry entry) {
-        if (mGroupMembershipManager.isOnlyChildInGroup(entry)) {
-            NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(entry);
-            return groupSummary.isDismissable() ? groupSummary : null;
-        }
-        return null;
-    }
-
-    @Override
-    @NonNull
-    public Runnable registerFutureDismissal(@NonNull NotificationEntry entry,
-            @CancellationReason int cancellationReason) {
-        NotificationEntry groupSummaryToDismiss = getGroupSummaryToDismiss(entry);
-        return () -> onDismiss(entry, cancellationReason, groupSummaryToDismiss);
-    }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
index 6a1e36f..ec10aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.collection.provider
 
 import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -25,6 +26,7 @@
 import javax.inject.Inject
 
 /** pipeline-agnostic implementation for getting [NotificationVisibility]. */
+@SysUISingleton
 class NotificationVisibilityProviderImpl @Inject constructor(
     private val notifDataStore: NotifLiveDataStore,
     private val notifCollection: CommonNotifCollection
@@ -46,4 +48,4 @@
         NotificationLogger.getNotificationLocation(notifCollection.getEntry(key))
 
     private fun getCount() = notifDataStore.activeNotifCount.value
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index a2cb950..1b3f83d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection.render;
 
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -28,10 +29,13 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.inject.Inject;
+
 /**
  * Provides grouping information for notification entries including information about a group's
  * expanded state.
  */
+@SysUISingleton
 public class GroupExpansionManagerImpl implements GroupExpansionManager, Coordinator {
     private final GroupMembershipManager mGroupMembershipManager;
     private final Set<OnGroupExpansionChangeListener> mOnGroupChangeListeners = new HashSet<>();
@@ -39,6 +43,7 @@
     // Set of summary keys whose groups are expanded
     private final Set<NotificationEntry> mExpandedGroups = new HashSet<>();
 
+    @Inject
     public GroupExpansionManagerImpl(GroupMembershipManager groupMembershipManager) {
         mGroupMembershipManager = groupMembershipManager;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 27a7cd7..d838252 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -41,23 +41,19 @@
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotifPipelineChoreographerModule;
 import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
-import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule;
 import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager;
 import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
-import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationPresenterExtensions;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -90,7 +86,6 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotifPanelEventsModule;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.wmshell.BubblesManager;
 
@@ -116,12 +111,10 @@
 })
 public interface NotificationsModule {
     @Binds
-    StackScrollAlgorithm.SectionProvider bindSectionProvider(
-            NotificationSectionsManager impl);
+    StackScrollAlgorithm.SectionProvider bindSectionProvider(NotificationSectionsManager impl);
 
     @Binds
-    StackScrollAlgorithm.BypassController bindBypassController(
-            KeyguardBypassController impl);
+    StackScrollAlgorithm.BypassController bindBypassController(KeyguardBypassController impl);
 
     /** Provides an instance of {@link NotificationEntryManager} */
     @SysUISingleton
@@ -194,12 +187,8 @@
     }
 
     /** Provides an instance of {@link NotifGutsViewManager} */
-    @SysUISingleton
-    @Provides
-    static NotifGutsViewManager provideNotifGutsViewManager(
-            NotificationGutsManager notificationGutsManager) {
-        return notificationGutsManager;
-    }
+    @Binds
+    NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
 
     /** Provides an instance of {@link VisualStabilityManager} */
     @SysUISingleton
@@ -253,25 +242,13 @@
     /** Provides an instance of {@link GroupMembershipManager} */
     @SysUISingleton
     @Provides
-    static GroupMembershipManager provideGroupMembershipManager(
-            NotifPipelineFlags notifPipelineFlags,
-            Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) {
-        return notifPipelineFlags.isNewPipelineEnabled()
-                ? new GroupMembershipManagerImpl()
-                : groupManagerLegacy.get();
+    static GroupMembershipManager provideGroupMembershipManager() {
+        return new GroupMembershipManagerImpl();
     }
 
     /** Provides an instance of {@link GroupExpansionManager} */
-    @SysUISingleton
-    @Provides
-    static GroupExpansionManager provideGroupExpansionManager(
-            NotifPipelineFlags notifPipelineFlags,
-            Lazy<GroupMembershipManager> groupMembershipManager,
-            Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) {
-        return notifPipelineFlags.isNewPipelineEnabled()
-                ? new GroupExpansionManagerImpl(groupMembershipManager.get())
-                : groupManagerLegacy.get();
-    }
+    @Binds
+    GroupExpansionManager provideGroupExpansionManager(GroupExpansionManagerImpl impl);
 
     /** Initializes the notification data pipeline (can be disabled via config). */
     @SysUISingleton
@@ -290,69 +267,28 @@
     /**
      * Provide the active notification collection managing the notifications to render.
      */
-    @Provides
-    @SysUISingleton
-    static CommonNotifCollection provideCommonNotifCollection(
-            NotifPipelineFlags notifPipelineFlags,
-            Lazy<NotifPipeline> pipeline,
-            NotificationEntryManager entryManager) {
-        return notifPipelineFlags.isNewPipelineEnabled()
-                ? pipeline.get() : entryManager;
-    }
+    @Binds
+    CommonNotifCollection provideCommonNotifCollection(NotifPipeline pipeline);
 
     /**
      * Provide the object which can be used to obtain NotificationVisibility objects.
      */
     @Binds
-    @SysUISingleton
     NotificationVisibilityProvider provideNotificationVisibilityProvider(
-            NotificationVisibilityProviderImpl newProvider);
+            NotificationVisibilityProviderImpl impl);
 
     /**
      * Provide the active implementation for presenting notifications.
      */
-    @Provides
-    @SysUISingleton
-    static NotifShadeEventSource provideNotifShadeEventSource(
-            NotifPipelineFlags notifPipelineFlags,
-            Lazy<ShadeEventCoordinator> shadeEventCoordinatorLazy,
-            Lazy<LegacyNotificationPresenterExtensions> legacyNotificationPresenterExtensionsLazy) {
-        return notifPipelineFlags.isNewPipelineEnabled()
-                ? shadeEventCoordinatorLazy.get()
-                : legacyNotificationPresenterExtensionsLazy.get();
-    }
+    @Binds
+    NotifShadeEventSource provideNotifShadeEventSource(ShadeEventCoordinator shadeEventCoordinator);
 
     /**
      * Provide a dismissal callback that's triggered when a user manually dismissed a notification
      * from the notification shade or it gets auto-cancelled by click.
      */
-    @Provides
-    @SysUISingleton
-    static OnUserInteractionCallback provideOnUserInteractionCallback(
-            NotifPipelineFlags notifPipelineFlags,
-            HeadsUpManager headsUpManager,
-            StatusBarStateController statusBarStateController,
-            Lazy<NotifCollection> notifCollection,
-            Lazy<NotificationVisibilityProvider> visibilityProvider,
-            Lazy<VisualStabilityCoordinator> visualStabilityCoordinator,
-            NotificationEntryManager entryManager,
-            VisualStabilityManager visualStabilityManager,
-            Lazy<GroupMembershipManager> groupMembershipManagerLazy) {
-        return notifPipelineFlags.isNewPipelineEnabled()
-                ? new OnUserInteractionCallbackImpl(
-                        visibilityProvider.get(),
-                        notifCollection.get(),
-                        headsUpManager,
-                        statusBarStateController,
-                        visualStabilityCoordinator.get())
-                : new OnUserInteractionCallbackImplLegacy(
-                        entryManager,
-                        visibilityProvider.get(),
-                        headsUpManager,
-                        statusBarStateController,
-                        visualStabilityManager,
-                        groupMembershipManagerLazy.get());
-    }
+    @Binds
+    OnUserInteractionCallback provideOnUserInteractionCallback(OnUserInteractionCallbackImpl impl);
 
     /** */
     @Binds
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 2fd02d9..defae5b 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
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.util.DumpUtilsKt.println;
@@ -5246,6 +5247,7 @@
         setClearAllInProgress(true);
         mShadeNeedsToClose = closeShade;
 
+        InteractionJankMonitor.getInstance().begin(this, CUJ_SHADE_CLEAR_ALL);
         // Decrease the delay for every row we animate to give the sense of
         // accelerating the swipes
         final int rowDelayDecrement = 5;
@@ -6158,6 +6160,7 @@
     private void onClearAllAnimationsEnd(
             List<ExpandableNotificationRow> viewsToRemove,
             @SelectedRows int selectedRows) {
+        InteractionJankMonitor.getInstance().end(CUJ_SHADE_CLEAR_ALL);
         if (mClearAllAnimationListener != null) {
             mClearAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
         }
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 a0d940e..1182fb3 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
@@ -28,7 +28,6 @@
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeCleared;
 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
 
 import android.content.res.Configuration;
@@ -38,12 +37,10 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.util.Pair;
 import android.view.Display;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -53,20 +50,17 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.KeyguardMediaController;
@@ -85,9 +79,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -163,13 +155,10 @@
     private final Resources mResources;
     private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
     private final ScrimController mScrimController;
-    private final NotifPipelineFlags mNotifPipelineFlags;
     private final NotifPipeline mNotifPipeline;
     private final NotifCollection mNotifCollection;
     private final NotificationEntryManager mNotificationEntryManager;
-    private final IStatusBarService mIStatusBarService;
     private final UiEventLogger mUiEventLogger;
-    private final LayoutInflater mLayoutInflater;
     private final NotificationRemoteInputManager mRemoteInputManager;
     private final VisualStabilityManager mVisualStabilityManager;
     private final ShadeController mShadeController;
@@ -206,8 +195,6 @@
     @Nullable
     private NotificationActivityStarter mNotificationActivityStarter;
 
-    private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
-
     @VisibleForTesting
     final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
             new View.OnAttachStateChangeListener() {
@@ -256,10 +243,7 @@
             mView.setAnimateBottomOnLayout(true);
         }
         // Let's update the footer once the notifications have been updated (in the next frame)
-        mView.post(() -> {
-            updateFooter();
-            updateSectionBoundaries("dynamic privacy changed");
-        });
+        mView.post(this::updateFooter);
     };
 
     @VisibleForTesting
@@ -634,7 +618,6 @@
             KeyguardMediaController keyguardMediaController,
             KeyguardBypassController keyguardBypassController,
             ZenModeController zenModeController,
-            SysuiColorExtractor colorExtractor,
             NotificationLockscreenUserManager lockscreenUserManager,
             MetricsLogger metricsLogger,
             DumpManager dumpManager,
@@ -647,15 +630,12 @@
             NotificationGroupManagerLegacy legacyGroupManager,
             GroupExpansionManager groupManager,
             @SilentHeader SectionHeaderController silentHeaderController,
-            NotifPipelineFlags notifPipelineFlags,
             NotifPipeline notifPipeline,
             NotifCollection notifCollection,
             NotificationEntryManager notificationEntryManager,
             LockscreenShadeTransitionController lockscreenShadeTransitionController,
             ShadeTransitionController shadeTransitionController,
-            IStatusBarService iStatusBarService,
             UiEventLogger uiEventLogger,
-            LayoutInflater layoutInflater,
             NotificationRemoteInputManager remoteInputManager,
             VisualStabilityManager visualStabilityManager,
             ShadeController shadeController,
@@ -698,14 +678,11 @@
                 mCentralSurfaces.requestNotificationUpdate("onGroupsChanged");
             }
         });
-        mNotifPipelineFlags = notifPipelineFlags;
         mSilentHeaderController = silentHeaderController;
         mNotifPipeline = notifPipeline;
         mNotifCollection = notifCollection;
         mNotificationEntryManager = notificationEntryManager;
-        mIStatusBarService = iStatusBarService;
         mUiEventLogger = uiEventLogger;
-        mLayoutInflater = layoutInflater;
         mRemoteInputManager = remoteInputManager;
         mVisualStabilityManager = visualStabilityManager;
         mShadeController = shadeController;
@@ -745,21 +722,12 @@
                 .setOnMenuEventListener(mMenuEventListener)
                 .build();
 
-        if (mNotifPipelineFlags.isNewPipelineEnabled()) {
-            mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
-                @Override
-                public void onEntryUpdated(NotificationEntry entry) {
-                    mView.onEntryUpdated(entry);
-                }
-            });
-        } else {
-            mNotificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
-                @Override
-                public void onPreEntryUpdated(NotificationEntry entry) {
-                    mView.onEntryUpdated(entry);
-                }
-            });
-        }
+        mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
+            @Override
+            public void onEntryUpdated(NotificationEntry entry) {
+                mView.onEntryUpdated(entry);
+            }
+        });
 
         mView.initView(mView.getContext(), mSwipeHelper, mNotificationStackSizeCalculator);
         mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
@@ -1231,10 +1199,6 @@
         Trace.endSection();
     }
 
-    public boolean areNotificationsHiddenInShade() {
-        return mZenModeController.areNotificationsHiddenInShade();
-    }
-
     public boolean isShowingEmptyShadeView() {
         return mShowEmptyShadeView;
     }
@@ -1339,15 +1303,6 @@
         };
     }
 
-    public void updateSectionBoundaries(String reason) {
-        if (mNotifPipelineFlags.isNewPipelineEnabled()) {
-            return;
-        }
-        Trace.beginSection("NSSLC.updateSectionBoundaries");
-        mView.updateSectionBoundaries(reason);
-        Trace.endSection();
-    }
-
     public void updateFooter() {
         Trace.beginSection("NSSLC.updateFooter");
         mView.updateFooter();
@@ -1463,39 +1418,18 @@
 
     private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
             @SelectedRows int selectedRows) {
-        if (mNotifPipelineFlags.isNewPipelineEnabled()) {
-            if (selectedRows == ROWS_ALL) {
-                mNotifCollection.dismissAllNotifications(
-                        mLockscreenUserManager.getCurrentUserId());
-            } else {
-                final List<Pair<NotificationEntry, DismissedByUserStats>>
-                        entriesWithRowsDismissedFromShade = new ArrayList<>();
-                for (ExpandableNotificationRow row : viewsToRemove) {
-                    final NotificationEntry entry = row.getEntry();
-                    entriesWithRowsDismissedFromShade.add(
-                            new Pair<>(entry, getDismissedByUserStats(entry)));
-                }
-                mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
-            }
+        if (selectedRows == ROWS_ALL) {
+            mNotifCollection.dismissAllNotifications(
+                    mLockscreenUserManager.getCurrentUserId());
         } else {
-            for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
-                if (canChildBeCleared(rowToRemove)) {
-                    mNotificationEntryManager.performRemoveNotification(
-                            rowToRemove.getEntry().getSbn(),
-                            getDismissedByUserStats(rowToRemove.getEntry()),
-                            NotificationListenerService.REASON_CANCEL_ALL);
-                } else {
-                    rowToRemove.resetTranslation();
-                }
+            final List<Pair<NotificationEntry, DismissedByUserStats>>
+                    entriesWithRowsDismissedFromShade = new ArrayList<>();
+            for (ExpandableNotificationRow row : viewsToRemove) {
+                final NotificationEntry entry = row.getEntry();
+                entriesWithRowsDismissedFromShade.add(
+                        new Pair<>(entry, getDismissedByUserStats(entry)));
             }
-            if (selectedRows == ROWS_ALL) {
-                try {
-                    // TODO(b/169585328): Do not clear media player notifications
-                    mIStatusBarService.onClearAllNotifications(
-                            mLockscreenUserManager.getCurrentUserId());
-                } catch (Exception ignored) {
-                }
-            }
+            mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 9f65350..a8486fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1167,9 +1167,6 @@
 
         mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
         mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
-        if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
-            mHeadsUpManager.addListener(mVisualStabilityManager);
-        }
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
 
         createNavigationBar(result);
@@ -4336,9 +4333,6 @@
                 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
             }
 
-            if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
-                mViewHierarchyManager.updateRowStates();
-            }
             mScreenPinningRequest.onConfigurationChanged();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
new file mode 100644
index 0000000..3942dae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.util.ViewController
+import javax.inject.Inject
+
+class KeyguardBottomAreaViewController @Inject constructor(view: KeyguardBottomAreaView) :
+    ViewController<KeyguardBottomAreaView> (view) {
+    override fun onViewAttached() {
+    }
+
+    override fun onViewDetached() {
+    }
+
+    fun getView(): KeyguardBottomAreaView {
+        // TODO: remove this method.
+        return mView
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index ed3d4ad..0001cd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -266,6 +266,9 @@
 
     private void setVisibility(@View.Visibility int visibility) {
         mContainer.setVisibility(visibility);
+        if (mKeyguardViewController != null) {
+            mKeyguardViewController.onBouncerVisibilityChanged(visibility);
+        }
         dispatchVisibilityChanged();
     }
 
@@ -397,10 +400,6 @@
         return mShowingSoon || mExpansion != EXPANSION_HIDDEN && mExpansion != EXPANSION_VISIBLE;
     }
 
-    public boolean getShowingSoon() {
-        return mShowingSoon;
-    }
-
     /**
      * @return {@code true} when bouncer's pre-hide animation already started but isn't completely
      *         hidden yet, {@code false} otherwise.
@@ -645,7 +644,7 @@
         /**
          * Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_VISIBLE}.
          * This is NOT called each time the bouncer is shown, but rather only when the fully
-         * shown amount has changed based on the panel expansion. The bouncer is visibility
+         * shown amount has changed based on the panel expansion. The bouncer's visibility
          * can still change when the expansion amount hasn't changed.
          * See {@link KeyguardBouncer#isShowing()} for the checks for the bouncer showing state.
          */
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 b257d14..a89b62c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -343,6 +343,8 @@
     private final LockIconViewController mLockIconViewController;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private final NotificationsQSContainerController mNotificationsQSContainerController;
+    private final Provider<KeyguardBottomAreaViewController>
+            mKeyguardBottomAreaViewControllerProvider;
     private boolean mAnimateNextPositionUpdate;
     private float mQuickQsHeaderHeight;
     private final ScreenOffAnimationController mScreenOffAnimationController;
@@ -741,6 +743,7 @@
             InteractionJankMonitor interactionJankMonitor,
             QsFrameTranslateController qsFrameTranslateController,
             SysUiState sysUiState,
+            Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             NotificationListContainer notificationListContainer,
             PanelEventsEmitter panelEventsEmitter,
@@ -782,6 +785,7 @@
         mNotificationsQSContainerController = notificationsQSContainerController;
         mNotificationListContainer = notificationListContainer;
         mNotificationStackSizeCalculator = notificationStackSizeCalculator;
+        mKeyguardBottomAreaViewControllerProvider = keyguardBottomAreaViewControllerProvider;
         mNotificationsQSContainerController.init();
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
         mNotificationIconAreaController = notificationIconAreaController;
@@ -1222,8 +1226,7 @@
         int index = mView.indexOfChild(mKeyguardBottomArea);
         mView.removeView(mKeyguardBottomArea);
         KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
-        mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate(
-                R.layout.keyguard_bottom_area, mView, false);
+        mKeyguardBottomArea = mKeyguardBottomAreaViewControllerProvider.get().getView();
         mKeyguardBottomArea.initFrom(oldBottomArea);
         mView.addView(mKeyguardBottomArea, index);
         initBottomArea();
@@ -3918,7 +3921,6 @@
      * {@link ShadeViewManager}.
      */
     public void updateNotificationViews(String reason) {
-        mNotificationStackScrollLayoutController.updateSectionBoundaries(reason);
         mNotificationStackScrollLayoutController.updateFooter();
 
         mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index d2fc1af..0e278d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -599,9 +599,7 @@
             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
         if (target == mExpandedHeight && mOverExpansion == 0.0f) {
             // We're at the target and didn't fling and there's no overshoot
-            endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
-            mKeyguardStateController.notifyPanelFlingEnd();
-            notifyExpandingFinished();
+            onFlingEnd(false /* cancelled */);
             return;
         }
         mIsFlinging = true;
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 d250022..b5e4302 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -965,7 +965,7 @@
 
     @Override
     public boolean bouncerIsOrWillBeShowing() {
-        return isBouncerShowing() || mBouncer.getShowingSoon();
+        return isBouncerShowing() || mBouncer.inTransit();
     }
 
     public boolean isFullscreenBouncer() {
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 cf776e3..72db884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -52,21 +52,15 @@
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationClickNotifier;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -92,24 +86,19 @@
 
     private final Context mContext;
 
-    private final CommandQueue mCommandQueue;
     private final Handler mMainThreadHandler;
     private final Executor mUiBgExecutor;
 
-    private final NotificationEntryManager mEntryManager;
-    private final NotifPipeline mNotifPipeline;
     private final NotificationVisibilityProvider mVisibilityProvider;
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final ActivityStarter mActivityStarter;
     private final NotificationClickNotifier mClickNotifier;
-    private final StatusBarStateController mStatusBarStateController;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final KeyguardManager mKeyguardManager;
     private final IDreamManager mDreamManager;
     private final Optional<BubblesManager> mBubblesManagerOptional;
     private final Lazy<AssistManager> mAssistManagerLazy;
     private final NotificationRemoteInputManager mRemoteInputManager;
-    private final GroupMembershipManager mGroupMembershipManager;
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     private final ShadeController mShadeController;
     private final KeyguardStateController mKeyguardStateController;
@@ -118,7 +107,6 @@
     private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback;
     private final ActivityIntentHelper mActivityIntentHelper;
 
-    private final NotifPipelineFlags mNotifPipelineFlags;
     private final MetricsLogger mMetricsLogger;
     private final StatusBarNotificationActivityStarterLogger mLogger;
 
@@ -134,23 +122,19 @@
     @Inject
     StatusBarNotificationActivityStarter(
             Context context,
-            CommandQueue commandQueue,
             Handler mainThreadHandler,
             Executor uiBgExecutor,
-            NotificationEntryManager entryManager,
             NotifPipeline notifPipeline,
             NotificationVisibilityProvider visibilityProvider,
             HeadsUpManagerPhone headsUpManager,
             ActivityStarter activityStarter,
             NotificationClickNotifier clickNotifier,
-            StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             KeyguardManager keyguardManager,
             IDreamManager dreamManager,
             Optional<BubblesManager> bubblesManagerOptional,
             Lazy<AssistManager> assistManagerLazy,
             NotificationRemoteInputManager remoteInputManager,
-            GroupMembershipManager groupMembershipManager,
             NotificationLockscreenUserManager lockscreenUserManager,
             ShadeController shadeController,
             KeyguardStateController keyguardStateController,
@@ -158,7 +142,6 @@
             LockPatternUtils lockPatternUtils,
             StatusBarRemoteInputCallback remoteInputCallback,
             ActivityIntentHelper activityIntentHelper,
-            NotifPipelineFlags notifPipelineFlags,
             MetricsLogger metricsLogger,
             StatusBarNotificationActivityStarterLogger logger,
             OnUserInteractionCallback onUserInteractionCallback,
@@ -168,23 +151,18 @@
             ActivityLaunchAnimator activityLaunchAnimator,
             NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
         mContext = context;
-        mCommandQueue = commandQueue;
         mMainThreadHandler = mainThreadHandler;
         mUiBgExecutor = uiBgExecutor;
-        mEntryManager = entryManager;
-        mNotifPipeline = notifPipeline;
         mVisibilityProvider = visibilityProvider;
         mHeadsUpManager = headsUpManager;
         mActivityStarter = activityStarter;
         mClickNotifier = clickNotifier;
-        mStatusBarStateController = statusBarStateController;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardManager = keyguardManager;
         mDreamManager = dreamManager;
         mBubblesManagerOptional = bubblesManagerOptional;
         mAssistManagerLazy = assistManagerLazy;
         mRemoteInputManager = remoteInputManager;
-        mGroupMembershipManager = groupMembershipManager;
         mLockscreenUserManager = lockscreenUserManager;
         mShadeController = shadeController;
         mKeyguardStateController = keyguardStateController;
@@ -192,7 +170,6 @@
         mLockPatternUtils = lockPatternUtils;
         mStatusBarRemoteInputCallback = remoteInputCallback;
         mActivityIntentHelper = activityIntentHelper;
-        mNotifPipelineFlags = notifPipelineFlags;
         mMetricsLogger = metricsLogger;
         mLogger = logger;
         mOnUserInteractionCallback = onUserInteractionCallback;
@@ -203,21 +180,12 @@
         mActivityLaunchAnimator = activityLaunchAnimator;
         mNotificationAnimationProvider = notificationAnimationProvider;
 
-        if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
-            mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
-                @Override
-                public void onPendingEntryAdded(NotificationEntry entry) {
-                    handleFullScreenIntent(entry);
-                }
-            });
-        } else {
-            mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
-                @Override
-                public void onEntryAdded(NotificationEntry entry) {
-                    handleFullScreenIntent(entry);
-                }
-            });
-        }
+        notifPipeline.addCollectionListener(new NotifCollectionListener() {
+            @Override
+            public void onEntryAdded(NotificationEntry entry) {
+                handleFullScreenIntent(entry);
+            }
+        });
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index bd69cc38..aebf0b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -30,13 +30,9 @@
 import android.util.Slog;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
-import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.widget.MessagingGroup;
-import com.android.internal.widget.MessagingMessage;
-import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceNotificationListener;
 import com.android.systemui.InitController;
@@ -57,7 +53,6 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
@@ -72,16 +67,12 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
 import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
-import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
-import java.util.List;
-
 import javax.inject.Inject;
 
 @CentralSurfacesComponent.CentralSurfacesScope
 class StatusBarNotificationPresenter implements NotificationPresenter,
-        ConfigurationController.ConfigurationListener,
         NotificationRowBinderImpl.BindRowCallback,
         CommandQueue.Callbacks {
     private static final String TAG = "StatusBarNotificationPresenter";
@@ -92,10 +83,8 @@
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final NotifShadeEventSource mNotifShadeEventSource;
-    private final NotificationEntryManager mEntryManager;
     private final NotificationMediaManager mMediaManager;
     private final NotificationGutsManager mGutsManager;
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final LockscreenGestureLogger mLockscreenGestureLogger;
 
     private final NotificationPanelViewController mNotificationPanel;
@@ -116,9 +105,6 @@
     private final IStatusBarService mBarService;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private final NotificationListContainer mNotifListContainer;
-    private boolean mReinflateNotificationsOnUserSwitched;
-    private boolean mDispatchUiModeChangeOnUserSwitched;
-    private TextView mNotificationPanelDebugText;
 
     protected boolean mVrMode;
 
@@ -143,15 +129,12 @@
             NotificationLockscreenUserManager lockscreenUserManager,
             SysuiStatusBarStateController sysuiStatusBarStateController,
             NotifShadeEventSource notifShadeEventSource,
-            NotificationEntryManager notificationEntryManager,
             NotificationMediaManager notificationMediaManager,
             NotificationGutsManager notificationGutsManager,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
             LockscreenGestureLogger lockscreenGestureLogger,
             InitController initController,
             NotificationInterruptStateProvider notificationInterruptStateProvider,
             NotificationRemoteInputManager remoteInputManager,
-            ConfigurationController configurationController,
             NotifPipelineFlags notifPipelineFlags,
             NotificationRemoteInputManager.Callback remoteInputManagerCallback,
             NotificationListContainer notificationListContainer) {
@@ -170,10 +153,8 @@
         mLockscreenUserManager = lockscreenUserManager;
         mStatusBarStateController = sysuiStatusBarStateController;
         mNotifShadeEventSource = notifShadeEventSource;
-        mEntryManager = notificationEntryManager;
         mMediaManager = notificationMediaManager;
         mGutsManager = notificationGutsManager;
-        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockscreenGestureLogger = lockscreenGestureLogger;
         mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
         mNotificationShadeWindowController = notificationShadeWindowController;
@@ -208,11 +189,6 @@
                     mNotifListContainer);
             mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
             mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
-            if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
-                mEntryManager.setUpWithPresenter(this);
-                mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
-                mEntryManager.addNotificationLifetimeExtender(mGutsManager);
-            }
             notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
             mLockscreenUserManager.setUpWithPresenter(this);
             mMediaManager.setUpWithPresenter(this);
@@ -225,7 +201,6 @@
 
             onUserSwitched(mLockscreenUserManager.getCurrentUserId());
         });
-        configurationController.addCallback(this);
     }
 
     /** Called when the shade has been emptied to attempt to close the shade */
@@ -240,65 +215,6 @@
     }
 
     @Override
-    public void onDensityOrFontScaleChanged() {
-        // TODO(b/145659174): Remove legacy pipeline code
-        if (mNotifPipelineFlags.isNewPipelineEnabled()) return;
-        MessagingMessage.dropCache();
-        MessagingGroup.dropCache();
-        if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
-            updateNotificationsOnDensityOrFontScaleChanged();
-        } else {
-            mReinflateNotificationsOnUserSwitched = true;
-        }
-    }
-
-    @Override
-    public void onUiModeChanged() {
-        // TODO(b/145659174): Remove legacy pipeline code
-        if (mNotifPipelineFlags.isNewPipelineEnabled()) return;
-        if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
-            updateNotificationsOnUiModeChanged();
-        } else {
-            mDispatchUiModeChangeOnUserSwitched = true;
-        }
-    }
-
-    @Override
-    public void onThemeChanged() {
-        onDensityOrFontScaleChanged();
-    }
-
-    private void updateNotificationsOnUiModeChanged() {
-        // TODO(b/145659174): Remove legacy pipeline code
-        if (mNotifPipelineFlags.isNewPipelineEnabled()) return;
-        List<NotificationEntry> userNotifications =
-                mEntryManager.getActiveNotificationsForCurrentUser();
-        for (int i = 0; i < userNotifications.size(); i++) {
-            NotificationEntry entry = userNotifications.get(i);
-            ExpandableNotificationRow row = entry.getRow();
-            if (row != null) {
-                row.onUiModeChanged();
-            }
-        }
-    }
-
-    private void updateNotificationsOnDensityOrFontScaleChanged() {
-        // TODO(b/145659174): Remove legacy pipeline code
-        if (mNotifPipelineFlags.isNewPipelineEnabled()) return;
-        List<NotificationEntry> userNotifications =
-                mEntryManager.getActiveNotificationsForCurrentUser();
-        for (int i = 0; i < userNotifications.size(); i++) {
-            NotificationEntry entry = userNotifications.get(i);
-            entry.onDensityOrFontScaleChanged();
-            boolean exposedGuts = entry.areGutsExposed();
-            if (exposedGuts) {
-                mGutsManager.onDensityOrFontScaleChanged(entry);
-            }
-        }
-    }
-
-
-    @Override
     public boolean isCollapsing() {
         return mNotificationPanel.isCollapsing()
                 || mNotificationShadeWindowController.isLaunchingActivity();
@@ -338,17 +254,6 @@
         // End old BaseStatusBar.userSwitched
         if (MULTIUSER_DEBUG) mNotificationPanel.setHeaderDebugInfo("USER " + newUserId);
         mCommandQueue.animateCollapsePanels();
-        if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
-            if (mReinflateNotificationsOnUserSwitched) {
-                updateNotificationsOnDensityOrFontScaleChanged();
-                mReinflateNotificationsOnUserSwitched = false;
-            }
-            if (mDispatchUiModeChangeOnUserSwitched) {
-                updateNotificationsOnUiModeChanged();
-                mDispatchUiModeChangeOnUserSwitched = false;
-            }
-            updateNotificationViews("user switched");
-        }
         mMediaManager.clearCurrentMediaNotification();
         mCentralSurfaces.setLockscreenUser(newUserId);
         updateMediaMetaData(true, false);
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 06532c4..ea4ecd5 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
@@ -42,6 +42,7 @@
 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.KeyguardBottomAreaView;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
@@ -290,4 +291,17 @@
                 secureSettings,
                 mainExecutor);
     }
+
+    /**
+     * Constructs a new, unattached {@link KeyguardBottomAreaView}.
+     *
+     * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
+     */
+    @Provides
+    public static KeyguardBottomAreaView providesKeyguardBottomAreaView(
+            NotificationPanelView npv, LayoutInflater layoutInflater) {
+        return (KeyguardBottomAreaView) layoutInflater.inflate(R
+                .layout.keyguard_bottom_area, npv, false);
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java
index c99ad23..0b5594b 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.tv;
 
-import android.content.Context;
-
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.dagger.GlobalRootComponent;
 
@@ -27,9 +25,7 @@
  */
 public class TvSystemUIFactory extends SystemUIFactory {
     @Override
-    protected GlobalRootComponent buildGlobalRootComponent(Context context) {
-        return DaggerTvGlobalRootComponent.builder()
-                .context(context)
-                .build();
+    protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() {
+        return DaggerTvGlobalRootComponent.builder();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 36a49af..f1e89ac 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -46,6 +46,7 @@
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.screenshot.ReferenceScreenshotModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -91,6 +92,7 @@
             GestureModule.class,
             PowerModule.class,
             QSModule.class,
+            ReferenceScreenshotModule.class,
             VolumeModule.class,
         },
         subcomponents = {
diff --git a/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt b/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt
new file mode 100644
index 0000000..f53b682
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.app.ActivityThread
+import android.os.Process
+import com.android.systemui.dagger.qualifiers.InstrumentationTest
+import javax.inject.Inject
+
+/**
+ * Used to check whether SystemUI should be fully initialized.
+ */
+class InitializationChecker @Inject constructor(
+    @InstrumentationTest private val instrumentationTest: Boolean
+) {
+
+    /**
+     * Only initialize components for the main system ui process running as the primary user
+     */
+    fun initializeComponents(): Boolean =
+        !instrumentationTest &&
+                Process.myUserHandle().isSystem &&
+                ActivityThread.currentProcessName() == ActivityThread.currentPackageName()
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
index ac1a83c..4021652 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -31,6 +32,7 @@
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
 import android.view.Gravity;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
@@ -42,6 +44,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -104,6 +107,17 @@
     }
 
     @Test
+    public void onBouncerVisible_propagatesToKeyguardSecurityContainerController() {
+        mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.VISIBLE);
+        mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.INVISIBLE);
+
+        InOrder order = inOrder(mKeyguardSecurityContainerController);
+        order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged(View.VISIBLE);
+        order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged(
+                View.INVISIBLE);
+    }
+
+    @Test
     public void testGravityReappliedOnConfigurationChange() {
         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index bc35142..68e49c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -25,17 +25,21 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.hardware.biometrics.BiometricSourceType;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.WindowInsetsController;
 
 import androidx.test.filters.SmallTest;
@@ -46,6 +50,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.SidefpsController;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.log.SessionTracker;
@@ -59,10 +64,14 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.Optional;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper()
@@ -124,6 +133,14 @@
     private SessionTracker mSessionTracker;
     @Mock
     private KeyguardViewController mKeyguardViewController;
+    @Mock
+    private SidefpsController mSidefpsController;
+    @Mock
+    private KeyguardPasswordViewController mKeyguardPasswordViewControllerMock;
+
+    @Captor
+    private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
+
     private Configuration mConfiguration;
 
     private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -160,7 +177,7 @@
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
                 mConfigurationController, mFalsingCollector, mFalsingManager,
                 mUserSwitcherController, mFeatureFlags, mGlobalSettings,
-                mSessionTracker).create(mSecurityCallback);
+                mSessionTracker, Optional.of(mSidefpsController)).create(mSecurityCallback);
     }
 
     @Test
@@ -258,9 +275,7 @@
     @Test
     public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() {
         when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true);
-        when(mKeyguardSecurityViewFlipperController.getSecurityView(
-                eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
-                .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+        setupGetSecurityView();
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
         verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
@@ -276,4 +291,126 @@
         verify(mUserSwitcherController)
                 .removeUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class));
     }
+
+    @Test
+    public void onBouncerVisibilityChanged_allConditionsGood_sideFpsHintShown() {
+        setupConditionsToEnableSideFpsHint();
+        reset(mSidefpsController);
+
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+
+        verify(mSidefpsController).show();
+        verify(mSidefpsController, never()).hide();
+    }
+
+    @Test
+    public void onBouncerVisibilityChanged_fpsSensorNotRunning_sideFpsHintHidden() {
+        setupConditionsToEnableSideFpsHint();
+        setFingerprintDetectionRunning(false);
+        reset(mSidefpsController);
+
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+
+        verify(mSidefpsController).hide();
+        verify(mSidefpsController, never()).show();
+    }
+
+    @Test
+    public void onBouncerVisibilityChanged_withoutSidedSecurity_sideFpsHintHidden() {
+        setupConditionsToEnableSideFpsHint();
+        setSidedSecurityMode(false);
+        reset(mSidefpsController);
+
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+
+        verify(mSidefpsController).hide();
+        verify(mSidefpsController, never()).show();
+    }
+
+    @Test
+    public void onBouncerVisibilityChanged_needsStrongAuth_sideFpsHintHidden() {
+        setupConditionsToEnableSideFpsHint();
+        setNeedsStrongAuth(true);
+        reset(mSidefpsController);
+
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+
+        verify(mSidefpsController).hide();
+        verify(mSidefpsController, never()).show();
+    }
+
+    @Test
+    public void onBouncerVisibilityChanged_sideFpsHintShown_sideFpsHintHidden() {
+        setupGetSecurityView();
+        setupConditionsToEnableSideFpsHint();
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+        verify(mSidefpsController, atLeastOnce()).show();
+        reset(mSidefpsController);
+
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE);
+
+        verify(mSidefpsController).hide();
+        verify(mSidefpsController, never()).show();
+    }
+
+    @Test
+    public void onStartingToHide_sideFpsHintShown_sideFpsHintHidden() {
+        setupGetSecurityView();
+        setupConditionsToEnableSideFpsHint();
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+        verify(mSidefpsController, atLeastOnce()).show();
+        reset(mSidefpsController);
+
+        mKeyguardSecurityContainerController.onStartingToHide();
+
+        verify(mSidefpsController).hide();
+        verify(mSidefpsController, never()).show();
+    }
+
+    @Test
+    public void onPause_sideFpsHintShown_sideFpsHintHidden() {
+        setupGetSecurityView();
+        setupConditionsToEnableSideFpsHint();
+        mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+        verify(mSidefpsController, atLeastOnce()).show();
+        reset(mSidefpsController);
+
+        mKeyguardSecurityContainerController.onPause();
+
+        verify(mSidefpsController).hide();
+        verify(mSidefpsController, never()).show();
+    }
+
+    private void setupConditionsToEnableSideFpsHint() {
+        attachView();
+        setSidedSecurityMode(true);
+        setFingerprintDetectionRunning(true);
+        setNeedsStrongAuth(false);
+    }
+
+    private void attachView() {
+        mKeyguardSecurityContainerController.onViewAttached();
+        verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture());
+    }
+
+    private void setFingerprintDetectionRunning(boolean running) {
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(running);
+        mKeyguardUpdateMonitorCallback.getValue().onBiometricRunningStateChanged(running,
+                BiometricSourceType.FINGERPRINT);
+    }
+
+    private void setSidedSecurityMode(boolean sided) {
+        when(mView.isSidedSecurityMode()).thenReturn(sided);
+    }
+
+    private void setNeedsStrongAuth(boolean needed) {
+        when(mKeyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(needed);
+        mKeyguardUpdateMonitorCallback.getValue().onStrongAuthStateChanged(/* userId= */ 0);
+    }
+
+    private void setupGetSecurityView() {
+        when(mKeyguardSecurityViewFlipperController.getSecurityView(
+                any(), any(KeyguardSecurityCallback.class)))
+                .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewControllerMock);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index b61fbbe..273786d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -207,6 +207,25 @@
     }
 
     @Test
+    fun animatesInvisibleViews() {
+        rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
+        rootView.visibility = View.INVISIBLE
+
+        val success = ViewHierarchyAnimator.animate(rootView)
+        // Change all bounds.
+        rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
+
+        assertTrue(success)
+        assertNotNull(rootView.getTag(R.id.tag_animator))
+        // The initial values should be those of the previous layout.
+        checkBounds(rootView, l = 10, t = 10, r = 50, b = 50)
+        endAnimation(rootView)
+        assertNull(rootView.getTag(R.id.tag_animator))
+        // The end values should be those of the latest layout.
+        checkBounds(rootView, l = 0, t = 15, r = 70, b = 80)
+    }
+
+    @Test
     fun animatesAppearingViewsFromStartToEnd() {
         // Starting GONE.
         rootView.visibility = View.GONE
@@ -222,20 +241,6 @@
         assertNull(rootView.getTag(R.id.tag_animator))
         checkBounds(rootView, l = 0, t = 100, r = 100, b = 200)
 
-        // Starting INVISIBLE.
-        rootView.visibility = View.INVISIBLE
-        rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
-        success = ViewHierarchyAnimator.animateAddition(rootView)
-        rootView.visibility = View.VISIBLE
-        rootView.layout(0 /* l */, 100 /* t */, 100 /* r */, 200 /* b */)
-
-        assertTrue(success)
-        assertNotNull(rootView.getTag(R.id.tag_animator))
-        checkBounds(rootView, l = 50, t = 150, r = 50, b = 150)
-        endAnimation(rootView)
-        assertNull(rootView.getTag(R.id.tag_animator))
-        checkBounds(rootView, l = 0, t = 100, r = 100, b = 200)
-
         // Starting with nothing.
         rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
         success = ViewHierarchyAnimator.animateAddition(rootView)
@@ -937,7 +942,7 @@
     }
 
     @Test
-    fun doesNotAnimateInvisibleViews() {
+    fun doesNotAnimateGoneViews() {
         rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
 
         // GONE
@@ -948,15 +953,6 @@
         assertFalse(success)
         assertNull(rootView.getTag(R.id.tag_animator))
         checkBounds(rootView, l = 0, t = 15, r = 55, b = 80)
-
-        // INVISIBLE.
-        rootView.visibility = View.INVISIBLE
-        success = ViewHierarchyAnimator.animate(rootView)
-        rootView.layout(0 /* l */, 20 /* t */, 10 /* r */, 50 /* b */)
-
-        assertFalse(success)
-        assertNull(rootView.getTag(R.id.tag_animator))
-        checkBounds(rootView, l = 0, t = 20, r = 10, b = 50)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS
new file mode 100644
index 0000000..adb10f0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS
@@ -0,0 +1,4 @@
+set noparent
+
+include /services/core/java/com/android/server/biometrics/OWNERS
+beverlyt@google.com
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index dec2b82..6157ccb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -62,7 +62,6 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyFloat
 import org.mockito.Mockito.anyInt
@@ -72,6 +71,7 @@
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenEver
 import org.mockito.junit.MockitoJUnit
 
 private const val DISPLAY_ID = 2
@@ -126,15 +126,15 @@
         context.addMockSystemService(DisplayManager::class.java, displayManager)
         context.addMockSystemService(WindowManager::class.java, windowManager)
 
-        `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
-        `when`(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
+        whenEver(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
+        whenEver(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
             .thenReturn(mock(LottieAnimationView::class.java))
         with(mock(ViewPropertyAnimator::class.java)) {
-            `when`(sidefpsView.animate()).thenReturn(this)
-            `when`(alpha(anyFloat())).thenReturn(this)
-            `when`(setStartDelay(anyLong())).thenReturn(this)
-            `when`(setDuration(anyLong())).thenReturn(this)
-            `when`(setListener(any())).thenAnswer {
+            whenEver(sidefpsView.animate()).thenReturn(this)
+            whenEver(alpha(anyFloat())).thenReturn(this)
+            whenEver(setStartDelay(anyLong())).thenReturn(this)
+            whenEver(setDuration(anyLong())).thenReturn(this)
+            whenEver(setListener(any())).thenAnswer {
                 (it.arguments[0] as Animator.AnimatorListener)
                     .onAnimationEnd(mock(Animator::class.java))
                 this
@@ -177,7 +177,7 @@
         displayBounds = Rect(0, 0, displayWidth, displayHeight)
         var locations = listOf(sensorLocation)
 
-        `when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
+        whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn(
             listOf(
                 FingerprintSensorPropertiesInternal(
                     SENSOR_ID,
@@ -196,12 +196,12 @@
         displayInfo.initInfo()
         val dmGlobal = mock(DisplayManagerGlobal::class.java)
         val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
-        `when`(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
-        `when`(windowManager.defaultDisplay).thenReturn(display)
-        `when`(windowManager.maximumWindowMetrics).thenReturn(
+        whenEver(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
+        whenEver(windowManager.defaultDisplay).thenReturn(display)
+        whenEver(windowManager.maximumWindowMetrics).thenReturn(
                 WindowMetrics(displayBounds, WindowInsets.CONSUMED)
         )
-        `when`(windowManager.currentWindowMetrics).thenReturn(
+        whenEver(windowManager.currentWindowMetrics).thenReturn(
             WindowMetrics(displayBounds, windowInsets)
         )
 
@@ -277,13 +277,13 @@
 
     @Test
     fun testShowsForMostSettings() = testWithDisplay {
-        `when`(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask()))
+        whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask()))
         testIgnoredFor(REASON_AUTH_SETTINGS, ignored = false)
     }
 
     @Test
     fun testIgnoredForVerySpecificSettings() = testWithDisplay {
-        `when`(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask()))
+        whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask()))
         testIgnoredFor(REASON_AUTH_SETTINGS)
     }
 
@@ -424,6 +424,20 @@
         assertThat(overlayViewParamsCaptor.value.x).isEqualTo(displayWidth - boundsWidth)
         assertThat(overlayViewParamsCaptor.value.y).isEqualTo(sensorLocation.sensorLocationY)
     }
+
+    @Test
+    fun hasSideFpsSensor_withSensorProps_returnsTrue() = testWithDisplay {
+        // By default all those tests assume the side fps sensor is available.
+
+        assertThat(fingerprintManager.hasSideFpsSensor()).isTrue()
+    }
+
+    @Test
+    fun hasSideFpsSensor_withoutSensorProps_returnsFalse() {
+        whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn(null)
+
+        assertThat(fingerprintManager.hasSideFpsSensor()).isFalse()
+    }
 }
 
 private fun insetsForSmallNavbar() = insetsWithBottom(60)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
index 7ab4958..e1eda11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -32,7 +32,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.testing.AndroidTestingRunner;
@@ -115,7 +114,7 @@
         actionProxyReceiver.onReceive(mContext, mIntent);
 
         verify(mMockScreenshotSmartActions, never())
-                .notifyScreenshotAction(any(Context.class), anyString(), anyString(), anyBoolean(),
+                .notifyScreenshotAction(anyString(), anyString(), anyBoolean(),
                         any(Intent.class));
     }
 
@@ -129,7 +128,7 @@
         actionProxyReceiver.onReceive(mContext, mIntent);
 
         verify(mMockScreenshotSmartActions).notifyScreenshotAction(
-                mContext, testId, ACTION_TYPE_SHARE, false, null);
+                testId, ACTION_TYPE_SHARE, false, null);
     }
 
     private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
index 664c125..d58f47a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
@@ -31,7 +31,6 @@
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
-import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
 import android.net.Uri;
@@ -81,7 +80,7 @@
 
         verify(mMockExecutor, never()).execute(any(Runnable.class));
         verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
-                any(Context.class), any(String.class), any(String.class), anyBoolean(),
+                any(String.class), any(String.class), anyBoolean(),
                 any(Intent.class));
     }
 
@@ -113,7 +112,7 @@
         }
 
         // ensure smart actions not called by default
-        verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(any(Context.class),
+        verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
                 any(String.class), any(String.class), anyBoolean(), any(Intent.class));
     }
 
@@ -129,7 +128,7 @@
         mDeleteScreenshotReceiver.onReceive(mContext, intent);
 
         verify(mMockExecutor).execute(any(Runnable.class));
-        verify(mMockScreenshotSmartActions).notifyScreenshotAction(mContext, testId,
+        verify(mMockScreenshotSmartActions).notifyScreenshotAction(testId,
                 ACTION_TYPE_DELETE, false, null);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 3d658ec..69b7b88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -43,7 +43,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
 
@@ -71,7 +70,7 @@
     public void setup() {
         mSmartActionsProvider = mock(
                 ScreenshotNotificationSmartActionsProvider.class);
-        mScreenshotSmartActions = new ScreenshotSmartActions();
+        mScreenshotSmartActions = new ScreenshotSmartActions(() -> mSmartActionsProvider);
         mHandler = mock(Handler.class);
     }
 
@@ -158,8 +157,7 @@
         Bitmap bitmap = mock(Bitmap.class);
         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
         ScreenshotNotificationSmartActionsProvider actionsProvider =
-                SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
-                        mContext, null, mHandler);
+                new ScreenshotNotificationSmartActionsProvider();
         CompletableFuture<List<Notification.Action>> smartActionsFuture =
                 mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
                         actionsProvider, REGULAR_SMART_ACTIONS,
@@ -183,7 +181,7 @@
         data.mActionsReadyListener = null;
         SaveImageInBackgroundTask task =
                 new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
-                        ActionTransition::new);
+                        ActionTransition::new, mSmartActionsProvider);
 
         Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
                 Uri.parse("Screenshot_123.png")).get().action;
@@ -211,7 +209,7 @@
         data.mActionsReadyListener = null;
         SaveImageInBackgroundTask task =
                 new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
-                        ActionTransition::new);
+                        ActionTransition::new, mSmartActionsProvider);
 
         Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
                 Uri.parse("Screenshot_123.png")).get().action;
@@ -239,7 +237,7 @@
         data.mActionsReadyListener = null;
         SaveImageInBackgroundTask task =
                 new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
-                        ActionTransition::new);
+                        ActionTransition::new, mSmartActionsProvider);
 
         Notification.Action deleteAction = task.createDeleteAction(mContext,
                 mContext.getResources(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
index 011e6b7..83c9497 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -74,6 +74,6 @@
         verify(mMockPendingIntent).send(
                 eq(mContext), eq(0), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
         verify(mMockScreenshotSmartActions).notifyScreenshotAction(
-                mContext, testId, testActionType, true, intent);
+                testId, testActionType, true, intent);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
new file mode 100644
index 0000000..73226fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.IntentFilter
+import android.os.Environment
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UserFileManagerImplTest : SysuiTestCase() {
+    companion object {
+        const val TEST_FILE_NAME = "abc.txt"
+    }
+
+    lateinit var userFileManager: UserFileManagerImpl
+    lateinit var backgroundExecutor: FakeExecutor
+    @Mock
+    lateinit var userManager: UserManager
+    @Mock
+    lateinit var broadcastDispatcher: BroadcastDispatcher
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        backgroundExecutor = FakeExecutor(FakeSystemClock())
+        userFileManager = UserFileManagerImpl(context, userManager,
+            broadcastDispatcher, backgroundExecutor)
+    }
+
+    @Test
+    fun testGetFile() {
+        assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path)
+            .isEqualTo("${context.filesDir}/$TEST_FILE_NAME")
+        assertThat(userFileManager.getFile(TEST_FILE_NAME, 11).path)
+            .isEqualTo("${context.filesDir}/${UserFileManagerImpl.ID}/11/files/$TEST_FILE_NAME")
+    }
+
+    @Test
+    fun testGetSharedPreferences() {
+        assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0))
+            .isNotEqualTo(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11))
+    }
+
+    @Test
+    fun testUserFileManagerStart() {
+        val userFileManager = spy(userFileManager)
+        userFileManager.start()
+        verify(userFileManager).clearDeletedUserData()
+        verify(broadcastDispatcher).registerReceiver(any(BroadcastReceiver::class.java),
+            any(IntentFilter::class.java),
+            any(Executor::class.java), isNull(), eq(Context.RECEIVER_EXPORTED), isNull())
+    }
+
+    @Test
+    fun testClearDeletedUserData() {
+        val dir = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID,
+            "11",
+            "files"
+        )
+        dir.mkdirs()
+        val file = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID,
+            "11",
+            "files",
+            TEST_FILE_NAME
+        )
+        val secondaryUserDir = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID,
+            "11",
+        )
+        file.createNewFile()
+        assertThat(secondaryUserDir.exists()).isTrue()
+        assertThat(file.exists()).isTrue()
+        userFileManager.clearDeletedUserData()
+        assertThat(backgroundExecutor.runAllReady()).isGreaterThan(0)
+        verify(userManager).aliveUsers
+        assertThat(secondaryUserDir.exists()).isFalse()
+        assertThat(file.exists()).isFalse()
+        dir.deleteRecursively()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index d67e26f..9c25462 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -30,6 +30,7 @@
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED;
+import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -212,7 +213,7 @@
                 R.string.do_financed_disclosure_with_name, ORGANIZATION_NAME);
 
         when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
-        when(mScreenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_ON);
+        when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON);
         when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
 
         when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom))
@@ -954,64 +955,170 @@
     }
 
     @Test
-    public void nonBypassFaceSuccess_touchExplorationEnabled_showsSwipeToOpen() {
-        // GIVEN non bypass face auth and touch exploration is enabled
-        when(mKeyguardBypassController.canBypass()).thenReturn(false);
-        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
-        createController();
-        String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
-        mController.setVisible(true);
-
-        // WHEN face authenticated
-        mController.getKeyguardCallback().onBiometricAuthenticated(0,
-                BiometricSourceType.FACE, false);
-
-        // THEN show 'swipe up to open' message
-        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
-    }
-
-    @Test
-    public void nonBypassFaceSuccess_a11yEnabled_showsSwipeToOpen() {
-        // GIVEN non bypass face auth and a11y is enabled
-        when(mKeyguardBypassController.canBypass()).thenReturn(false);
-        when(mAccessibilityManager.isEnabled()).thenReturn(true);
-        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
-        createController();
-        String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
-        mController.setVisible(true);
-
-        // WHEN face auth is successful
-        mController.getKeyguardCallback().onBiometricAuthenticated(0,
-                BiometricSourceType.FACE, false);
-
-        // THEN show 'swipe up to open' message
-        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
-    }
-
-    @Test
-    public void coEx_nonBypassFaceSuccess_showsPressLockIcon() {
-        // GIVEN udfps is supported, non-bypass face auth, and no a11y enabled
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
-        when(mKeyguardBypassController.canBypass()).thenReturn(false);
-        when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
-        when(mAccessibilityManager.isEnabled()).thenReturn(false);
-        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+    public void coEx_faceSuccess_showsPressToOpen() {
+        // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, no a11y enabled
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
         when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
                 .thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+        when(mAccessibilityManager.isEnabled()).thenReturn(false);
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
         createController();
         mController.setVisible(true);
 
         // WHEN face auth succeeds
+        when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
         mController.getKeyguardCallback().onBiometricAuthenticated(0,
                 BiometricSourceType.FACE, false);
 
-        // THEN press unlock icon to open message shows
-        String pressLockIcon = mContext.getString(R.string.keyguard_face_successful_unlock_press);
-        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressLockIcon);
+        // THEN 'face unlocked. press unlock icon to open' message shows
+        String pressToOpen = mContext.getString(R.string.keyguard_face_successful_unlock_press);
+        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressToOpen);
 
-        assertThat(mTextView.getText()).isNotEqualTo(pressLockIcon);
+        assertThat(mTextView.getText()).isNotEqualTo(pressToOpen);
+    }
+
+
+    @Test
+    public void coEx_faceSuccess_touchExplorationEnabled_showsFaceUnlockedSwipeToOpen() {
+        // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y enabled
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+        when(mAccessibilityManager.isEnabled()).thenReturn(true);
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+        createController();
+        mController.setVisible(true);
+
+        // WHEN face authenticated
+        when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+        mController.getKeyguardCallback().onBiometricAuthenticated(0,
+                BiometricSourceType.FACE, false);
+
+        // THEN show 'face unlocked. swipe up to open' message
+        String faceUnlockedSwipeToOpen =
+                mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
+        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
+    }
+
+    @Test
+    public void coEx_faceSuccess_a11yEnabled_showsFaceUnlockedSwipeToOpen() {
+        // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y is enabled
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+        when(mAccessibilityManager.isEnabled()).thenReturn(true);
+        createController();
+        mController.setVisible(true);
+
+        // WHEN face auth is successful
+        when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+        mController.getKeyguardCallback().onBiometricAuthenticated(0,
+                BiometricSourceType.FACE, false);
+
+        // THEN show 'swipe up to open' message
+        String faceUnlockedSwipeToOpen =
+                mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
+        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
+    }
+
+    @Test
+    public void faceOnly_faceSuccess_showsFaceUnlockedSwipeToOpen() {
+        // GIVEN bouncer isn't showing, can skip bouncer, no udfps supported
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
+        createController();
+        mController.setVisible(true);
+
+        // WHEN face auth is successful
+        when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+        mController.getKeyguardCallback().onBiometricAuthenticated(0,
+                BiometricSourceType.FACE, false);
+
+        // THEN show 'swipe up to open' message
+        String faceUnlockedSwipeToOpen =
+                mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
+        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
+    }
+
+    @Test
+    public void udfpsOnly_a11yEnabled_showsSwipeToOpen() {
+        // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y is enabled
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+        when(mAccessibilityManager.isEnabled()).thenReturn(true);
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+        createController();
+        mController.setVisible(true);
+
+        // WHEN showActionToUnlock
+        mController.showActionToUnlock();
+
+        // THEN show 'swipe up to open' message
+        String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
+        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
+    }
+
+    @Test
+    public void udfpsOnly_showsPressToOpen() {
+        // GIVEN bouncer isn't showing, udfps is supported, a11y is NOT enabled, can skip bouncer
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+        when(mAccessibilityManager.isEnabled()).thenReturn(false);
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+        createController();
+        mController.setVisible(true);
+
+        // WHEN showActionToUnlock
+        mController.showActionToUnlock();
+
+        // THEN show 'press unlock icon to open' message
+        String pressToOpen = mContext.getString(R.string.keyguard_unlock_press);
+        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressToOpen);
+    }
+
+    @Test
+    public void canSkipBouncer_noSecurity_showSwipeToUnlockHint() {
+        // GIVEN bouncer isn't showing, can skip bouncer, no security (udfps isn't supported,
+        // face wasn't authenticated)
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
+        createController();
+        mController.setVisible(true);
+
+        // WHEN showActionToUnlock
+        mController.showActionToUnlock();
+
+        // THEN show 'swipe up to open' message
+        String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
+        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
+    }
+
+    @Test
+    public void cannotSkipBouncer_showSwipeToUnlockHint() {
+        // GIVEN bouncer isn't showing and cannot skip bouncer
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(false);
+        createController();
+        mController.setVisible(true);
+
+        // WHEN showActionToUnlock
+        mController.showActionToUnlock();
+
+        // THEN show 'swipe up to open' message
+        String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
+        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
     }
 
     private void sendUpdateDisclosureBroadcast() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 4efd5c9..f42e6fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -150,7 +150,6 @@
         MockitoAnnotations.initMocks(this);
 
         when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
-        when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
 
         mController = new NotificationStackScrollLayoutController(
                 true,
@@ -166,7 +165,6 @@
                 mKeyguardMediaController,
                 mKeyguardBypassController,
                 mZenModeController,
-                mColorExtractor,
                 mNotificationLockscreenUserManager,
                 mMetricsLogger,
                 mDumpManager,
@@ -179,15 +177,12 @@
                 mLegacyGroupManager,
                 mLegacyGroupManager,
                 mSilentHeaderController,
-                mNotifPipelineFlags,
                 mNotifPipeline,
                 mNotifCollection,
                 mEntryManager,
                 mLockscreenShadeTransitionController,
                 mShadeTransitionController,
-                mIStatusBarService,
                 mUiEventLogger,
-                mLayoutInflater,
                 mRemoteInputManager,
                 mVisualStabilityManager,
                 mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 39021d8..60a3d95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -230,6 +230,15 @@
     }
 
     @Test
+    public void show_notifiesKeyguardViewController() {
+        mBouncer.ensureView();
+
+        mBouncer.show(/* resetSecuritySelection= */ false);
+
+        verify(mKeyguardHostViewController).onBouncerVisibilityChanged(View.VISIBLE);
+    }
+
+    @Test
     public void testHide_notifiesFalsingManager() {
         mBouncer.hide(false);
         verify(mFalsingCollector).onBouncerHidden();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index d558759..98cd080 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -173,6 +173,8 @@
     @Mock
     private KeyguardBottomAreaView mKeyguardBottomArea;
     @Mock
+    private KeyguardBottomAreaViewController mKeyguardBottomAreaViewController;
+    @Mock
     private KeyguardBottomAreaView mQsFrame;
     private KeyguardStatusView mKeyguardStatusView;
     @Mock
@@ -400,6 +402,7 @@
         when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
         when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
                 .thenReturn(mHeadsUpCallback);
+        when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea);
         when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
         when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
         when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
@@ -482,7 +485,8 @@
         mMainHandler = new Handler(Looper.getMainLooper());
         mPanelEventsEmitter = new NotificationPanelViewController.PanelEventsEmitter();
 
-        mNotificationPanelViewController = new NotificationPanelViewController(mView,
+        mNotificationPanelViewController = new NotificationPanelViewController(
+                mView,
                 mMainHandler,
                 mLayoutInflater,
                 mFeatureFlags,
@@ -533,6 +537,7 @@
                 mInteractionJankMonitor,
                 mQsFrameTranslateController,
                 mSysUiState,
+                () -> mKeyguardBottomAreaViewController,
                 mKeyguardUnlockAnimationController,
                 mNotificationListContainer,
                 mPanelEventsEmitter,
@@ -986,6 +991,21 @@
     }
 
     @Test
+    public void testSwipe_exactlyToTarget_notifiesNssl() {
+        // No over-expansion
+        mNotificationPanelViewController.setOverExpansion(0f);
+        // Fling to a target that is equal to the current position (i.e. a no-op fling).
+        mNotificationPanelViewController.flingToHeight(
+                0f,
+                true,
+                mNotificationPanelViewController.mExpandedHeight,
+                1f,
+                false);
+        // Verify that the NSSL is notified that the panel is *not* flinging.
+        verify(mNotificationStackScrollLayoutController).setPanelFlinging(false);
+    }
+
+    @Test
     public void testDoubleTapRequired_Keyguard() {
         FalsingManager.FalsingTapListener listener = getFalsingTapListener();
         mStatusBarStateController.setState(KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 0c1d042..a6b0bc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -389,6 +389,16 @@
     }
 
     @Test
+    public void testBouncerIsOrWillBeShowing_whenBouncerIsInTransit() {
+        when(mBouncer.isShowing()).thenReturn(false);
+        when(mBouncer.inTransit()).thenReturn(true);
+
+        assertTrue(
+                "Is or will be showing should be true when bouncer is in transit",
+                mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing());
+    }
+
+    @Test
     public void testShowAltAuth_unlockingWithBiometricNotAllowed() {
         // GIVEN alt auth exists, unlocking with biometric isn't allowed
         mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
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 ecea14c..f53f5fb 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
@@ -59,18 +59,15 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationClickNotifier;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -127,8 +124,6 @@
     @Mock
     private ShadeControllerImpl mShadeController;
     @Mock
-    private NotifPipelineFlags mNotifPipelineFlags;
-    @Mock
     private NotifPipeline mNotifPipeline;
     @Mock
     private NotificationVisibilityProvider mVisibilityProvider;
@@ -148,15 +143,13 @@
     private ActivityLaunchAnimator mActivityLaunchAnimator;
     @Mock
     private InteractionJankMonitor mJankMonitor;
-    private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
-    private NotificationTestHelper mNotificationTestHelper;
+    private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
     private ExpandableNotificationRow mNotificationRow;
     private ExpandableNotificationRow mBubbleNotificationRow;
 
     private final Answer<Void> mCallOnDismiss = answerVoid(
             (OnDismissAction dismissAction, Runnable cancel,
                     Boolean afterKeyguardGone) -> dismissAction.onDismiss());
-    private ArrayList<NotificationEntry> mActiveNotifications;
 
     @Before
     public void setUp() throws Exception {
@@ -165,29 +158,28 @@
         when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
         when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
 
-        mNotificationTestHelper = new NotificationTestHelper(
+        NotificationTestHelper notificationTestHelper = new NotificationTestHelper(
                 mContext,
                 mDependency,
                 TestableLooper.get(this));
 
         // Create standard notification with contentIntent
-        mNotificationRow = mNotificationTestHelper.createRow();
+        mNotificationRow = notificationTestHelper.createRow();
         StatusBarNotification sbn = mNotificationRow.getEntry().getSbn();
         sbn.getNotification().contentIntent = mContentIntent;
         sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
 
         // Create bubble notification row with contentIntent
-        mBubbleNotificationRow = mNotificationTestHelper.createBubble();
+        mBubbleNotificationRow = notificationTestHelper.createBubble();
         StatusBarNotification bubbleSbn = mBubbleNotificationRow.getEntry().getSbn();
         bubbleSbn.getNotification().contentIntent = mContentIntent;
         bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
 
-        mActiveNotifications = new ArrayList<>();
-        mActiveNotifications.add(mNotificationRow.getEntry());
-        mActiveNotifications.add(mBubbleNotificationRow.getEntry());
-        when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications);
+        ArrayList<NotificationEntry> activeNotifications = new ArrayList<>();
+        activeNotifications.add(mNotificationRow.getEntry());
+        activeNotifications.add(mBubbleNotificationRow.getEntry());
+        when(mEntryManager.getVisibleNotifications()).thenReturn(activeNotifications);
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-        when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
         when(mOnUserInteractionCallback.registerFutureDismissal(eq(mNotificationRow.getEntry()),
                 anyInt())).thenReturn(mFutureDismissalRunnable);
         when(mVisibilityProvider.obtain(anyString(), anyBoolean()))
@@ -207,23 +199,19 @@
         mNotificationActivityStarter =
                 new StatusBarNotificationActivityStarter(
                         getContext(),
-                        mock(CommandQueue.class),
                         mHandler,
                         mUiBgExecutor,
-                        mEntryManager,
                         mNotifPipeline,
                         mVisibilityProvider,
                         headsUpManager,
                         mActivityStarter,
                         mClickNotifier,
-                        mock(StatusBarStateController.class),
                         mStatusBarKeyguardViewManager,
                         mock(KeyguardManager.class),
                         mock(IDreamManager.class),
                         Optional.of(mBubblesManager),
                         () -> mAssistManager,
                         mRemoteInputManager,
-                        mock(NotificationGroupManagerLegacy.class),
                         mock(NotificationLockscreenUserManager.class),
                         mShadeController,
                         mKeyguardStateController,
@@ -231,7 +219,6 @@
                         mock(LockPatternUtils.class),
                         mock(StatusBarRemoteInputCallback.class),
                         mActivityIntentHelper,
-                        mNotifPipelineFlags,
                         mock(MetricsLogger.class),
                         mock(StatusBarNotificationActivityStarterLogger.class),
                         mOnUserInteractionCallback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 1a3dd3a..0bfd1ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -33,7 +33,6 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.FakeMetricsLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.ForegroundServiceNotificationListener;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
@@ -50,7 +49,6 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
@@ -61,7 +59,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
@@ -125,15 +122,12 @@
                 mock(NotificationLockscreenUserManager.class),
                 mock(SysuiStatusBarStateController.class),
                 mock(NotifShadeEventSource.class),
-                mock(NotificationEntryManager.class),
                 mock(NotificationMediaManager.class),
                 mock(NotificationGutsManager.class),
-                mock(KeyguardUpdateMonitor.class),
                 lockscreenGestureLogger,
                 mInitController,
                 mNotificationInterruptStateProvider,
                 mock(NotificationRemoteInputManager.class),
-                mock(ConfigurationController.class),
                 mock(NotifPipelineFlags.class),
                 mock(NotificationRemoteInputManager.Callback.class),
                 mock(NotificationListContainer.class));
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8fe57e18..bd1a2a6 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -123,6 +123,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.IRemoteViewsFactory;
 import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
 import com.android.server.WidgetBackupProvider;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -266,7 +267,10 @@
         mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mSaveStateHandler = BackgroundThread.getHandler();
-        mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
+        final ServiceThread serviceThread = new ServiceThread(TAG,
+                android.os.Process.THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
+        serviceThread.start();
+        mCallbackHandler = new CallbackHandler(serviceThread.getLooper());
         mBackupRestoreController = new BackupRestoreController();
         mSecurityPolicy = new SecurityPolicy();
         mIsProviderInfoPersisted = !ActivityManager.isLowRamDeviceStatic()
@@ -307,26 +311,26 @@
         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         packageFilter.addDataScheme("package");
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                packageFilter, null, null);
+                packageFilter, null, mCallbackHandler);
 
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                sdFilter, null, null);
+                sdFilter, null, mCallbackHandler);
 
         IntentFilter offModeFilter = new IntentFilter();
         offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
         offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                offModeFilter, null, null);
+                offModeFilter, null, mCallbackHandler);
 
         IntentFilter suspendPackageFilter = new IntentFilter();
         suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
         suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                suspendPackageFilter, null, null);
+                suspendPackageFilter, null, mCallbackHandler);
     }
 
     private void registerOnCrossProfileProvidersChangedListener() {
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
index a2b2059..6b23587 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -30,6 +30,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PackageInfoFlags;
@@ -39,8 +40,6 @@
 import android.os.Binder;
 import android.util.Slog;
 
-import com.android.internal.util.ArrayUtils;
-
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -65,15 +64,18 @@
 
     static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
             @UserIdInt int userId, @NonNull String packageName) {
-        final boolean requested = ArrayUtils.contains(
-                getPackageInfo(context, userId, packageName).reqFeatures,
-                FEATURE_COMPANION_DEVICE_SETUP);
+        String requiredFeature = FEATURE_COMPANION_DEVICE_SETUP;
 
-        if (requested) {
-            throw new IllegalStateException("Must declare uses-feature "
-                    + FEATURE_COMPANION_DEVICE_SETUP
-                    + " in manifest to use this API");
+        FeatureInfo[] requestedFeatures = getPackageInfo(context, userId, packageName).reqFeatures;
+        for (int i = 0; i < requestedFeatures.length; i++) {
+            if (requiredFeature.equals(requestedFeatures[i].name)) {
+                return;
+            }
         }
+
+        throw new IllegalStateException("Must declare uses-feature "
+                + requiredFeature
+                + " in manifest to use this API");
     }
 
     /**
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index c678a67..e1a0bfd 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -160,6 +160,7 @@
     public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
             "android.hardware.biometrics.face.IFace/",
             "android.hardware.biometrics.fingerprint.IFingerprint/",
+            "android.hardware.input.processor.IInputProcessor/",
             "android.hardware.light.ILights/",
             "android.hardware.power.IPower/",
             "android.hardware.power.stats.IPowerStats/",
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 92a8dcd..98e3a21 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2269,7 +2269,9 @@
                 final boolean inBgRestricted = ast.isAppBackgroundRestricted(
                         app.info.uid, app.info.packageName);
                 if (inBgRestricted) {
-                    mAppsInBackgroundRestricted.add(app);
+                    synchronized (mService) {
+                        mAppsInBackgroundRestricted.add(app);
+                    }
                 }
                 app.mState.setBackgroundRestricted(inBgRestricted);
             }
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 1b66932..12324bff 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -176,9 +177,8 @@
                 mPermManager.revokeRuntimePermission(packageName, NOTIFICATION_PERMISSION,
                         userId, TAG);
             }
-            int flagMask = userSet || !grant
-                    ? FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT :
-                    FLAG_PERMISSION_USER_SET;
+            int flagMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED;
+            flagMask = userSet || !grant ? flagMask | FLAG_PERMISSION_GRANTED_BY_DEFAULT : flagMask;
             if (userSet) {
                 mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION,
                         flagMask, FLAG_PERMISSION_USER_SET, true, userId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 867be24..10bd4ee 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -308,7 +308,6 @@
 import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
-import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.InputApplicationHandle;
@@ -352,6 +351,7 @@
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.WindowManagerService.H;
 import com.android.server.wm.utils.InsetUtils;
+import com.android.server.wm.utils.WmDisplayCutout;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -9609,9 +9609,8 @@
                 final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
                 final int dw = rotated ? display.mBaseDisplayHeight : display.mBaseDisplayWidth;
                 final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
-                final DisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation)
-                        .getDisplayCutout();
-                policy.getNonDecorInsetsLw(rotation, cutout, mNonDecorInsets[rotation]);
+                final WmDisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation);
+                policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
                 mStableInsets[rotation].set(mNonDecorInsets[rotation]);
                 policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d5362a0..890b910 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2565,7 +2565,6 @@
             mInTask = null;
         }
         mInTaskFragment = inTaskFragment;
-        sendNewTaskFragmentResultRequestIfNeeded();
 
         mStartFlags = startFlags;
         // If the onlyIfNeeded flag is set, then we can do this if the activity being launched
@@ -2608,18 +2607,6 @@
         }
     }
 
-    private void sendNewTaskFragmentResultRequestIfNeeded() {
-        if (mStartActivity.resultTo != null && mInTaskFragment != null
-                && mInTaskFragment != mStartActivity.resultTo.getTaskFragment()) {
-            Slog.w(TAG,
-                    "Activity is launching as a new TaskFragment, so cancelling activity result.");
-            mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
-                    mStartActivity.requestCode, RESULT_CANCELED,
-                    null /* data */, null /* dataGrants */);
-            mStartActivity.resultTo = null;
-        }
-    }
-
     private void computeLaunchingTaskFlags() {
         // If the caller is not coming from another activity, but has given us an explicit task into
         // which they would like us to launch the new activity, then let's see about doing that.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 124b8a6..bbc3558 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -436,7 +436,7 @@
      */
     final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
 
-    /** @see #computeCompatSmallestWidth(boolean, int, int, int) */
+    /** @see #computeCompatSmallestWidth(boolean, int, int) */
     private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
 
     /**
@@ -2012,7 +2012,7 @@
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
         // By updating the Display info here it will be available to
         // #computeScreenConfiguration() later.
-        updateDisplayAndOrientation(getConfiguration().uiMode, null /* outConfig */);
+        updateDisplayAndOrientation(null /* outConfig */);
 
         // NOTE: We disable the rotation in the emulator because
         //       it doesn't support hardware OpenGL emulation yet.
@@ -2062,7 +2062,7 @@
      * changed.
      * Do not call if {@link WindowManagerService#mDisplayReady} == false.
      */
-    private DisplayInfo updateDisplayAndOrientation(int uiMode, Configuration outConfig) {
+    private DisplayInfo updateDisplayAndOrientation(Configuration outConfig) {
         // Use the effective "visual" dimensions based on current rotation
         final int rotation = getRotation();
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
@@ -2074,18 +2074,16 @@
         final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
         final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
 
-        final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
-                displayCutout);
-        final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
-                displayCutout);
+        final Rect appFrame = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation,
+                wmDisplayCutout);
         mDisplayInfo.rotation = rotation;
         mDisplayInfo.logicalWidth = dw;
         mDisplayInfo.logicalHeight = dh;
         mDisplayInfo.logicalDensityDpi = mBaseDisplayDensity;
         mDisplayInfo.physicalXDpi = mBaseDisplayPhysicalXDpi;
         mDisplayInfo.physicalYDpi = mBaseDisplayPhysicalYDpi;
-        mDisplayInfo.appWidth = appWidth;
-        mDisplayInfo.appHeight = appHeight;
+        mDisplayInfo.appWidth = appFrame.width();
+        mDisplayInfo.appHeight = appFrame.height();
         if (isDefaultDisplay) {
             mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
@@ -2099,7 +2097,7 @@
             mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
         }
 
-        computeSizeRangesAndScreenLayout(mDisplayInfo, rotated, uiMode, dw, dh,
+        computeSizeRangesAndScreenLayout(mDisplayInfo, rotated, dw, dh,
                 mDisplayMetrics.density, outConfig);
 
         mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
@@ -2189,10 +2187,8 @@
         outConfig.windowConfiguration.setMaxBounds(0, 0, dw, dh);
         outConfig.windowConfiguration.setBounds(outConfig.windowConfiguration.getMaxBounds());
 
-        final int uiMode = getConfiguration().uiMode;
-        final DisplayCutout displayCutout =
-                calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
-        computeScreenAppConfiguration(outConfig, dw, dh, rotation, uiMode, displayCutout);
+        final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
+        computeScreenAppConfiguration(outConfig, dw, dh, rotation, wmDisplayCutout);
 
         final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
         displayInfo.rotation = rotation;
@@ -2201,38 +2197,35 @@
         final Rect appBounds = outConfig.windowConfiguration.getAppBounds();
         displayInfo.appWidth = appBounds.width();
         displayInfo.appHeight = appBounds.height();
+        final DisplayCutout displayCutout = wmDisplayCutout.getDisplayCutout();
         displayInfo.displayCutout = displayCutout.isEmpty() ? null : displayCutout;
-        computeSizeRangesAndScreenLayout(displayInfo, rotated, uiMode, dw, dh,
+        computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh,
                 mDisplayMetrics.density, outConfig);
         return displayInfo;
     }
 
     /** Compute configuration related to application without changing current display. */
     private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
-            int rotation, int uiMode, DisplayCutout displayCutout) {
-        final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
-                displayCutout);
-        final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
-                displayCutout);
-        mDisplayPolicy.getNonDecorInsetsLw(rotation, displayCutout, mTmpRect);
-        final int leftInset = mTmpRect.left;
-        final int topInset = mTmpRect.top;
+            int rotation, WmDisplayCutout wmDisplayCutout) {
+        DisplayFrames displayFrames =
+                mDisplayPolicy.getSimulatedDisplayFrames(rotation, dw, dh, wmDisplayCutout);
+        final Rect appFrame =
+                mDisplayPolicy.getNonDecorDisplayFrameWithSimulatedFrame(displayFrames);
         // AppBounds at the root level should mirror the app screen size.
-        outConfig.windowConfiguration.setAppBounds(leftInset /* left */, topInset /* top */,
-                leftInset + appWidth /* right */, topInset + appHeight /* bottom */);
+        outConfig.windowConfiguration.setAppBounds(appFrame);
         outConfig.windowConfiguration.setRotation(rotation);
         outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
 
         final float density = mDisplayMetrics.density;
-        outConfig.screenWidthDp = (int) (mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation,
-                uiMode, displayCutout) / density + 0.5f);
-        outConfig.screenHeightDp = (int) (mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation,
-                uiMode, displayCutout) / density + 0.5f);
+        final Point configSize =
+                mDisplayPolicy.getConfigDisplaySizeWithSimulatedFrame(displayFrames);
+        outConfig.screenWidthDp = (int) (configSize.x / density + 0.5f);
+        outConfig.screenHeightDp = (int) (configSize.y / density + 0.5f);
         outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);
         outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
 
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw, dh);
+        outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dw, dh);
         outConfig.windowConfiguration.setDisplayRotation(rotation);
     }
 
@@ -2241,7 +2234,7 @@
      * Do not call if mDisplayReady == false.
      */
     void computeScreenConfiguration(Configuration config) {
-        final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
+        final DisplayInfo displayInfo = updateDisplayAndOrientation(config);
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
         mTmpRect.set(0, 0, dw, dh);
@@ -2250,8 +2243,8 @@
         config.windowConfiguration.setWindowingMode(getWindowingMode());
         config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
 
-        computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation, config.uiMode,
-                displayInfo.displayCutout);
+        computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation,
+                calculateDisplayCutoutForRotation(getRotation()));
 
         config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
                 | ((displayInfo.flags & Display.FLAG_ROUND) != 0
@@ -2340,7 +2333,7 @@
         mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
     }
 
-    private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh) {
+    private int computeCompatSmallestWidth(boolean rotated, int dw, int dh) {
         mTmpDisplayMetrics.setTo(mDisplayMetrics);
         final DisplayMetrics tmpDm = mTmpDisplayMetrics;
         final int unrotDw, unrotDh;
@@ -2351,25 +2344,20 @@
             unrotDw = dw;
             unrotDh = dh;
         }
-        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw,
-                unrotDh);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh,
-                unrotDw);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw,
-                unrotDh);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh,
-                unrotDw);
+        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, tmpDm, unrotDw, unrotDh);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, tmpDm, unrotDh, unrotDw);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, tmpDm, unrotDw, unrotDh);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, tmpDm, unrotDh, unrotDw);
         return sw;
     }
 
-    private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
+    private int reduceCompatConfigWidthSize(int curSize, int rotation,
             DisplayMetrics dm, int dw, int dh) {
-        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
-                rotation).getDisplayCutout();
-        dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
-                displayCutout);
-        dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
-                displayCutout);
+        final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
+        final Rect nonDecorSize = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation,
+                wmDisplayCutout);
+        dm.noncompatWidthPixels = nonDecorSize.width();
+        dm.noncompatHeightPixels = nonDecorSize.height();
         float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
         int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
         if (curSize == 0 || size < curSize) {
@@ -2379,7 +2367,7 @@
     }
 
     private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated,
-            int uiMode, int dw, int dh, float density, Configuration outConfig) {
+            int dw, int dh, float density, Configuration outConfig) {
 
         // We need to determine the smallest width that will occur under normal
         // operation.  To this, start with the base screen size and compute the
@@ -2397,37 +2385,34 @@
         displayInfo.smallestNominalAppHeight = 1<<30;
         displayInfo.largestNominalAppWidth = 0;
         displayInfo.largestNominalAppHeight = 0;
-        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, uiMode, unrotDw, unrotDh);
-        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, uiMode, unrotDh, unrotDw);
-        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, uiMode, unrotDw, unrotDh);
-        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, uiMode, unrotDh, unrotDw);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, unrotDw, unrotDh);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, unrotDh, unrotDw);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, unrotDw, unrotDh);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, unrotDh, unrotDw);
 
         if (outConfig == null) {
             return;
         }
         int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw);
         outConfig.smallestScreenWidthDp =
                 (int) (displayInfo.smallestNominalAppWidth / density + 0.5f);
         outConfig.screenLayout = sl;
     }
 
-    private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
-            int uiMode) {
+    private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh) {
         // Get the display cutout at this rotation.
-        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
-                rotation).getDisplayCutout();
+        final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
 
         // Get the app screen size at this rotation.
-        int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
-        int h = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation, displayCutout);
+        final Rect size = mDisplayPolicy.getNonDecorDisplayFrame(dw, dh, rotation, wmDisplayCutout);
 
         // Compute the screen layout size class for this rotation.
-        int longSize = w;
-        int shortSize = h;
+        int longSize = size.width();
+        int shortSize = size.height();
         if (longSize < shortSize) {
             int tmp = longSize;
             longSize = shortSize;
@@ -2438,25 +2423,20 @@
         return Configuration.reduceScreenLayout(curLayout, longSize, shortSize);
     }
 
-    private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation,
-            int uiMode, int dw, int dh) {
-        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
-                rotation).getDisplayCutout();
-        final int width = mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
-                displayCutout);
-        if (width < displayInfo.smallestNominalAppWidth) {
-            displayInfo.smallestNominalAppWidth = width;
+    private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int dw, int dh) {
+        final WmDisplayCutout wmDisplayCutout = calculateDisplayCutoutForRotation(rotation);
+        final Point size = mDisplayPolicy.getConfigDisplaySize(dw, dh, rotation, wmDisplayCutout);
+        if (size.x < displayInfo.smallestNominalAppWidth) {
+            displayInfo.smallestNominalAppWidth = size.x;
         }
-        if (width > displayInfo.largestNominalAppWidth) {
-            displayInfo.largestNominalAppWidth = width;
+        if (size.x > displayInfo.largestNominalAppWidth) {
+            displayInfo.largestNominalAppWidth = size.x;
         }
-        final int height = mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
-                displayCutout);
-        if (height < displayInfo.smallestNominalAppHeight) {
-            displayInfo.smallestNominalAppHeight = height;
+        if (size.y < displayInfo.smallestNominalAppHeight) {
+            displayInfo.smallestNominalAppHeight = size.y;
         }
-        if (height > displayInfo.largestNominalAppHeight) {
-            displayInfo.largestNominalAppHeight = height;
+        if (size.y > displayInfo.largestNominalAppHeight) {
+            displayInfo.largestNominalAppHeight = size.y;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 98a51a9..33702794 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -102,6 +102,7 @@
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.gui.DropInputMode;
@@ -126,6 +127,8 @@
 import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.InsetsVisibilities;
+import android.view.PrivacyIndicatorBounds;
+import android.view.RoundedCorners;
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewDebug;
@@ -159,6 +162,7 @@
 import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wallpaper.WallpaperManagerInternal;
+import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -1990,35 +1994,6 @@
         return mUiContext;
     }
 
-    private int getNavigationBarWidth(int rotation, int uiMode, int position) {
-        if (mNavigationBar == null) {
-            return 0;
-        }
-        LayoutParams lp = mNavigationBar.mAttrs;
-        if (lp.paramsForRotation != null
-                && lp.paramsForRotation.length == 4
-                && lp.paramsForRotation[rotation] != null) {
-            lp = lp.paramsForRotation[rotation];
-        }
-        Insets providedInsetsSize = null;
-        if (lp.providedInsets != null) {
-            for (InsetsFrameProvider provider : lp.providedInsets) {
-                if (provider.type != ITYPE_NAVIGATION_BAR) {
-                    continue;
-                }
-                providedInsetsSize = provider.insetsSize;
-            }
-        }
-        if (providedInsetsSize != null) {
-            if (position == NAV_BAR_LEFT) {
-                return providedInsetsSize.left;
-            } else if (position == NAV_BAR_RIGHT) {
-                return providedInsetsSize.right;
-            }
-        }
-        return lp.width;
-    }
-
     void notifyDisplayReady() {
         mHandler.post(() -> {
             final int displayId = getDisplayId();
@@ -2035,45 +2010,24 @@
     }
 
     /**
-     * Return the display width available after excluding any screen
-     * decorations that could never be removed in Honeycomb. That is, system bar or
-     * button bar.
+     * Return the display frame available after excluding any screen decorations that could never be
+     * removed in Honeycomb. That is, system bar or button bar.
+     *
+     * @return display frame excluding all non-decor insets.
      */
-    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
-            DisplayCutout displayCutout) {
-        int width = fullWidth;
-        if (hasNavigationBar()) {
-            final int navBarPosition = navigationBarPosition(rotation);
-            if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
-                width -= getNavigationBarWidth(rotation, uiMode, navBarPosition);
-            }
-        }
-        if (displayCutout != null) {
-            width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
-        }
-        return width;
+    Rect getNonDecorDisplayFrame(int fullWidth, int fullHeight, int rotation,
+            WmDisplayCutout cutout) {
+        final DisplayFrames displayFrames =
+                getSimulatedDisplayFrames(rotation, fullWidth, fullHeight, cutout);
+        return getNonDecorDisplayFrameWithSimulatedFrame(displayFrames);
     }
 
-    @VisibleForTesting
-    int getNavigationBarHeight(int rotation) {
-        if (mNavigationBar == null) {
-            return 0;
-        }
-        LayoutParams lp = mNavigationBar.mAttrs.forRotation(rotation);
-        Insets providedInsetsSize = null;
-        if (lp.providedInsets != null) {
-            for (InsetsFrameProvider provider : lp.providedInsets) {
-                if (provider.type != ITYPE_NAVIGATION_BAR) {
-                    continue;
-                }
-                providedInsetsSize = provider.insetsSize;
-                if (providedInsetsSize != null) {
-                    return providedInsetsSize.bottom;
-                }
-                break;
-            }
-        }
-        return lp.height;
+    Rect getNonDecorDisplayFrameWithSimulatedFrame(DisplayFrames displayFrames) {
+        final Rect nonDecorInsets =
+                getInsets(displayFrames, Type.displayCutout() | Type.navigationBars()).toRect();
+        final Rect displayFrame = new Rect(displayFrames.mInsetsState.getDisplayFrame());
+        displayFrame.inset(nonDecorInsets);
+        return displayFrame;
     }
 
     /**
@@ -2095,53 +2049,24 @@
     }
 
     /**
-     * Return the display height available after excluding any screen
-     * decorations that could never be removed in Honeycomb. That is, system bar or
-     * button bar.
-     */
-    public int getNonDecorDisplayHeight(int fullHeight, int rotation, DisplayCutout displayCutout) {
-        int height = fullHeight;
-        final int navBarPosition = navigationBarPosition(rotation);
-        if (navBarPosition == NAV_BAR_BOTTOM) {
-            height -= getNavigationBarHeight(rotation);
-        }
-        if (displayCutout != null) {
-            height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
-        }
-        return height;
-    }
-
-    /**
-     * Return the available screen width that we should report for the
+     * Return the available screen size that we should report for the
      * configuration.  This must be no larger than
-     * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller
+     * {@link #getNonDecorDisplayFrame(int, int, int, DisplayCutout)}; it may be smaller
      * than that to account for more transient decoration like a status bar.
      */
-    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
-            DisplayCutout displayCutout) {
-        return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout);
+    public Point getConfigDisplaySize(int fullWidth, int fullHeight, int rotation,
+            WmDisplayCutout wmDisplayCutout) {
+        final DisplayFrames displayFrames = getSimulatedDisplayFrames(rotation, fullWidth,
+                fullHeight, wmDisplayCutout);
+        return getConfigDisplaySizeWithSimulatedFrame(displayFrames);
     }
 
-    /**
-     * Return the available screen height that we should report for the
-     * configuration.  This must be no larger than
-     * {@link #getNonDecorDisplayHeight(int, int, DisplayCutout)}; it may be smaller
-     * than that to account for more transient decoration like a status bar.
-     */
-    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
-            DisplayCutout displayCutout) {
-        // There is a separate status bar at the top of the display.  We don't count that as part
-        // of the fixed decor, since it can hide; however, for purposes of configurations,
-        // we do want to exclude it since applications can't generally use that part
-        // of the screen.
-        int statusBarHeight = mStatusBarHeightForRotation[rotation];
-        if (displayCutout != null) {
-            // If there is a cutout, it may already have accounted for some part of the status
-            // bar height.
-            statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
-        }
-        return getNonDecorDisplayHeight(fullHeight, rotation, displayCutout)
-                - statusBarHeight;
+    Point getConfigDisplaySizeWithSimulatedFrame(DisplayFrames displayFrames) {
+        final Insets insets = getInsets(displayFrames,
+                Type.displayCutout() | Type.navigationBars() | Type.statusBars());
+        Rect configFrame = new Rect(displayFrames.mInsetsState.getDisplayFrame());
+        configFrame.inset(insets);
+        return new Point(configFrame.width(), configFrame.height());
     }
 
     /**
@@ -2173,48 +2098,69 @@
      * Calculates the stable insets without running a layout.
      *
      * @param displayRotation the current display rotation
+     * @param displayWidth full display width
+     * @param displayHeight full display height
      * @param displayCutout the current display cutout
      * @param outInsets the insets to return
      */
-    public void getStableInsetsLw(int displayRotation, DisplayCutout displayCutout,
-            Rect outInsets) {
-        outInsets.setEmpty();
+    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+            WmDisplayCutout displayCutout, Rect outInsets) {
+        final DisplayFrames displayFrames = getSimulatedDisplayFrames(displayRotation,
+                displayWidth, displayHeight, displayCutout);
+        getStableInsetsWithSimulatedFrame(displayFrames, outInsets);
+    }
 
-        // Navigation bar and status bar.
-        getNonDecorInsetsLw(displayRotation, displayCutout, outInsets);
-        convertNonDecorInsetsToStableInsets(outInsets, displayRotation);
+    void getStableInsetsWithSimulatedFrame(DisplayFrames displayFrames, Rect outInsets) {
+        // Navigation bar, status bar, and cutout.
+        outInsets.set(getInsets(displayFrames,
+                Type.displayCutout() | Type.navigationBars() | Type.statusBars()).toRect());
     }
 
     /**
      * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
-     * bar or button bar. See {@link #getNonDecorDisplayWidth}.
-     *  @param displayRotation the current display rotation
-     * @param displayCutout the current display cutout
+     * bar or button bar. See {@link #getNonDecorDisplayFrame}.
+     *
+     * @param displayRotation the current display rotation
+     * @param fullWidth the width of the display, including all insets
+     * @param fullHeight the height of the display, including all insets
+     * @param cutout the current display cutout
      * @param outInsets the insets to return
      */
-    public void getNonDecorInsetsLw(int displayRotation, DisplayCutout displayCutout,
-            Rect outInsets) {
-        outInsets.setEmpty();
+    public void getNonDecorInsetsLw(int displayRotation, int fullWidth, int fullHeight,
+            WmDisplayCutout cutout, Rect outInsets) {
+        final DisplayFrames displayFrames =
+                getSimulatedDisplayFrames(displayRotation, fullWidth, fullHeight, cutout);
+        getNonDecorInsetsWithSimulatedFrame(displayFrames, outInsets);
+    }
 
-        // Only navigation bar
-        if (hasNavigationBar()) {
-            final int uiMode = mService.mPolicy.getUiMode();
-            int position = navigationBarPosition(displayRotation);
-            if (position == NAV_BAR_BOTTOM) {
-                outInsets.bottom = getNavigationBarHeight(displayRotation);
-            } else if (position == NAV_BAR_RIGHT) {
-                outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position);
-            } else if (position == NAV_BAR_LEFT) {
-                outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position);
-            }
-        }
+    void getNonDecorInsetsWithSimulatedFrame(DisplayFrames displayFrames, Rect outInsets) {
+        outInsets.set(getInsets(displayFrames,
+                Type.displayCutout() | Type.navigationBars()).toRect());
+    }
 
-        if (displayCutout != null) {
-            outInsets.left += displayCutout.getSafeInsetLeft();
-            outInsets.top += displayCutout.getSafeInsetTop();
-            outInsets.right += displayCutout.getSafeInsetRight();
-            outInsets.bottom += displayCutout.getSafeInsetBottom();
-        }
+    DisplayFrames getSimulatedDisplayFrames(int displayRotation, int fullWidth,
+            int fullHeight, WmDisplayCutout cutout) {
+        final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
+        info.rotation = displayRotation;
+        info.logicalWidth = fullWidth;
+        info.logicalHeight = fullHeight;
+        info.displayCutout = cutout.getDisplayCutout();
+        final RoundedCorners roundedCorners =
+                mDisplayContent.calculateRoundedCornersForRotation(displayRotation);
+        final PrivacyIndicatorBounds indicatorBounds =
+                mDisplayContent.calculatePrivacyIndicatorBoundsForRotation(displayRotation);
+        final DisplayFrames displayFrames = new DisplayFrames(getDisplayId(), new InsetsState(),
+                info, cutout, roundedCorners, indicatorBounds);
+        simulateLayoutDisplay(displayFrames);
+        return displayFrames;
+    }
+
+    @VisibleForTesting
+    Insets getInsets(DisplayFrames displayFrames, @InsetsType int type) {
+        final InsetsState state = displayFrames.mInsetsState;
+        final Insets insets = state.calculateInsets(state.getDisplayFrame(), type,
+                true /* ignoreVisibility */);
+        return insets;
     }
 
     @NavigationBarPosition
@@ -2240,7 +2186,7 @@
      * @see WindowManagerPolicyConstants#NAV_BAR_BOTTOM
      */
     @NavigationBarPosition
-    public int getNavBarPosition() {
+    int getNavBarPosition() {
         return mNavigationBarPosition;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 21c5886..b34a3ed 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -98,6 +98,7 @@
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.am.HostingRecord;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -2194,11 +2195,13 @@
         mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
 
         final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
-        policy.getNonDecorInsetsLw(displayInfo.rotation,
-                displayInfo.displayCutout, mTmpInsets);
+        final WmDisplayCutout cutout =
+                rootTask.mDisplayContent.calculateDisplayCutoutForRotation(displayInfo.rotation);
+        final DisplayFrames displayFrames = policy.getSimulatedDisplayFrames(displayInfo.rotation,
+                displayInfo.logicalWidth, displayInfo.logicalHeight, cutout);
+        policy.getNonDecorInsetsWithSimulatedFrame(displayFrames, mTmpInsets);
         intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
-
-        policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
+        policy.getStableInsetsWithSimulatedFrame(displayFrames, mTmpInsets);
         intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
     }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 09f6110..c455ac1 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -533,9 +533,14 @@
         }
         // Need to update layers on involved displays since they were all paused while
         // the animation played. This puts the layers back into the correct order.
-        for (int i = displays.size() - 1; i >= 0; --i) {
-            if (displays.valueAt(i) == null) continue;
-            displays.valueAt(i).assignChildLayers(t);
+        mController.mBuildingFinishLayers = true;
+        try {
+            for (int i = displays.size() - 1; i >= 0; --i) {
+                if (displays.valueAt(i) == null) continue;
+                displays.valueAt(i).assignChildLayers(t);
+            }
+        } finally {
+            mController.mBuildingFinishLayers = false;
         }
         if (rootLeash.isValid()) {
             t.reparent(rootLeash, null);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index dbc2c5f..88572a9 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -100,6 +100,14 @@
     // TODO(b/188595497): remove when not needed.
     final StatusBarManagerInternal mStatusBar;
 
+    /**
+     * `true` when building surface layer order for the finish transaction. We want to prevent
+     * wm from touching z-order of surfaces during transitions, but we still need to be able to
+     * calculate the layers for the finishTransaction. So, when assigning layers into the finish
+     * transaction, set this to true so that the {@link canAssignLayers} will allow it.
+     */
+    boolean mBuildingFinishLayers = false;
+
     TransitionController(ActivityTaskManagerService atm,
             TaskSnapshotController taskSnapshotController,
             TransitionTracer transitionTracer) {
@@ -309,6 +317,15 @@
         return false;
     }
 
+    /**
+     * Whether WM can assign layers to window surfaces at this time. This is usually false while
+     * playing, but can be "opened-up" for certain transition operations like calculating layers
+     * for finishTransaction.
+     */
+    boolean canAssignLayers() {
+        return mBuildingFinishLayers || !isPlaying();
+    }
+
     @WindowConfiguration.WindowingMode
     int getWindowingModeAtStart(@NonNull WindowContainer wc) {
         if (mCollectingTransition == null) return wc.getWindowingMode();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 19b3384..d9b25ad 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -429,6 +429,13 @@
         if (insetsTypes == null || insetsTypes.length == 0) {
             throw new IllegalArgumentException("Insets type not specified.");
         }
+        if (mDisplayContent == null) {
+            // This is possible this container is detached when WM shell is responding to a previous
+            // request. WM shell will be updated when this container is attached again and the
+            // insets need to be updated.
+            Slog.w(TAG, "Can't add local rect insets source provider when detached. " + this);
+            return;
+        }
         if (mLocalInsetsSourceProviders == null) {
             mLocalInsetsSourceProviders = new SparseArray<>();
         }
@@ -1011,6 +1018,9 @@
         if (dc != null && dc != this) {
             dc.getPendingTransaction().merge(mPendingTransaction);
         }
+        if (dc != this && mLocalInsetsSourceProviders != null) {
+            mLocalInsetsSourceProviders.clear();
+        }
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer child = mChildren.get(i);
             child.onDisplayChanged(dc);
@@ -2473,7 +2483,7 @@
     void assignLayer(Transaction t, int layer) {
         // Don't assign layers while a transition animation is playing
         // TODO(b/173528115): establish robust best-practices around z-order fighting.
-        if (mTransitionController.isPlaying()) return;
+        if (!mTransitionController.canAssignLayers()) return;
         final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
         if (mSurfaceControl != null && changed) {
             setLayer(t, layer);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d31dfee..86876c3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -317,6 +317,7 @@
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.power.ShutdownThread;
 import com.android.server.utils.PriorityDump;
+import com.android.server.wm.utils.WmDisplayCutout;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -1853,7 +1854,8 @@
             ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
                     + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
 
-            if (win.isVisibleRequestedOrAdding() && displayContent.updateOrientation()) {
+            if ((win.isVisibleRequestedOrAdding() && displayContent.updateOrientation())
+                    || win.providesNonDecorInsets()) {
                 displayContent.sendNewConfiguration();
             }
 
@@ -6376,7 +6378,8 @@
                         + " callers=" + Debug.getCallers(3));
                 return NAV_BAR_INVALID;
             }
-            return displayContent.getDisplayPolicy().getNavBarPosition();
+            return displayContent.getDisplayPolicy().navigationBarPosition(
+                    displayContent.getDisplayRotation().getRotation());
         }
     }
 
@@ -7217,7 +7220,9 @@
         final DisplayContent dc = mRoot.getDisplayContent(displayId);
         if (dc != null) {
             final DisplayInfo di = dc.getDisplayInfo();
-            dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.displayCutout, outInsets);
+            final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(di.rotation);
+            dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+                    cutout, outInsets);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index af8c4c8..b3fe19f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -244,6 +244,7 @@
 import android.view.ViewDebug;
 import android.view.ViewTreeObserver;
 import android.view.WindowInfo;
+import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowManager;
 import android.view.animation.Animation;
@@ -1921,6 +1922,19 @@
         return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL;
     }
 
+    boolean providesNonDecorInsets() {
+        if (mProvidedInsetsSources == null) {
+            return false;
+        }
+        for (int i = mProvidedInsetsSources.size() - 1; i >= 0; i--) {
+            final int type = mProvidedInsetsSources.keyAt(i);
+            if ((InsetsState.toPublicType(type) & WindowInsets.Type.navigationBars()) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void clearPolicyVisibilityFlag(int policyVisibilityFlag) {
         mPolicyVisibility &= ~policyVisibilityFlag;
         mWmService.scheduleAnimationLocked();
@@ -2631,14 +2645,19 @@
             }
 
             removeImmediately();
-            // Removing a visible window will effect the computed orientation
-            // So just update orientation if needed.
+            boolean sentNewConfig = false;
             if (wasVisible) {
+                // Removing a visible window will effect the computed orientation
+                // So just update orientation if needed.
                 final DisplayContent displayContent = getDisplayContent();
                 if (displayContent.updateOrientation()) {
                     displayContent.sendNewConfiguration();
+                    sentNewConfig = true;
                 }
             }
+            if (!sentNewConfig && providesNonDecorInsets()) {
+                getDisplayContent().sendNewConfiguration();
+            }
             mWmService.updateFocusedWindowLocked(isFocused()
                             ? UPDATE_FOCUS_REMOVING_FOCUS
                             : UPDATE_FOCUS_NORMAL,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index d4886e4..f2b1dc9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -18,6 +18,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -74,6 +75,8 @@
 
     private PermissionHelper mPermissionHelper;
 
+    private static final int USER_FLAG_MASK = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -183,7 +186,7 @@
         verify(mPermManager).grantRuntimePermission(
                 "pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+                USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
                 FLAG_PERMISSION_USER_SET, true, 10);
     }
 
@@ -202,7 +205,7 @@
         verify(mPermManager).grantRuntimePermission(
                 "pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+                USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
                 FLAG_PERMISSION_USER_SET, true, 10);
     }
 
@@ -216,7 +219,7 @@
         verify(mPermManager).revokeRuntimePermission(
                 eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString());
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+                USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
                 FLAG_PERMISSION_USER_SET, true, 10);
     }
 
@@ -230,7 +233,7 @@
         verify(mPermManager).grantRuntimePermission(
                 "pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                FLAG_PERMISSION_USER_SET, 0, true, 10);
+                USER_FLAG_MASK, 0, true, 10);
     }
 
     @Test
@@ -243,7 +246,7 @@
         verify(mPermManager).revokeRuntimePermission(
                 eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString());
         verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
-                FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0,
+                USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0,
                 true, 10);
     }
 
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 3f3d01a..ea0e8c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -157,6 +157,7 @@
 
 import com.android.internal.R;
 import com.android.server.wm.ActivityRecord.State;
+import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -550,7 +551,8 @@
         final Rect insets = new Rect();
         final DisplayInfo displayInfo = task.mDisplayContent.getDisplayInfo();
         final DisplayPolicy policy = task.mDisplayContent.getDisplayPolicy();
-        policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.displayCutout, insets);
+        policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+                displayInfo.logicalHeight, WmDisplayCutout.NO_CUTOUT, insets);
         policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
         Task.intersectWithInsetsIfFits(stableRect, stableRect, insets);
 
@@ -591,7 +593,8 @@
         final Rect insets = new Rect();
         final DisplayInfo displayInfo = rootTask.mDisplayContent.getDisplayInfo();
         final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
-        policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.displayCutout, insets);
+        policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+                displayInfo.logicalHeight, WmDisplayCutout.NO_CUTOUT, insets);
         policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
         Task.intersectWithInsetsIfFits(stableRect, stableRect, insets);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index f41fee7..a001eda 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -25,10 +25,13 @@
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
 import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.wm.utils.WmDisplayCutout;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ErrorCollector;
@@ -46,7 +49,8 @@
 
     @Test
     public void portrait() {
-        final DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */);
+        final Pair<DisplayInfo, WmDisplayCutout> di =
+                displayInfoForRotation(ROTATION_0, false /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
         verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -55,7 +59,8 @@
 
     @Test
     public void portrait_withCutout() {
-        final DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */);
+        final Pair<DisplayInfo, WmDisplayCutout> di =
+                displayInfoForRotation(ROTATION_0, true /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
         verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT);
@@ -64,7 +69,8 @@
 
     @Test
     public void landscape() {
-        final DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */);
+        final Pair<DisplayInfo, WmDisplayCutout> di =
+                displayInfoForRotation(ROTATION_90, false /* withCutout */);
 
         if (mDisplayPolicy.navigationBarCanMove()) {
             verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
@@ -79,7 +85,8 @@
 
     @Test
     public void landscape_withCutout() {
-        final DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */);
+        final Pair<DisplayInfo, WmDisplayCutout> di =
+                displayInfoForRotation(ROTATION_90, true /* withCutout */);
 
         if (mDisplayPolicy.navigationBarCanMove()) {
             verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
@@ -94,7 +101,8 @@
 
     @Test
     public void seascape() {
-        final DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */);
+        final Pair<DisplayInfo, WmDisplayCutout> di =
+                displayInfoForRotation(ROTATION_270, false /* withCutout */);
 
         if (mDisplayPolicy.navigationBarCanMove()) {
             verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
@@ -109,7 +117,8 @@
 
     @Test
     public void seascape_withCutout() {
-        final DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */);
+        final Pair<DisplayInfo, WmDisplayCutout> di =
+                displayInfoForRotation(ROTATION_270, true /* withCutout */);
 
         if (mDisplayPolicy.navigationBarCanMove()) {
             verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
@@ -124,7 +133,8 @@
 
     @Test
     public void upsideDown() {
-        final DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */);
+        final Pair<DisplayInfo, WmDisplayCutout> di =
+                displayInfoForRotation(ROTATION_180, false /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
         verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT);
@@ -133,28 +143,34 @@
 
     @Test
     public void upsideDown_withCutout() {
-        final DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */);
+        final Pair<DisplayInfo, WmDisplayCutout> di =
+                displayInfoForRotation(ROTATION_180, true /* withCutout */);
 
         verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
         verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT);
         verifyConsistency(di);
     }
 
-    private void verifyStableInsets(DisplayInfo di, int left, int top, int right, int bottom) {
-        mErrorCollector.checkThat("stableInsets", getStableInsetsLw(di), equalTo(new Rect(
+    private void verifyStableInsets(Pair<DisplayInfo, WmDisplayCutout> diPair, int left, int top,
+            int right, int bottom) {
+        mErrorCollector.checkThat("stableInsets", getStableInsetsLw(diPair.first, diPair.second),
+                equalTo(new Rect(left, top, right, bottom)));
+    }
+
+    private void verifyNonDecorInsets(Pair<DisplayInfo, WmDisplayCutout> diPair, int left, int top,
+            int right, int bottom) {
+        mErrorCollector.checkThat("nonDecorInsets",
+                getNonDecorInsetsLw(diPair.first, diPair.second), equalTo(new Rect(
                 left, top, right, bottom)));
     }
 
-    private void verifyNonDecorInsets(DisplayInfo di, int left, int top, int right, int bottom) {
-        mErrorCollector.checkThat("nonDecorInsets", getNonDecorInsetsLw(di), equalTo(new Rect(
-                left, top, right, bottom)));
-    }
-
-    private void verifyConsistency(DisplayInfo di) {
-        verifyConsistency("configDisplay", di, getStableInsetsLw(di),
-                getConfigDisplayWidth(di), getConfigDisplayHeight(di));
-        verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di),
-                getNonDecorDisplayWidth(di), getNonDecorDisplayHeight(di));
+    private void verifyConsistency(Pair<DisplayInfo, WmDisplayCutout> diPair) {
+        final DisplayInfo di = diPair.first;
+        final WmDisplayCutout cutout = diPair.second;
+        verifyConsistency("configDisplay", di, getStableInsetsLw(di, cutout),
+                getConfigDisplayWidth(di, cutout), getConfigDisplayHeight(di, cutout));
+        verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di, cutout),
+                getNonDecorDisplayWidth(di, cutout), getNonDecorDisplayHeight(di, cutout));
     }
 
     private void verifyConsistency(String what, DisplayInfo di, Rect insets, int width,
@@ -165,39 +181,42 @@
                 equalTo(di.logicalHeight - insets.top - insets.bottom));
     }
 
-    private Rect getStableInsetsLw(DisplayInfo di) {
+    private Rect getStableInsetsLw(DisplayInfo di, WmDisplayCutout cutout) {
         Rect result = new Rect();
-        mDisplayPolicy.getStableInsetsLw(di.rotation, di.displayCutout, result);
+        mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+                cutout, result);
         return result;
     }
 
-    private Rect getNonDecorInsetsLw(DisplayInfo di) {
+    private Rect getNonDecorInsetsLw(DisplayInfo di, WmDisplayCutout cutout) {
         Rect result = new Rect();
-        mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.displayCutout, result);
+        mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+                cutout, result);
         return result;
     }
 
-    private int getNonDecorDisplayWidth(DisplayInfo di) {
-        return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight,
-                di.rotation, 0 /* ui */, di.displayCutout);
+    private int getNonDecorDisplayWidth(DisplayInfo di, WmDisplayCutout cutout) {
+        return mDisplayPolicy.getNonDecorDisplayFrame(di.logicalWidth, di.logicalHeight,
+                di.rotation, cutout).width();
     }
 
-    private int getNonDecorDisplayHeight(DisplayInfo di) {
-        return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalHeight, di.rotation,
-                di.displayCutout);
+    private int getNonDecorDisplayHeight(DisplayInfo di, WmDisplayCutout cutout) {
+        return mDisplayPolicy.getNonDecorDisplayFrame(di.logicalWidth, di.logicalHeight,
+                di.rotation, cutout).height();
     }
 
-    private int getConfigDisplayWidth(DisplayInfo di) {
-        return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight,
-                di.rotation, 0 /* ui */, di.displayCutout);
+    private int getConfigDisplayWidth(DisplayInfo di, WmDisplayCutout cutout) {
+        return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
+                di.rotation, cutout).x;
     }
 
-    private int getConfigDisplayHeight(DisplayInfo di) {
-        return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight,
-                di.rotation, 0 /* ui */, di.displayCutout);
+    private int getConfigDisplayHeight(DisplayInfo di, WmDisplayCutout cutout) {
+        return mDisplayPolicy.getConfigDisplaySize(di.logicalWidth, di.logicalHeight,
+                di.rotation, cutout).y;
     }
 
-    private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
-        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout, false).first;
+    private static Pair<DisplayInfo, WmDisplayCutout> displayInfoForRotation(int rotation,
+            boolean withDisplayCutout) {
+        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout, false);
     }
 }
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 1e64e46..e0de76f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -26,12 +26,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -43,6 +40,7 @@
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
+import android.view.WindowInsets;
 
 import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
 
@@ -204,7 +202,11 @@
                 doReturn(true).when(newDisplay).supportsSystemDecorations();
                 doReturn(true).when(displayPolicy).hasNavigationBar();
                 doReturn(NAV_BAR_BOTTOM).when(displayPolicy).navigationBarPosition(anyInt());
-                doReturn(20).when(displayPolicy).getNavigationBarHeight(anyInt());
+                doReturn(Insets.of(0, 0, 0, 20)).when(displayPolicy).getInsets(any(),
+                        eq(WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars()));
+                doReturn(Insets.of(0, 20, 0, 20)).when(displayPolicy).getInsets(any(),
+                        eq(WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars()
+                                | WindowInsets.Type.statusBars()));
             } else {
                 doReturn(false).when(displayPolicy).hasNavigationBar();
                 doReturn(false).when(displayPolicy).hasStatusBar();
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 8a539cd..919418b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -323,6 +323,10 @@
             mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
             mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
             mNavBarWindow.mAttrs.setFitInsetsTypes(0);
+            mNavBarWindow.mAttrs.layoutInDisplayCutoutMode =
+                    LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+            mNavBarWindow.mAttrs.privateFlags |=
+                    WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
             for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
                 mNavBarWindow.mAttrs.paramsForRotation[rot] =
                         getNavBarLayoutParamsForRotation(rot);
@@ -379,6 +383,9 @@
         lp.height = height;
         lp.gravity = gravity;
         lp.setFitInsetsTypes(0);
+        lp.privateFlags |=
+                WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
+        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         return lp;
     }