Merge "Fix share/edit actions for work profile." into tm-qpr-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f320b74..e837920 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9591,21 +9591,16 @@
         @NonNull
         public ArrayList<Action> getActionsListWithSystemActions() {
             // Define the system actions we expect to see
-            final Action negativeAction = makeNegativeAction();
-            final Action answerAction = makeAnswerAction();
-            // Sort the expected actions into the correct order:
-            // * If there's no answer action, put the hang up / decline action at the end
-            // * Otherwise put the answer action at the end, and put the decline action at start.
-            final Action firstAction = answerAction == null ? null : negativeAction;
-            final Action lastAction = answerAction == null ? negativeAction : answerAction;
+            final Action firstAction = makeNegativeAction();
+            final Action lastAction = makeAnswerAction();
 
             // Start creating the result list.
             int nonContextualActionSlotsRemaining = MAX_ACTION_BUTTONS;
             ArrayList<Action> resultActions = new ArrayList<>(MAX_ACTION_BUTTONS);
-            if (firstAction != null) {
-                resultActions.add(firstAction);
-                --nonContextualActionSlotsRemaining;
-            }
+
+            // Always have a first action.
+            resultActions.add(firstAction);
+            --nonContextualActionSlotsRemaining;
 
             // Copy actions into the new list, correcting system actions.
             if (mBuilder.mActions != null) {
@@ -9621,14 +9616,14 @@
                         --nonContextualActionSlotsRemaining;
                     }
                     // If there's exactly one action slot left, fill it with the lastAction.
-                    if (nonContextualActionSlotsRemaining == 1) {
+                    if (lastAction != null && nonContextualActionSlotsRemaining == 1) {
                         resultActions.add(lastAction);
                         --nonContextualActionSlotsRemaining;
                     }
                 }
             }
             // If there are any action slots left, the lastAction still needs to be added.
-            if (nonContextualActionSlotsRemaining >= 1) {
+            if (lastAction != null && nonContextualActionSlotsRemaining >= 1) {
                 resultActions.add(lastAction);
             }
             return resultActions;
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index a47fe82..8174778 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -25,11 +25,12 @@
 import android.os.PersistableBundle;
 import android.os.TransactionTooLargeException;
 import android.util.Log;
-import android.util.LogWriter;
 import android.util.Slog;
 
 import com.android.internal.util.IndentingPrintWriter;
 
+import java.io.StringWriter;
+
 /**
  * Container that has data pending to be used at later stages of
  * {@link android.app.servertransaction.ClientTransaction}.
@@ -134,6 +135,16 @@
             mDescription = description;
         }
 
+        private String collectBundleStates() {
+            final StringWriter writer = new StringWriter();
+            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+            pw.println("Bundle stats:");
+            Bundle.dumpStats(pw, mState);
+            pw.println("PersistableBundle stats:");
+            Bundle.dumpStats(pw, mPersistentState);
+            return writer.toString().stripTrailing();
+        }
+
         @Override
         public void run() {
             // Tell activity manager we have been stopped.
@@ -142,19 +153,24 @@
                 // TODO(lifecycler): Use interface callback instead of AMS.
                 ActivityClient.getInstance().activityStopped(
                         mActivity.token, mState, mPersistentState, mDescription);
-            } catch (RuntimeException ex) {
-                // Dump statistics about bundle to help developers debug
-                final LogWriter writer = new LogWriter(Log.WARN, TAG);
-                final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
-                pw.println("Bundle stats:");
-                Bundle.dumpStats(pw, mState);
-                pw.println("PersistableBundle stats:");
-                Bundle.dumpStats(pw, mPersistentState);
+            } catch (RuntimeException runtimeException) {
+                // Collect the statistics about bundle
+                final String bundleStats = collectBundleStates();
 
-                if (ex.getCause() instanceof TransactionTooLargeException
-                        && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
-                    Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
-                    return;
+                RuntimeException ex = runtimeException;
+                if (ex.getCause() instanceof TransactionTooLargeException) {
+                    // Embed the stats into exception message to help developers debug if the
+                    // transaction size is too large.
+                    final String message = ex.getMessage() + "\n" + bundleStats;
+                    ex = new RuntimeException(message, ex.getCause());
+                    if (mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+                        Log.e(TAG, "App sent too much data in instance state, so it was ignored",
+                                ex);
+                        return;
+                    }
+                } else {
+                    // Otherwise, dump the stats anyway.
+                    Log.w(TAG, bundleStats);
                 }
                 throw ex;
             }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8428060..a9799f2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -701,7 +701,7 @@
 
     <!-- Indicates the time needed to time out the fold animation if the device stops in half folded
          mode. -->
-    <integer name="config_unfoldTransitionHalfFoldedTimeout">600</integer>
+    <integer name="config_unfoldTransitionHalfFoldedTimeout">1000</integer>
 
     <!-- Indicates that the device supports having more than one internal display on at the same
          time. Only applicable to devices with more than one internal display. If this option is
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index f9f3b4c..0b8b29b 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -59,6 +59,7 @@
 import static org.mockito.Mockito.spy;
 
 import android.annotation.Nullable;
+import android.app.Notification.CallStyle;
 import android.content.Context;
 import android.content.Intent;
 import android.content.LocusId;
@@ -92,6 +93,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
 import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
@@ -531,6 +533,108 @@
     }
 
     @Test
+    public void testCallStyle_getSystemActions_forIncomingCall() {
+        PendingIntent answerIntent = createPendingIntent("answer");
+        PendingIntent declineIntent = createPendingIntent("decline");
+        Notification.CallStyle style = Notification.CallStyle.forIncomingCall(
+                new Person.Builder().setName("A Caller").build(),
+                declineIntent,
+                answerIntent
+        );
+        style.setBuilder(new Notification.Builder(mContext, "Channel"));
+
+        List<Notification.Action> actions = style.getActionsListWithSystemActions();
+
+        assertEquals(2, actions.size());
+        assertEquals(declineIntent, actions.get(0).actionIntent);
+        assertEquals(answerIntent, actions.get(1).actionIntent);
+    }
+
+    @Test
+    public void testCallStyle_getSystemActions_forOngoingCall() {
+        PendingIntent hangUpIntent = createPendingIntent("hangUp");
+        Notification.CallStyle style = Notification.CallStyle.forOngoingCall(
+                new Person.Builder().setName("A Caller").build(),
+                hangUpIntent
+        );
+        style.setBuilder(new Notification.Builder(mContext, "Channel"));
+
+        List<Notification.Action> actions = style.getActionsListWithSystemActions();
+
+        assertEquals(1, actions.size());
+        assertEquals(hangUpIntent, actions.get(0).actionIntent);
+    }
+
+    @Test
+    public void testCallStyle_getSystemActions_forIncomingCallWithOtherActions() {
+        PendingIntent answerIntent = createPendingIntent("answer");
+        PendingIntent declineIntent = createPendingIntent("decline");
+        Notification.CallStyle style = Notification.CallStyle.forIncomingCall(
+                new Person.Builder().setName("A Caller").build(),
+                declineIntent,
+                answerIntent
+        );
+        Notification.Action actionToKeep = makeNotificationAction(null);
+        Notification.Action actionToDrop = makeNotificationAction(null);
+        Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel")
+                .addAction(actionToKeep)
+                .addAction(actionToDrop); //expect to move this action to the end
+        style.setBuilder(notifBuilder); //add a builder with actions
+
+        List<Notification.Action> actions = style.getActionsListWithSystemActions();
+
+        assertEquals(4, actions.size());
+        assertEquals(declineIntent, actions.get(0).actionIntent);
+        assertEquals(actionToKeep, actions.get(1));
+        assertEquals(answerIntent, actions.get(2).actionIntent);
+        assertEquals(actionToDrop, actions.get(3));
+    }
+
+    @Test
+    public void testCallStyle_getSystemActions_forOngoingCallWithOtherActions() {
+        PendingIntent hangUpIntent = createPendingIntent("hangUp");
+        Notification.CallStyle style = Notification.CallStyle.forOngoingCall(
+                new Person.Builder().setName("A Caller").build(),
+                hangUpIntent
+        );
+        Notification.Action firstAction = makeNotificationAction(null);
+        Notification.Action secondAction = makeNotificationAction(null);
+        Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel")
+                .addAction(firstAction)
+                .addAction(secondAction);
+        style.setBuilder(notifBuilder); //add a builder with actions
+
+        List<Notification.Action> actions = style.getActionsListWithSystemActions();
+
+        assertEquals(3, actions.size());
+        assertEquals(hangUpIntent, actions.get(0).actionIntent);
+        assertEquals(firstAction, actions.get(1));
+        assertEquals(secondAction, actions.get(2));
+    }
+
+    @Test
+    public void testCallStyle_getSystemActions_dropsOldSystemActions() {
+        PendingIntent hangUpIntent = createPendingIntent("decline");
+        Notification.CallStyle style = Notification.CallStyle.forOngoingCall(
+                new Person.Builder().setName("A Caller").build(),
+                hangUpIntent
+        );
+        Bundle actionExtras = new Bundle();
+        actionExtras.putBoolean("key_action_priority", true);
+        Notification.Action oldSystemAction = makeNotificationAction(
+                builder -> builder.addExtras(actionExtras)
+        );
+        Notification.Builder notifBuilder = new Notification.Builder(mContext, "Channel")
+                .addAction(oldSystemAction);
+        style.setBuilder(notifBuilder); //add a builder with actions
+
+        List<Notification.Action> actions = style.getActionsListWithSystemActions();
+
+        assertFalse("Old versions of system actions should be dropped.",
+                actions.contains(oldSystemAction));
+    }
+
+    @Test
     public void testBuild_ensureSmallIconIsNotTooBig_resizesIcon() {
         Icon hugeIcon = Icon.createWithBitmap(
                 Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));
@@ -788,7 +892,7 @@
 
     @Test
     public void testRestoreFromExtras_Call_invalidExtra_noCrash() {
-        Notification.Style style = new Notification.CallStyle();
+        Notification.Style style = new CallStyle();
         Bundle fakeTypes = new Bundle();
         fakeTypes.putParcelable(EXTRA_CALL_PERSON, new Bundle());
         fakeTypes.putParcelable(EXTRA_ANSWER_INTENT, new Bundle());
@@ -962,4 +1066,12 @@
         }
         return actionBuilder.build();
     }
+
+    /**
+     * Creates a PendingIntent with the given action.
+     */
+    private PendingIntent createPendingIntent(String action) {
+        return PendingIntent.getActivity(mContext, 0, new Intent(action),
+                PendingIntent.FLAG_MUTABLE);
+    }
 }
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
index db54ae3..d4439f9 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
@@ -39,7 +39,6 @@
         android:layout_height="wrap_content"
         android:paddingStart="24dp"
         android:paddingEnd="24dp"
-        android:singleLine="true"
         android:textAppearance="?android:attr/textAppearanceListItemSmall"/>
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index d90156d..8135aaa 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -241,4 +241,6 @@
     <string name="clock_title_bubble">Bubble</string>
     <!-- Name of the "Analog" clock face [CHAR LIMIT=15]-->
     <string name="clock_title_analog">Analog</string>
+    <!-- Title of bouncer when we want to authenticate before continuing with action. [CHAR LIMIT=NONE] -->
+    <string name="keyguard_unlock_to_continue">Unlock your device to continue</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
index 692fe83..e6a2bfa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
@@ -17,7 +17,6 @@
 package com.android.keyguard
 
 import android.app.StatusBarManager.SESSION_KEYGUARD
-import android.content.Context
 import android.hardware.biometrics.BiometricSourceType
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.logging.UiEvent
@@ -41,11 +40,10 @@
  */
 @SysUISingleton
 class KeyguardBiometricLockoutLogger @Inject constructor(
-    context: Context?,
     private val uiEventLogger: UiEventLogger,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val sessionTracker: SessionTracker
-) : CoreStartable(context) {
+) : CoreStartable {
     private var fingerprintLockedOut = false
     private var faceLockedOut = false
     private var encryptedOrLockdown = false
@@ -169,4 +167,4 @@
             return strongAuthFlags and flagCheck != 0
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c34db15..93ee151 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -67,7 +67,6 @@
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
 import android.view.WindowManager;
-import android.widget.AdapterView;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -318,7 +317,8 @@
     }
 
     void initMode(@Mode int mode, GlobalSettings globalSettings, FalsingManager falsingManager,
-            UserSwitcherController userSwitcherController) {
+            UserSwitcherController userSwitcherController,
+            UserSwitcherViewMode.UserSwitcherCallback userSwitcherCallback) {
         if (mCurrentMode == mode) return;
         Log.i(TAG, "Switching mode from " + modeToString(mCurrentMode) + " to "
                 + modeToString(mode));
@@ -330,7 +330,7 @@
                 mViewMode = new OneHandedViewMode();
                 break;
             case MODE_USER_SWITCHER:
-                mViewMode = new UserSwitcherViewMode();
+                mViewMode = new UserSwitcherViewMode(userSwitcherCallback);
                 break;
             default:
                 mViewMode = new DefaultViewMode();
@@ -864,6 +864,12 @@
         private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
                 this::setupUserSwitcher;
 
+        private UserSwitcherCallback mUserSwitcherCallback;
+
+        UserSwitcherViewMode(UserSwitcherCallback userSwitcherCallback) {
+            mUserSwitcherCallback = userSwitcherCallback;
+        }
+
         @Override
         public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
                 @NonNull KeyguardSecurityViewFlipper viewFlipper,
@@ -1040,34 +1046,25 @@
                 }
             };
 
-            if (adapter.getCount() < 2) {
-                // The drop down arrow is at index 1
-                ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(0);
-                anchor.setClickable(false);
-                return;
-            } else {
-                ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(255);
-            }
-
             anchor.setOnClickListener((v) -> {
                 if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
                 mPopup = new KeyguardUserSwitcherPopupMenu(v.getContext(), mFalsingManager);
                 mPopup.setAnchorView(anchor);
                 mPopup.setAdapter(adapter);
-                mPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-                        public void onItemClick(AdapterView parent, View view, int pos, long id) {
-                            if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
-                            if (!view.isEnabled()) return;
-
-                            // Subtract one for the header
-                            UserRecord user = adapter.getItem(pos - 1);
-                            if (!user.isCurrent) {
-                                adapter.onUserListItemClicked(user);
-                            }
-                            mPopup.dismiss();
-                            mPopup = null;
-                        }
-                    });
+                mPopup.setOnItemClickListener((parent, view, pos, id) -> {
+                    if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
+                    if (!view.isEnabled()) return;
+                    // Subtract one for the header
+                    UserRecord user = adapter.getItem(pos - 1);
+                    if (user.isManageUsers || user.isAddSupervisedUser) {
+                        mUserSwitcherCallback.showUnlockToContinueMessage();
+                    }
+                    if (!user.isCurrent) {
+                        adapter.onUserListItemClicked(user);
+                    }
+                    mPopup.dismiss();
+                    mPopup = null;
+                });
                 mPopup.show();
             });
         }
@@ -1122,6 +1119,10 @@
                 constraintSet.applyTo(mView);
             }
         }
+
+        interface UserSwitcherCallback {
+            void showUnlockToContinueMessage();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index d448f40..bcd1a1e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -620,7 +620,9 @@
             mode = KeyguardSecurityContainer.MODE_ONE_HANDED;
         }
 
-        mView.initMode(mode, mGlobalSettings, mFalsingManager, mUserSwitcherController);
+        mView.initMode(mode, mGlobalSettings, mFalsingManager, mUserSwitcherController,
+                () -> showMessage(getContext().getString(R.string.keyguard_unlock_to_continue),
+                        null));
     }
 
     public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
index 9eb2c11..c9128e5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
@@ -109,12 +109,13 @@
             object : AnimatorListenerAdapter() {
                 override fun onAnimationEnd(animation: Animator) {
                     runningSecurityShiftAnimator = null
+                    if (shouldRestoreLayerType) {
+                        v.setLayerType(View.LAYER_TYPE_NONE, /* paint= */ null)
+                    }
                 }
             }
         )
 
-        var finishedFadingOutNonSecurityView = false
-
         runningSecurityShiftAnimator.addUpdateListener { animation: ValueAnimator ->
             val switchPoint = SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION
             val isFadingOut = animation.animatedFraction < switchPoint
@@ -153,6 +154,13 @@
                         startRect.right + currentTranslation,
                         startRect.bottom
                     )
+                } else {
+                    v.setLeftTopRightBottom(
+                        startRect.left,
+                        startRect.top,
+                        startRect.right,
+                        startRect.bottom
+                    )
                 }
             } else {
                 // And in again over the remaining (100-X)%.
@@ -175,32 +183,13 @@
                         endRect.right - translationRemaining,
                         endRect.bottom
                     )
-                }
-            }
-            if (animation.animatedFraction == 1.0f && shouldRestoreLayerType) {
-                v.setLayerType(View.LAYER_TYPE_NONE, /* paint= */ null)
-            }
-
-            // For views that are not the security view flipper, we do not want to apply
-            // an x translation animation. Instead, we want to fade out, move to final position and
-            // then fade in.
-            if (v !is KeyguardSecurityViewFlipper) {
-                // Opacity goes close to 0 but does not fully get to 0.
-                if (opacity - 0.001f < 0f) {
+                } else {
                     v.setLeftTopRightBottom(
                         endRect.left,
                         endRect.top,
                         endRect.right,
                         endRect.bottom
                     )
-                    finishedFadingOutNonSecurityView = true
-                } else if (!finishedFadingOutNonSecurityView) {
-                    v.setLeftTopRightBottom(
-                        startRect.left,
-                        startRect.top,
-                        startRect.right,
-                        startRect.bottom
-                    )
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
index 37829f2..a89cbf5 100644
--- a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
+++ b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
@@ -19,11 +19,11 @@
 
 @SysUISingleton
 class ChooserSelector @Inject constructor(
-        context: Context,
+        private val context: Context,
         private val featureFlags: FeatureFlags,
         @Application private val coroutineScope: CoroutineScope,
         @Background private val bgDispatcher: CoroutineDispatcher
-) : CoreStartable(context) {
+) : CoreStartable {
 
     private val packageManager = context.packageManager
     private val chooserComponent = ComponentName.unflattenFromString(
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index 0201cdc..929ebea 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -16,39 +16,41 @@
 
 package com.android.systemui;
 
-import android.content.Context;
 import android.content.res.Configuration;
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.PrintWriter;
 
 /**
- * A top-level module of system UI code (sometimes called "system UI services" elsewhere in code).
- * Which CoreStartable modules are loaded can be controlled via a config resource.
+ * Code that needs to be run when SystemUI is started.
+ *
+ * Which CoreStartable modules are loaded is controlled via the dagger graph. Bind them into the
+ * CoreStartable map with code such as:
+ *
+ *  <pre>
+ *  &#64;Binds
+ *  &#64;IntoMap
+ *  &#64;ClassKey(FoobarStartable::class)
+ *  abstract fun bind(impl: FoobarStartable): CoreStartable
+ *  </pre>
  *
  * @see SystemUIApplication#startServicesIfNeeded()
  */
-public abstract class CoreStartable implements Dumpable {
-    protected final Context mContext;
-
-    public CoreStartable(Context context) {
-        mContext = context;
-    }
+public interface CoreStartable extends Dumpable {
 
     /** Main entry point for implementations. Called shortly after app startup. */
-    public abstract void start();
+    void start();
 
-    protected void onConfigurationChanged(Configuration newConfig) {
+    /** */
+    default void onConfigurationChanged(Configuration newConfig) {
     }
 
     @Override
-    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+    default void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
     }
 
-    @VisibleForTesting
-    protected void onBootCompleted() {
+    /** Called when the device reports BOOT_COMPLETED. */
+    default void onBootCompleted() {
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 9cdce64..8f41956 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -46,7 +46,7 @@
  * system that are used for testing the latency.
  */
 @SysUISingleton
-public class LatencyTester extends CoreStartable {
+public class LatencyTester implements CoreStartable {
     private static final boolean DEFAULT_ENABLED = Build.IS_ENG;
     private static final String
             ACTION_FINGERPRINT_WAKE =
@@ -62,13 +62,11 @@
 
     @Inject
     public LatencyTester(
-            Context context,
             BiometricUnlockController biometricUnlockController,
             BroadcastDispatcher broadcastDispatcher,
             DeviceConfigProxy deviceConfigProxy,
             @Main DelayableExecutor mainExecutor
     ) {
-        super(context);
         mBiometricUnlockController = biometricUnlockController;
         mBroadcastDispatcher = broadcastDispatcher;
         mDeviceConfigProxy = deviceConfigProxy;
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 2e13903..b5f42a1 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -105,7 +105,7 @@
  * for antialiasing and emulation purposes.
  */
 @SysUISingleton
-public class ScreenDecorations extends CoreStartable implements Tunable , Dumpable {
+public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
     private static final boolean DEBUG = false;
     private static final String TAG = "ScreenDecorations";
 
@@ -130,6 +130,7 @@
     @VisibleForTesting
     protected boolean mIsRegistered;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final Context mContext;
     private final Executor mMainExecutor;
     private final TunerService mTunerService;
     private final SecureSettings mSecureSettings;
@@ -308,7 +309,7 @@
             ThreadFactory threadFactory,
             PrivacyDotDecorProviderFactory dotFactory,
             FaceScanningProviderFactory faceScanningFactory) {
-        super(context);
+        mContext = context;
         mMainExecutor = mainExecutor;
         mSecureSettings = secureSettings;
         mBroadcastDispatcher = broadcastDispatcher;
@@ -973,7 +974,7 @@
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigurationChanged(Configuration newConfig) {
         if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
             Log.i(TAG, "ScreenDecorations is disabled");
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index 1f2de4c..5bd85a7 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -38,16 +38,17 @@
  * @see SliceBroadcastRelay
  */
 @SysUISingleton
-public class SliceBroadcastRelayHandler extends CoreStartable {
+public class SliceBroadcastRelayHandler implements CoreStartable {
     private static final String TAG = "SliceBroadcastRelay";
     private static final boolean DEBUG = false;
 
     private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
+    private final Context mContext;
     private final BroadcastDispatcher mBroadcastDispatcher;
 
     @Inject
     public SliceBroadcastRelayHandler(Context context, BroadcastDispatcher broadcastDispatcher) {
-        super(context);
+        mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 9138b23..8415ef8 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -47,8 +47,6 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.NotificationChannels;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 import java.util.Comparator;
 import java.util.Map;
 import java.util.TreeMap;
@@ -296,14 +294,10 @@
         CoreStartable startable;
         if (DEBUG) Log.d(TAG, "loading: " + clsName);
         try {
-            Constructor<?> constructor = Class.forName(clsName).getConstructor(
-                    Context.class);
-            startable = (CoreStartable) constructor.newInstance(this);
+            startable = (CoreStartable) Class.forName(clsName).newInstance();
         } catch (ClassNotFoundException
-                | NoSuchMethodException
                 | IllegalAccessException
-                | InstantiationException
-                | InvocationTargetException ex) {
+                | InstantiationException ex) {
             throw new RuntimeException(ex);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/VendorServices.java b/packages/SystemUI/src/com/android/systemui/VendorServices.java
index 139448c0..a320939 100644
--- a/packages/SystemUI/src/com/android/systemui/VendorServices.java
+++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java
@@ -16,15 +16,12 @@
 
 package com.android.systemui;
 
-import android.content.Context;
-
 /**
  * Placeholder for any vendor-specific services.
  */
-public class VendorServices extends CoreStartable {
+public class VendorServices implements CoreStartable {
 
-    public VendorServices(Context context) {
-        super(context);
+    public VendorServices() {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index a1288b5..9f1c9b4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -69,7 +69,7 @@
  * Class to register system actions with accessibility framework.
  */
 @SysUISingleton
-public class SystemActions extends CoreStartable {
+public class SystemActions implements CoreStartable {
     private static final String TAG = "SystemActions";
 
     /**
@@ -177,6 +177,7 @@
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     private final SystemActionsBroadcastReceiver mReceiver;
+    private final Context mContext;
     private final Optional<Recents> mRecentsOptional;
     private Locale mLocale;
     private final AccessibilityManager mA11yManager;
@@ -190,7 +191,7 @@
             NotificationShadeWindowController notificationShadeController,
             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             Optional<Recents> recentsOptional) {
-        super(context);
+        mContext = context;
         mRecentsOptional = recentsOptional;
         mReceiver = new SystemActionsBroadcastReceiver();
         mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
@@ -219,7 +220,6 @@
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
         final Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
         if (!locale.equals(mLocale)) {
             mLocale = locale;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 6e14ddc..ab11fce 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -52,11 +52,12 @@
  * when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
  */
 @SysUISingleton
-public class WindowMagnification extends CoreStartable implements WindowMagnifierCallback,
+public class WindowMagnification implements CoreStartable, WindowMagnifierCallback,
         CommandQueue.Callbacks {
     private static final String TAG = "WindowMagnification";
 
     private final ModeSwitchesController mModeSwitchesController;
+    private final Context mContext;
     private final Handler mHandler;
     private final AccessibilityManager mAccessibilityManager;
     private final CommandQueue mCommandQueue;
@@ -102,7 +103,7 @@
     public WindowMagnification(Context context, @Main Handler mainHandler,
             CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
             SysUiState sysUiState, OverviewProxyService overviewProxyService) {
-        super(context);
+        mContext = context;
         mHandler = mainHandler;
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mCommandQueue = commandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index aae92ad..d43e5d9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -102,7 +102,7 @@
  * {@link com.android.keyguard.KeyguardUpdateMonitor}
  */
 @SysUISingleton
-public class AuthController extends CoreStartable implements CommandQueue.Callbacks,
+public class AuthController implements CoreStartable,  CommandQueue.Callbacks,
         AuthDialogCallback, DozeReceiver {
 
     private static final String TAG = "AuthController";
@@ -110,6 +110,7 @@
     private static final int SENSOR_PRIVACY_DELAY = 500;
 
     private final Handler mHandler;
+    private final Context mContext;
     private final Execution mExecution;
     private final CommandQueue mCommandQueue;
     private final StatusBarStateController mStatusBarStateController;
@@ -669,7 +670,7 @@
             @NonNull InteractionJankMonitor jankMonitor,
             @Main Handler handler,
             @Background DelayableExecutor bgExecutor) {
-        super(context);
+        mContext = context;
         mExecution = execution;
         mUserManager = userManager;
         mLockPatternUtils = lockPatternUtils;
@@ -1099,8 +1100,7 @@
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
+    public void onConfigurationChanged(Configuration newConfig) {
         updateSensorLocations();
 
         // Save the state of the current dialog (buttons showing, etc)
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcherStartable.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcherStartable.kt
index d7b263a..c536e81 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcherStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcherStartable.kt
@@ -16,16 +16,14 @@
 
 package com.android.systemui.broadcast
 
-import android.content.Context
 import com.android.systemui.CoreStartable
 import javax.inject.Inject
 
 class BroadcastDispatcherStartable @Inject constructor(
-    context: Context,
     val broadcastDispatcher: BroadcastDispatcher
-) : CoreStartable(context) {
+) : CoreStartable {
 
     override fun start() {
         broadcastDispatcher.initialize()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index f526277..05e3f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -39,8 +39,8 @@
  * ClipboardListener brings up a clipboard overlay when something is copied to the clipboard.
  */
 @SysUISingleton
-public class ClipboardListener extends CoreStartable
-        implements ClipboardManager.OnPrimaryClipChangedListener {
+public class ClipboardListener implements
+        CoreStartable, ClipboardManager.OnPrimaryClipChangedListener {
     private static final String TAG = "ClipboardListener";
 
     @VisibleForTesting
@@ -49,6 +49,7 @@
     static final String EXTRA_SUPPRESS_OVERLAY =
             "com.android.systemui.SUPPRESS_CLIPBOARD_OVERLAY";
 
+    private final Context mContext;
     private final DeviceConfigProxy mDeviceConfig;
     private final ClipboardOverlayControllerFactory mOverlayFactory;
     private final ClipboardManager mClipboardManager;
@@ -59,7 +60,7 @@
     public ClipboardListener(Context context, DeviceConfigProxy deviceConfigProxy,
             ClipboardOverlayControllerFactory overlayFactory, ClipboardManager clipboardManager,
             UiEventLogger uiEventLogger) {
-        super(context);
+        mContext = context;
         mDeviceConfig = deviceConfigProxy;
         mOverlayFactory = overlayFactory;
         mClipboardManager = clipboardManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index 99ca3c7..d145f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -40,11 +40,12 @@
  * {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
  * the designated dream overlay component.
  */
-public class DreamOverlayRegistrant extends CoreStartable {
+public class DreamOverlayRegistrant implements CoreStartable {
     private static final String TAG = "DreamOverlayRegistrant";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private final IDreamManager mDreamManager;
     private final ComponentName mOverlayServiceComponent;
+    private final Context mContext;
     private final Resources mResources;
     private boolean mCurrentRegisteredState = false;
 
@@ -98,7 +99,7 @@
 
     @Inject
     public DreamOverlayRegistrant(Context context, @Main Resources resources) {
-        super(context);
+        mContext = context;
         mResources = resources;
         mDreamManager = IDreamManager.Stub.asInterface(
                 ServiceManager.getService(DreamService.DREAM_SERVICE));
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
index bbcab60..ee2f1af 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.dreams.complication;
 
-import android.content.Context;
 import android.database.ContentObserver;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -37,7 +36,7 @@
  * user, and pushes updates to {@link DreamOverlayStateController}.
  */
 @SysUISingleton
-public class ComplicationTypesUpdater extends CoreStartable {
+public class ComplicationTypesUpdater implements CoreStartable {
     private final DreamBackend mDreamBackend;
     private final Executor mExecutor;
     private final SecureSettings mSecureSettings;
@@ -45,13 +44,11 @@
     private final DreamOverlayStateController mDreamOverlayStateController;
 
     @Inject
-    ComplicationTypesUpdater(Context context,
+    ComplicationTypesUpdater(
             DreamBackend dreamBackend,
             @Main Executor executor,
             SecureSettings secureSettings,
             DreamOverlayStateController dreamOverlayStateController) {
-        super(context);
-
         mDreamBackend = dreamBackend;
         mExecutor = executor;
         mSecureSettings = secureSettings;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
index 675a2f4..77e1fc9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
@@ -19,7 +19,6 @@
 import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
 import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS;
 
-import android.content.Context;
 import android.view.View;
 
 import com.android.systemui.CoreStartable;
@@ -61,7 +60,7 @@
      * {@link CoreStartable} responsible for registering {@link DreamClockTimeComplication} with
      * SystemUI.
      */
-    public static class Registrant extends CoreStartable {
+    public static class Registrant implements CoreStartable {
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final DreamClockTimeComplication mComplication;
 
@@ -69,10 +68,9 @@
          * Default constructor to register {@link DreamClockTimeComplication}.
          */
         @Inject
-        public Registrant(Context context,
+        public Registrant(
                 DreamOverlayStateController dreamOverlayStateController,
                 DreamClockTimeComplication dreamClockTimeComplication) {
-            super(context);
             mDreamOverlayStateController = dreamOverlayStateController;
             mComplication = dreamClockTimeComplication;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index 821e13e..0ccb222 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -71,7 +71,7 @@
     /**
      * {@link CoreStartable} for registering the complication with SystemUI on startup.
      */
-    public static class Registrant extends CoreStartable {
+    public static class Registrant implements CoreStartable {
         private final DreamHomeControlsComplication mComplication;
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final ControlsComponent mControlsComponent;
@@ -90,11 +90,9 @@
                 };
 
         @Inject
-        public Registrant(Context context, DreamHomeControlsComplication complication,
+        public Registrant(DreamHomeControlsComplication complication,
                 DreamOverlayStateController dreamOverlayStateController,
                 ControlsComponent controlsComponent) {
-            super(context);
-
             mComplication = complication;
             mControlsComponent = controlsComponent;
             mDreamOverlayStateController = dreamOverlayStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index a981f25..c3aaf0c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -61,7 +61,7 @@
      * {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
      * SystemUI.
      */
-    public static class Registrant extends CoreStartable {
+    public static class Registrant implements CoreStartable {
         private final DreamSmartspaceController mSmartSpaceController;
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final SmartSpaceComplication mComplication;
@@ -78,11 +78,10 @@
          * Default constructor for {@link SmartSpaceComplication}.
          */
         @Inject
-        public Registrant(Context context,
+        public Registrant(
                 DreamOverlayStateController dreamOverlayStateController,
                 SmartSpaceComplication smartSpaceComplication,
                 DreamSmartspaceController smartSpaceController) {
-            super(context);
             mDreamOverlayStateController = dreamOverlayStateController;
             mComplication = smartSpaceComplication;
             mSmartSpaceController = smartSpaceController;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
index c0e3021..560dcbd 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
@@ -16,9 +16,7 @@
 
 package com.android.systemui.flags
 
-import android.content.Context
 import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import dagger.Binds
@@ -30,12 +28,11 @@
 class FeatureFlagsDebugStartable
 @Inject
 constructor(
-    @Application context: Context,
     dumpManager: DumpManager,
     private val commandRegistry: CommandRegistry,
     private val flagCommand: FlagCommand,
     featureFlags: FeatureFlags
-) : CoreStartable(context) {
+) : CoreStartable {
 
     init {
         dumpManager.registerDumpable(FeatureFlagsDebug.TAG) { pw, args ->
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
index f138f1e..e7d8cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
@@ -16,9 +16,7 @@
 
 package com.android.systemui.flags
 
-import android.content.Context
 import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
 import dagger.Binds
 import dagger.Module
@@ -28,8 +26,7 @@
 
 class FeatureFlagsReleaseStartable
 @Inject
-constructor(@Application context: Context, dumpManager: DumpManager, featureFlags: FeatureFlags) :
-    CoreStartable(context) {
+constructor(dumpManager: DumpManager, featureFlags: FeatureFlags) : CoreStartable {
 
     init {
         dumpManager.registerDumpable(FeatureFlagsRelease.TAG) { pw, args ->
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 3868b6f..72e0ec1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -105,10 +105,6 @@
      */
     public static final UnreleasedFlag MODERN_BOUNCER = new UnreleasedFlag(208);
 
-    /** Whether UserSwitcherActivity should use modern architecture. */
-    public static final ReleasedFlag MODERN_USER_SWITCHER_ACTIVITY =
-            new ReleasedFlag(209, true);
-
     /**
      * Whether the user interactor and repository should use `UserSwitcherController`.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index 74d5bd5..9f321d8 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -36,8 +36,7 @@
  * Manages power menu plugins and communicates power menu actions to the CentralSurfaces.
  */
 @SysUISingleton
-public class GlobalActionsComponent extends CoreStartable
-        implements Callbacks, GlobalActionsManager {
+public class GlobalActionsComponent implements CoreStartable, Callbacks, GlobalActionsManager {
 
     private final CommandQueue mCommandQueue;
     private final ExtensionController mExtensionController;
@@ -48,11 +47,10 @@
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     @Inject
-    public GlobalActionsComponent(Context context, CommandQueue commandQueue,
+    public GlobalActionsComponent(CommandQueue commandQueue,
             ExtensionController extensionController,
             Provider<GlobalActions> globalActionsProvider,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
-        super(context);
         mCommandQueue = commandQueue;
         mExtensionController = extensionController;
         mGlobalActionsProvider = globalActionsProvider;
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 568143c..4f1a2b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -27,7 +27,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.res.Configuration;
 import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -66,7 +65,7 @@
 
 /** */
 @SysUISingleton
-public class KeyboardUI extends CoreStartable implements InputManager.OnTabletModeChangedListener {
+public class KeyboardUI implements CoreStartable, InputManager.OnTabletModeChangedListener {
     private static final String TAG = "KeyboardUI";
     private static final boolean DEBUG = false;
 
@@ -127,13 +126,12 @@
 
     @Inject
     public KeyboardUI(Context context, Provider<LocalBluetoothManager> bluetoothManagerProvider) {
-        super(context);
+        mContext = context;
         this.mBluetoothManagerProvider = bluetoothManagerProvider;
     }
 
     @Override
     public void start() {
-        mContext = super.mContext;
         HandlerThread thread = new HandlerThread("Keyboard", Process.THREAD_PRIORITY_BACKGROUND);
         thread.start();
         mHandler = new KeyboardHandler(thread.getLooper());
@@ -141,10 +139,6 @@
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-    }
-
-    @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.println("KeyboardUI:");
         pw.println("  mEnabled=" + mEnabled);
@@ -156,7 +150,7 @@
     }
 
     @Override
-    protected void onBootCompleted() {
+    public void onBootCompleted() {
         mHandler.sendEmptyMessage(MSG_ON_BOOT_COMPLETED);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9a118e0..9b036c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -186,7 +186,7 @@
  * directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
  * thread of the keyguard.
  */
-public class KeyguardViewMediator extends CoreStartable implements Dumpable,
+public class KeyguardViewMediator implements CoreStartable, Dumpable,
         StatusBarStateController.StateListener {
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
@@ -272,6 +272,7 @@
     private boolean mShuttingDown;
     private boolean mDozing;
     private boolean mAnimatingScreenOff;
+    private final Context mContext;
     private final FalsingCollector mFalsingCollector;
 
     /** High level access to the power manager for WakeLocks */
@@ -1128,7 +1129,7 @@
             DreamOverlayStateController dreamOverlayStateController,
             Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
             Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
-        super(context);
+        mContext = context;
         mFalsingCollector = falsingCollector;
         mLockPatternUtils = lockPatternUtils;
         mBroadcastDispatcher = broadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 01cd3e2..f663b0d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -19,7 +19,7 @@
 
 import android.content.Intent
 import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
@@ -67,19 +67,19 @@
      *
      * @param configKey The configuration key corresponding to the [KeyguardQuickAffordanceModel] of
      * the affordance that was clicked
-     * @param animationController An optional controller for the activity-launch animation
+     * @param expandable An optional [Expandable] for the activity- or dialog-launch animation
      */
     fun onQuickAffordanceClicked(
         configKey: KClass<out KeyguardQuickAffordanceConfig>,
-        animationController: ActivityLaunchAnimator.Controller?,
+        expandable: Expandable?,
     ) {
         @Suppress("UNCHECKED_CAST") val config = registry.get(configKey as KClass<Nothing>)
-        when (val result = config.onQuickAffordanceClicked(animationController)) {
+        when (val result = config.onQuickAffordanceClicked(expandable)) {
             is KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity ->
                 launchQuickAffordance(
                     intent = result.intent,
                     canShowWhileLocked = result.canShowWhileLocked,
-                    animationController = animationController
+                    expandable = expandable,
                 )
             is KeyguardQuickAffordanceConfig.OnClickedResult.Handled -> Unit
         }
@@ -104,6 +104,7 @@
                 KeyguardQuickAffordanceModel.Visible(
                     configKey = configs[index]::class,
                     icon = visibleState.icon,
+                    toggle = visibleState.toggle,
                 )
             } else {
                 KeyguardQuickAffordanceModel.Hidden
@@ -114,7 +115,7 @@
     private fun launchQuickAffordance(
         intent: Intent,
         canShowWhileLocked: Boolean,
-        animationController: ActivityLaunchAnimator.Controller?,
+        expandable: Expandable?,
     ) {
         @LockPatternUtils.StrongAuthTracker.StrongAuthFlags
         val strongAuthFlags =
@@ -130,13 +131,13 @@
             activityStarter.postStartActivityDismissingKeyguard(
                 intent,
                 0 /* delay */,
-                animationController
+                expandable?.activityLaunchController(),
             )
         } else {
             activityStarter.startActivity(
                 intent,
                 true /* dismissShade */,
-                animationController,
+                expandable?.activityLaunchController(),
                 true /* showOverLockscreenWhenLocked */,
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
index eb6bb92..e56b259 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
@@ -19,6 +19,7 @@
 
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
 import kotlin.reflect.KClass
 
 /**
@@ -35,5 +36,7 @@
         val configKey: KClass<out KeyguardQuickAffordanceConfig>,
         /** An icon for the affordance. */
         val icon: Icon,
+        /** The toggle state for the affordance. */
+        val toggle: KeyguardQuickAffordanceToggleState,
     ) : KeyguardQuickAffordanceModel()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 89604f0..8384260 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -20,7 +20,7 @@
 import android.content.Context
 import android.content.Intent
 import androidx.annotation.DrawableRes
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
@@ -61,7 +61,7 @@
         }
 
     override fun onQuickAffordanceClicked(
-        animationController: ActivityLaunchAnimator.Controller?,
+        expandable: Expandable?,
     ): KeyguardQuickAffordanceConfig.OnClickedResult {
         return KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
             intent =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 8e1c6b7..95027d0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -18,8 +18,9 @@
 package com.android.systemui.keyguard.domain.quickaffordance
 
 import android.content.Intent
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
 import kotlinx.coroutines.flow.Flow
 
 /** Defines interface that can act as data source for a single quick affordance model. */
@@ -27,9 +28,7 @@
 
     val state: Flow<State>
 
-    fun onQuickAffordanceClicked(
-        animationController: ActivityLaunchAnimator.Controller?
-    ): OnClickedResult
+    fun onQuickAffordanceClicked(expandable: Expandable?): OnClickedResult
 
     /**
      * Encapsulates the state of a "quick affordance" in the keyguard bottom area (for example, a
@@ -44,6 +43,9 @@
         data class Visible(
             /** An icon for the affordance. */
             val icon: Icon,
+            /** The toggle state for the affordance. */
+            val toggle: KeyguardQuickAffordanceToggleState =
+                KeyguardQuickAffordanceToggleState.NotSupported,
         ) : State()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index d97deaf..502a607 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -18,7 +18,7 @@
 package com.android.systemui.keyguard.domain.quickaffordance
 
 import com.android.systemui.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
@@ -66,7 +66,7 @@
     }
 
     override fun onQuickAffordanceClicked(
-        animationController: ActivityLaunchAnimator.Controller?,
+        expandable: Expandable?,
     ): KeyguardQuickAffordanceConfig.OnClickedResult {
         return KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
             intent = controller.intent,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 9196b09..a24a0d6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -23,7 +23,7 @@
 import android.service.quickaccesswallet.QuickAccessWalletClient
 import android.util.Log
 import com.android.systemui.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
@@ -84,11 +84,11 @@
     }
 
     override fun onQuickAffordanceClicked(
-        animationController: ActivityLaunchAnimator.Controller?,
+        expandable: Expandable?,
     ): KeyguardQuickAffordanceConfig.OnClickedResult {
         walletController.startQuickAccessUiIntent(
             activityStarter,
-            animationController,
+            expandable?.activityLaunchController(),
             /* hasCard= */ true,
         )
         return KeyguardQuickAffordanceConfig.OnClickedResult.Handled
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordanceToggleState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordanceToggleState.kt
new file mode 100644
index 0000000..55d38a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordanceToggleState.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.keyguard.shared.quickaffordance
+
+/** Enumerates all possible toggle states for a quick affordance on the lock-screen. */
+sealed class KeyguardQuickAffordanceToggleState {
+    /** Toggling is not supported. */
+    object NotSupported : KeyguardQuickAffordanceToggleState()
+    /** The quick affordance is on. */
+    object On : KeyguardQuickAffordanceToggleState()
+    /** The quick affordance is off. */
+    object Off : KeyguardQuickAffordanceToggleState()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 65b85c0..2c99ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -29,7 +29,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.settingslib.Utils
 import com.android.systemui.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
@@ -238,14 +238,26 @@
 
         IconViewBinder.bind(viewModel.icon, view)
 
+        view.isActivated = viewModel.isActivated
         view.drawable.setTint(
             Utils.getColorAttrDefaultColor(
                 view.context,
-                com.android.internal.R.attr.textColorPrimary
+                if (viewModel.isActivated) {
+                    com.android.internal.R.attr.textColorPrimaryInverse
+                } else {
+                    com.android.internal.R.attr.textColorPrimary
+                },
             )
         )
         view.backgroundTintList =
-            Utils.getColorAttr(view.context, com.android.internal.R.attr.colorSurface)
+            Utils.getColorAttr(
+                view.context,
+                if (viewModel.isActivated) {
+                    com.android.internal.R.attr.colorAccentPrimary
+                } else {
+                    com.android.internal.R.attr.colorSurface
+                }
+            )
 
         view.isClickable = viewModel.isClickable
         if (viewModel.isClickable) {
@@ -268,7 +280,7 @@
                 viewModel.onClicked(
                     KeyguardQuickAffordanceViewModel.OnClickedParameters(
                         configKey = viewModel.configKey,
-                        animationController = ActivityLaunchAnimator.Controller.fromView(view),
+                        expandable = Expandable.fromView(view),
                     )
                 )
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 970ee4c..535ca72 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -119,10 +120,11 @@
                     onClicked = { parameters ->
                         quickAffordanceInteractor.onQuickAffordanceClicked(
                             configKey = parameters.configKey,
-                            animationController = parameters.animationController,
+                            expandable = parameters.expandable,
                         )
                     },
                     isClickable = isClickable,
+                    isActivated = toggle is KeyguardQuickAffordanceToggleState.On,
                 )
             is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index 0971f13..bf598ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
 import kotlin.reflect.KClass
@@ -30,9 +30,10 @@
     val icon: Icon = Icon.Resource(res = 0, contentDescription = null),
     val onClicked: (OnClickedParameters) -> Unit = {},
     val isClickable: Boolean = false,
+    val isActivated: Boolean = false,
 ) {
     data class OnClickedParameters(
         val configKey: KClass<out KeyguardQuickAffordanceConfig>,
-        val animationController: ActivityLaunchAnimator.Controller?,
+        val expandable: Expandable?,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java b/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
index 8f9357a..c7e4c5e 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
@@ -21,7 +21,6 @@
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
 
 import android.annotation.Nullable;
-import android.content.Context;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -48,7 +47,7 @@
  * session. Can be used across processes via StatusBarManagerService#registerSessionListener
  */
 @SysUISingleton
-public class SessionTracker extends CoreStartable {
+public class SessionTracker implements CoreStartable {
     private static final String TAG = "SessionTracker";
     private static final boolean DEBUG = false;
 
@@ -65,13 +64,11 @@
 
     @Inject
     public SessionTracker(
-            Context context,
             IStatusBarService statusBarService,
             AuthController authController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             KeyguardStateController keyguardStateController
     ) {
-        super(context);
         mStatusBarManagerService = statusBarService;
         mAuthController = authController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 529d066..1b81e88 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -344,4 +344,14 @@
     public static LogBuffer provideKeyguardLogBuffer(LogBufferFactory factory) {
         return factory.create("KeyguardLog", 250);
     }
+
+    /**
+     * Provides a {@link LogBuffer} for Udfps logs.
+     */
+    @Provides
+    @SysUISingleton
+    @UdfpsLog
+    public static LogBuffer provideUdfpsLogBuffer(LogBufferFactory factory) {
+        return factory.create("UdfpsLog", 1000);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/UdfpsLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/UdfpsLog.java
new file mode 100644
index 0000000..14000e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/UdfpsLog.java
@@ -0,0 +1,30 @@
+/*
+ * 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.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface UdfpsLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 0b9b32b..2a8168b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -51,9 +51,10 @@
  * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
  */
 @SysUISingleton
-public class RingtonePlayer extends CoreStartable {
+public class RingtonePlayer implements CoreStartable {
     private static final String TAG = "RingtonePlayer";
     private static final boolean LOGD = false;
+    private final Context mContext;
 
     // TODO: support Uri switching under same IBinder
 
@@ -64,7 +65,7 @@
 
     @Inject
     public RingtonePlayer(Context context) {
-        super(context);
+        mContext = context;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index 53b4d43..91e7b49 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.flags.Flags.DREAM_MEDIA_COMPLICATION;
 
-import android.content.Context;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -38,7 +37,7 @@
  * {@link MediaDreamSentinel} is responsible for tracking media state and registering/unregistering
  * the media complication as appropriate
  */
-public class MediaDreamSentinel extends CoreStartable {
+public class MediaDreamSentinel implements CoreStartable {
     private static final String TAG = "MediaDreamSentinel";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -113,11 +112,10 @@
     private final FeatureFlags mFeatureFlags;
 
     @Inject
-    public MediaDreamSentinel(Context context, MediaDataManager mediaDataManager,
+    public MediaDreamSentinel(MediaDataManager mediaDataManager,
             DreamOverlayStateController dreamOverlayStateController,
             DreamMediaEntryComplication mediaEntryComplication,
             FeatureFlags featureFlags) {
-        super(context);
         mMediaDataManager = mediaDataManager;
         mDreamOverlayStateController = dreamOverlayStateController;
         mMediaEntryComplication = mediaEntryComplication;
diff --git a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
index d60172a..0ba5f28 100644
--- a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
@@ -40,7 +40,7 @@
  * documented at {@link #handleTaskStackChanged} apply.
  */
 @SysUISingleton
-public class HomeSoundEffectController extends CoreStartable {
+public class HomeSoundEffectController implements CoreStartable {
 
     private static final String TAG = "HomeSoundEffectController";
     private final AudioManager mAudioManager;
@@ -65,7 +65,6 @@
             TaskStackChangeListeners taskStackChangeListeners,
             ActivityManagerWrapper activityManagerWrapper,
             PackageManager packageManager) {
-        super(context);
         mAudioManager = audioManager;
         mTaskStackChangeListeners = taskStackChangeListeners;
         mActivityManagerWrapper = activityManagerWrapper;
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index f5caefb..a4a96806 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -43,7 +43,7 @@
     private val commandRegistry: CommandRegistry,
     private val context: Context,
     @Main private val mainExecutor: Executor
-) : CoreStartable(context) {
+) : CoreStartable {
 
     /** All commands for the sender device. */
     inner class SenderCommand : Command {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 50a10bc..6da8c69 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -114,6 +114,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener;
@@ -211,6 +212,7 @@
     private final NotificationShadeDepthController mNotificationShadeDepthController;
     private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
     private final UserContextProvider mUserContextProvider;
+    private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final RegionSamplingHelper mRegionSamplingHelper;
     private final int mNavColorSampleMargin;
     private NavigationBarFrame mFrame;
@@ -451,6 +453,28 @@
                 }
             };
 
+    private final WakefulnessLifecycle.Observer mWakefulnessObserver =
+            new WakefulnessLifecycle.Observer() {
+                private void notifyScreenStateChanged(boolean isScreenOn) {
+                    notifyNavigationBarScreenOn();
+                    mView.onScreenStateChanged(isScreenOn);
+                }
+
+                @Override
+                public void onStartedWakingUp() {
+                    notifyScreenStateChanged(true);
+                    if (isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode)) {
+                        mRegionSamplingHelper.start(mSamplingBounds);
+                    }
+                }
+
+                @Override
+                public void onFinishedGoingToSleep() {
+                    notifyScreenStateChanged(false);
+                    mRegionSamplingHelper.stop();
+                }
+            };
+
     @Inject
     NavigationBar(
             NavigationBarView navigationBarView,
@@ -491,7 +515,8 @@
             NavigationBarTransitions navigationBarTransitions,
             EdgeBackGestureHandler edgeBackGestureHandler,
             Optional<BackAnimation> backAnimation,
-            UserContextProvider userContextProvider) {
+            UserContextProvider userContextProvider,
+            WakefulnessLifecycle wakefulnessLifecycle) {
         super(navigationBarView);
         mFrame = navigationBarFrame;
         mContext = context;
@@ -529,6 +554,7 @@
         mTelecomManagerOptional = telecomManagerOptional;
         mInputMethodManager = inputMethodManager;
         mUserContextProvider = userContextProvider;
+        mWakefulnessLifecycle = wakefulnessLifecycle;
 
         mNavColorSampleMargin = getResources()
                 .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
@@ -682,11 +708,10 @@
         prepareNavigationBarView();
         checkNavBarModes();
 
-        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
         mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
                 Handler.getMain(), UserHandle.ALL);
+        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
         notifyNavigationBarScreenOn();
 
         mOverviewProxyService.addCallback(mOverviewProxyListener);
@@ -737,6 +762,7 @@
         getBarTransitions().destroy();
         mOverviewProxyService.removeCallback(mOverviewProxyListener);
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+        mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
         if (mOrientationHandle != null) {
             resetSecondaryHandle();
             getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
@@ -1619,19 +1645,6 @@
                 return;
             }
             String action = intent.getAction();
-            if (Intent.ACTION_SCREEN_OFF.equals(action)
-                    || Intent.ACTION_SCREEN_ON.equals(action)) {
-                notifyNavigationBarScreenOn();
-                boolean isScreenOn = Intent.ACTION_SCREEN_ON.equals(action);
-                mView.onScreenStateChanged(isScreenOn);
-                if (isScreenOn) {
-                    if (isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode)) {
-                        mRegionSamplingHelper.start(mSamplingBounds);
-                    }
-                } else {
-                    mRegionSamplingHelper.stop();
-                }
-            }
             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 // The accessibility settings may be different for the new user
                 updateAccessibilityStateFlags();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 029cf68..3fd1aa7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -21,6 +21,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
 import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
 
 import android.content.ContentResolver;
@@ -141,7 +142,13 @@
     public void onConfigChanged(Configuration newConfig) {
         boolean isOldConfigTablet = mIsTablet;
         mIsTablet = isTablet(mContext);
+        boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
         boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
+        // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
+        Log.d(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+                + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
+                + " willApplyConfigToNavbars=" + willApplyConfig
+                + " navBarCount=" + mNavigationBars.size());
         if (mTaskbarDelegate.isInitialized()) {
             mTaskbarDelegate.onConfigurationChanged(newConfig);
         }
@@ -150,7 +157,7 @@
             return;
         }
 
-        if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+        if (willApplyConfig) {
             for (int i = 0; i < mNavigationBars.size(); i++) {
                 recreateNavigationBar(mNavigationBars.keyAt(i));
             }
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 a8799c7..709467f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -113,7 +113,7 @@
     private static final int MAX_NUM_LOGGED_GESTURES = 10;
 
     static final boolean DEBUG_MISSING_GESTURE = false;
-    static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
+    public static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
 
     private ISystemGestureExclusionListener mGestureExclusionListener =
             new ISystemGestureExclusionListener.Stub() {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 67dae9e..1da866e 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -46,6 +46,7 @@
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -59,7 +60,7 @@
 import dagger.Lazy;
 
 @SysUISingleton
-public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
+public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
 
     static final String TAG = "PowerUI";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -78,6 +79,7 @@
 
     private final PowerManager mPowerManager;
     private final WarningsUI mWarnings;
+    private final WakefulnessLifecycle mWakefulnessLifecycle;
     private InattentiveSleepWarningView mOverlayView;
     private final Configuration mLastConfiguration = new Configuration();
     private int mPlugType = 0;
@@ -103,22 +105,37 @@
 
     private IThermalEventListener mSkinThermalEventListener;
     private IThermalEventListener mUsbThermalEventListener;
+    private final Context mContext;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final CommandQueue mCommandQueue;
     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+    private final WakefulnessLifecycle.Observer mWakefulnessObserver =
+            new WakefulnessLifecycle.Observer() {
+                @Override
+                public void onStartedWakingUp() {
+                    mScreenOffTime = -1;
+                }
+
+                @Override
+                public void onFinishedGoingToSleep() {
+                    mScreenOffTime = SystemClock.elapsedRealtime();
+                }
+            };
 
     @Inject
     public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
             CommandQueue commandQueue, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
             WarningsUI warningsUI, EnhancedEstimates enhancedEstimates,
+            WakefulnessLifecycle wakefulnessLifecycle,
             PowerManager powerManager) {
-        super(context);
+        mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
         mCommandQueue = commandQueue;
         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
         mWarnings = warningsUI;
         mEnhancedEstimates = enhancedEstimates;
         mPowerManager = powerManager;
+        mWakefulnessLifecycle = wakefulnessLifecycle;
     }
 
     public void start() {
@@ -137,6 +154,7 @@
                 false, obs, UserHandle.USER_ALL);
         updateBatteryWarningLevels();
         mReceiver.init();
+        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
 
         // Check to see if we need to let the user know that the phone previously shut down due
         // to the temperature being too high.
@@ -169,7 +187,7 @@
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigurationChanged(Configuration newConfig) {
         final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
 
         // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
@@ -232,8 +250,6 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-            filter.addAction(Intent.ACTION_SCREEN_OFF);
-            filter.addAction(Intent.ACTION_SCREEN_ON);
             filter.addAction(Intent.ACTION_USER_SWITCHED);
             mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
             // Force get initial values. Relying on Sticky behavior until API for getting info.
@@ -316,10 +332,6 @@
                             plugged, bucket);
                 });
 
-            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                mScreenOffTime = SystemClock.elapsedRealtime();
-            } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
-                mScreenOffTime = -1;
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 mWarnings.userSwitched();
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 5510eb1..cd32a10 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -67,7 +67,7 @@
  * recording audio, accessing the camera or accessing the location.
  */
 @SysUISingleton
-public class TvOngoingPrivacyChip extends CoreStartable implements PrivacyItemController.Callback,
+public class TvOngoingPrivacyChip implements CoreStartable, PrivacyItemController.Callback,
         PrivacyChipDrawable.PrivacyChipDrawableListener {
     private static final String TAG = "TvOngoingPrivacyChip";
     private static final boolean DEBUG = false;
@@ -134,7 +134,6 @@
     @Inject
     public TvOngoingPrivacyChip(Context context, PrivacyItemController privacyItemController,
             IWindowManager iWindowManager) {
-        super(context);
         if (DEBUG) Log.d(TAG, "Privacy chip running");
         mContext = context;
         mPrivacyItemController = privacyItemController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 11d9555..d3c06f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -23,7 +23,6 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import com.android.settingslib.Utils
-import com.android.settingslib.drawable.UserIconDrawable
 import com.android.systemui.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
@@ -250,22 +249,19 @@
         status: UserSwitcherStatusModel.Enabled
     ): FooterActionsButtonViewModel {
         val icon = status.currentUserImage!!
-        val iconTint =
-            if (status.isGuestUser && icon !is UserIconDrawable) {
-                Utils.getColorAttrDefaultColor(context, android.R.attr.colorForeground)
-            } else {
-                null
-            }
 
         return FooterActionsButtonViewModel(
             id = R.id.multi_user_switch,
-            Icon.Loaded(
-                icon,
-                ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
-            ),
-            iconTint,
-            R.drawable.qs_footer_action_circle,
-            this::onUserSwitcherClicked,
+            icon =
+                Icon.Loaded(
+                    icon,
+                    ContentDescription.Loaded(
+                        userSwitcherContentDescription(status.currentUserName)
+                    ),
+                ),
+            iconTint = null,
+            background = R.drawable.qs_footer_action_circle,
+            onClick = this::onUserSwitcherClicked,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 9b3b843..b041f95 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -29,13 +29,14 @@
 /**
  * A proxy to a Recents implementation.
  */
-public class Recents extends CoreStartable implements CommandQueue.Callbacks {
+public class Recents implements CoreStartable, CommandQueue.Callbacks {
 
+    private final Context mContext;
     private final RecentsImplementation mImpl;
     private final CommandQueue mCommandQueue;
 
     public Recents(Context context, RecentsImplementation impl, CommandQueue commandQueue) {
-        super(context);
+        mContext = context;
         mImpl = impl;
         mCommandQueue = commandQueue;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
index 950806d..ead3b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
@@ -49,7 +49,6 @@
     private final SwipeDismissHandler mSwipeDismissHandler;
     private final GestureDetector mSwipeDetector;
     private View mActionsContainer;
-    private View mActionsContainerBackground;
     private SwipeDismissCallbacks mCallbacks;
     private final DisplayMetrics mDisplayMetrics;
 
@@ -111,6 +110,9 @@
                     }
                 });
         mSwipeDetector.setIsLongpressEnabled(false);
+
+        mCallbacks = new SwipeDismissCallbacks() {
+        }; // default to unimplemented callbacks
     }
 
     public void setCallbacks(SwipeDismissCallbacks callbacks) {
@@ -119,16 +121,13 @@
 
     @Override
     public boolean onInterceptHoverEvent(MotionEvent event) {
-        if (mCallbacks != null) {
-            mCallbacks.onInteraction();
-        }
+        mCallbacks.onInteraction();
         return super.onInterceptHoverEvent(event);
     }
 
     @Override // View
     protected void onFinishInflate() {
         mActionsContainer = findViewById(R.id.actions_container);
-        mActionsContainerBackground = findViewById(R.id.actions_container_background);
     }
 
     @Override
@@ -186,6 +185,13 @@
         inoutInfo.touchableRegion.set(r);
     }
 
+    private int getBackgroundRight() {
+        // background expected to be null in testing.
+        // animation may have unexpected behavior if view is not present
+        View background = findViewById(R.id.actions_container_background);
+        return background == null ? 0 : background.getRight();
+    }
+
     /**
      * Allows a view to be swipe-dismissed, or returned to its location if distance threshold is not
      * met
@@ -213,8 +219,6 @@
             mGestureDetector = new GestureDetector(context, gestureListener);
             mDisplayMetrics = new DisplayMetrics();
             context.getDisplay().getRealMetrics(mDisplayMetrics);
-            mCallbacks = new SwipeDismissCallbacks() {
-            }; // default to unimplemented callbacks
         }
 
         @Override
@@ -230,7 +234,9 @@
                     return true;
                 }
                 if (isPastDismissThreshold()) {
-                    dismiss();
+                    ValueAnimator anim = createSwipeDismissAnimation();
+                    mCallbacks.onSwipeDismissInitiated(anim);
+                    dismiss(anim);
                 } else {
                     // if we've moved, but not past the threshold, start the return animation
                     if (DEBUG_DISMISS) {
@@ -295,10 +301,7 @@
         }
 
         void dismiss() {
-            float velocityPxPerMs = FloatingWindowUtil.dpToPx(mDisplayMetrics, VELOCITY_DP_PER_MS);
-            ValueAnimator anim = createSwipeDismissAnimation(velocityPxPerMs);
-            mCallbacks.onSwipeDismissInitiated(anim);
-            dismiss(anim);
+            dismiss(createSwipeDismissAnimation());
         }
 
         private void dismiss(ValueAnimator animator) {
@@ -323,6 +326,11 @@
             mDismissAnimation.start();
         }
 
+        private ValueAnimator createSwipeDismissAnimation() {
+            float velocityPxPerMs = FloatingWindowUtil.dpToPx(mDisplayMetrics, VELOCITY_DP_PER_MS);
+            return createSwipeDismissAnimation(velocityPxPerMs);
+        }
+
         private ValueAnimator createSwipeDismissAnimation(float velocity) {
             // velocity is measured in pixels per millisecond
             velocity = Math.min(3, Math.max(1, velocity));
@@ -337,7 +345,7 @@
             if (startX > 0 || (startX == 0 && layoutDir == LAYOUT_DIRECTION_RTL)) {
                 finalX = mDisplayMetrics.widthPixels;
             } else {
-                finalX = -1 * mActionsContainerBackground.getRight();
+                finalX = -1 * getBackgroundRight();
             }
             float distance = Math.abs(finalX - startX);
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 1c4af1d..231e415 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -28,6 +28,7 @@
 import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW;
 import static com.android.systemui.screenshot.LogConfig.logTag;
 import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT;
 
 import static java.util.Objects.requireNonNull;
 
@@ -334,9 +335,7 @@
             if (DEBUG_UI) {
                 Log.d(TAG, "Corner timeout hit");
             }
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT, 0,
-                    mPackageName);
-            ScreenshotController.this.dismissScreenshot(false);
+            dismissScreenshot(SCREENSHOT_INTERACTION_TIMEOUT);
         });
 
         mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
@@ -365,8 +364,7 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (ClipboardOverlayController.COPY_OVERLAY_ACTION.equals(intent.getAction())) {
-                    mUiEventLogger.log(SCREENSHOT_DISMISSED_OTHER);
-                    dismissScreenshot(false);
+                    dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
                 }
             }
         };
@@ -414,24 +412,20 @@
     /**
      * Clears current screenshot
      */
-    void dismissScreenshot(boolean immediate) {
+    void dismissScreenshot(ScreenshotEvent event) {
         if (DEBUG_DISMISS) {
-            Log.d(TAG, "dismissScreenshot(immediate=" + immediate + ")");
+            Log.d(TAG, "dismissScreenshot");
         }
         // If we're already animating out, don't restart the animation
-        // (but do obey an immediate dismissal)
-        if (!immediate && mScreenshotView.isDismissing()) {
+        if (mScreenshotView.isDismissing()) {
             if (DEBUG_DISMISS) {
                 Log.v(TAG, "Already dismissing, ignoring duplicate command");
             }
             return;
         }
+        mUiEventLogger.log(event, 0, mPackageName);
         mScreenshotHandler.cancelTimeout();
-        if (immediate) {
-            finishDismiss();
-        } else {
-            mScreenshotView.animateDismissal();
-        }
+        mScreenshotView.animateDismissal();
     }
 
     boolean isPendingSharedTransition() {
@@ -504,7 +498,7 @@
                 if (DEBUG_INPUT) {
                     Log.d(TAG, "onKeyEvent: KeyEvent.KEYCODE_BACK");
                 }
-                dismissScreenshot(false);
+                dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
                 return true;
             }
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index a4a59ce..2176825 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -89,8 +89,7 @@
                     Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
                 }
                 if (!mScreenshot.isPendingSharedTransition()) {
-                    mUiEventLogger.log(SCREENSHOT_DISMISSED_OTHER);
-                    mScreenshot.dismissScreenshot(false);
+                    mScreenshot.dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
index ad073c0..d450afa 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -42,11 +42,11 @@
 @SysUISingleton
 class UserFileManagerImpl @Inject constructor(
     // Context of system process and system user.
-    val context: Context,
+    private val context: Context,
     val userManager: UserManager,
     val broadcastDispatcher: BroadcastDispatcher,
     @Background val backgroundExecutor: DelayableExecutor
-) : UserFileManager, CoreStartable(context) {
+) : UserFileManager, CoreStartable {
     companion object {
         private const val FILES = "files"
         @VisibleForTesting internal const val SHARED_PREFS = "shared_prefs"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e331812..200579a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -6051,6 +6051,10 @@
                 mShadeLog.logMotionEvent(event, "onTouch: PulseExpansionHandler handled event");
                 return true;
             }
+            if (mPulsing) {
+                mShadeLog.logMotionEvent(event, "onTouch: eat touch, device pulsing");
+                return true;
+            }
             if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
                     && !mNotificationStackScrollLayoutController.isLongPressInProgress()
                     && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
@@ -6073,7 +6077,7 @@
             }
 
             handled |= handleTouch(event);
-            return !mDozing || mPulsing || handled;
+            return !mDozing || handled;
         }
 
         private boolean handleTouch(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 6abf339..ff26766 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -32,10 +32,10 @@
  * Dispatches shortcut to System UI components
  */
 @SysUISingleton
-public class ShortcutKeyDispatcher extends CoreStartable
-        implements ShortcutKeyServiceProxy.Callbacks {
+public class ShortcutKeyDispatcher implements CoreStartable, ShortcutKeyServiceProxy.Callbacks {
 
     private static final String TAG = "ShortcutKeyDispatcher";
+    private final Context mContext;
 
     private ShortcutKeyServiceProxy mShortcutKeyServiceProxy = new ShortcutKeyServiceProxy(this);
     private IWindowManager mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
@@ -50,7 +50,7 @@
 
     @Inject
     public ShortcutKeyDispatcher(Context context) {
-        super(context);
+        mContext = context;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index c290ce2..184dc25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -189,7 +189,6 @@
     protected NotificationPresenter mPresenter;
     protected ContentObserver mLockscreenSettingsObserver;
     protected ContentObserver mSettingsObserver;
-    private boolean mHideSilentNotificationsOnLockscreen;
 
     @Inject
     public NotificationLockscreenUserManagerImpl(Context context,
@@ -266,12 +265,6 @@
                 UserHandle.USER_ALL);
 
         mContext.getContentResolver().registerContentObserver(
-                mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS),
-                true,
-                mLockscreenSettingsObserver,
-                UserHandle.USER_ALL);
-
-        mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
                 mSettingsObserver);
 
@@ -340,9 +333,6 @@
         final boolean allowedByDpm = (dpmFlags
                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
 
-        mHideSilentNotificationsOnLockscreen = mSecureSettings.getIntForUser(
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1, mCurrentUserId) == 0;
-
         setShowLockscreenNotifications(show && allowedByDpm);
 
         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index bbff046..8222c9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -52,7 +52,10 @@
 import kotlin.math.max
 
 /**
- * A utility class to enable the downward swipe on when pulsing.
+ * A utility class that handles notification panel expansion when a user swipes downward on a
+ * notification from the pulsing state.
+ * If face-bypass is enabled, the user can swipe down anywhere on the screen (not just from a
+ * notification) to trigger the notification panel expansion.
  */
 @SysUISingleton
 class PulseExpansionHandler @Inject
@@ -62,7 +65,7 @@
     private val bypassController: KeyguardBypassController,
     private val headsUpManager: HeadsUpManagerPhone,
     private val roundnessManager: NotificationRoundnessManager,
-    private val configurationController: ConfigurationController,
+    configurationController: ConfigurationController,
     private val statusBarStateController: StatusBarStateController,
     private val falsingManager: FalsingManager,
     private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 59022c0f..822840d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -66,11 +66,12 @@
  * splitted screen.
  */
 @SysUISingleton
-public class InstantAppNotifier extends CoreStartable
-        implements CommandQueue.Callbacks, KeyguardStateController.Callback {
+public class InstantAppNotifier
+        implements CoreStartable, CommandQueue.Callbacks, KeyguardStateController.Callback {
     private static final String TAG = "InstantAppNotifier";
     public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5;
 
+    private final Context mContext;
     private final Handler mHandler = new Handler();
     private final Executor mUiBgExecutor;
     private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
@@ -83,7 +84,7 @@
             CommandQueue commandQueue,
             @UiBackground Executor uiBgExecutor,
             KeyguardStateController keyguardStateController) {
-        super(context);
+        mContext = context;
         mCommandQueue = commandQueue;
         mUiBgExecutor = uiBgExecutor;
         mKeyguardStateController = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
index 6f41425..9a7610d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
@@ -114,7 +114,18 @@
      */
     public void unbindHeadsUpView(NotificationEntry entry) {
         abortBindCallback(entry);
-        mStage.getStageParams(entry).markContentViewsFreeable(FLAG_CONTENT_VIEW_HEADS_UP);
+
+        // params may be null if the notification was already removed from the collection but we let
+        // it stick around during a launch animation. In this case, the heads up view has already
+        // been unbound, so we don't need to unbind it.
+        // TODO(b/253081345): Change this back to getStageParams and remove null check.
+        RowContentBindParams params = mStage.tryGetStageParams(entry);
+        if (params == null) {
+            mLogger.entryBindStageParamsNullOnUnbind(entry);
+            return;
+        }
+
+        params.markContentViewsFreeable(FLAG_CONTENT_VIEW_HEADS_UP);
         mLogger.entryContentViewMarkedFreeable(entry);
         mStage.requestRebind(entry, e -> mLogger.entryUnbound(e));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
index d1feaa0..5dbec8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
@@ -47,6 +47,14 @@
             "start unbinding heads up entry $str1 "
         })
     }
+
+    fun entryBindStageParamsNullOnUnbind(entry: NotificationEntry) {
+        buffer.log(TAG, INFO, {
+            str1 = entry.logKey
+        }, {
+            "heads up entry bind stage params null on unbind $str1 "
+        })
+    }
 }
 
 private const val TAG = "HeadsUpViewBinder"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index c956a2e..659df24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -72,7 +72,6 @@
 
 @SysUISingleton
 private class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
-    context: Context,
     @Main private val handler: Handler,
     private val keyguardStateController: KeyguardStateController,
     private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -82,7 +81,7 @@
     private val broadcastDispatcher: BroadcastDispatcher,
     private val secureSettings: SecureSettings,
     private val globalSettings: GlobalSettings
-) : CoreStartable(context), KeyguardNotificationVisibilityProvider {
+) : CoreStartable, KeyguardNotificationVisibilityProvider {
     private val showSilentNotifsUri =
             secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
     private val onStateChangedListeners = ListenerSet<Consumer<String>>()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java
index 7c41800..d626c18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java
@@ -21,6 +21,7 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
@@ -64,7 +65,7 @@
      * Get the stage parameters for the entry. Clients should use this to modify how the stage
      * handles the notification content.
      */
-    public final Params getStageParams(@NonNull NotificationEntry entry) {
+    public final @NonNull Params getStageParams(@NonNull NotificationEntry entry) {
         Params params = mContentParams.get(entry);
         if (params == null) {
             // TODO: This should throw an exception but there are some cases of re-entrant calls
@@ -79,6 +80,17 @@
         return params;
     }
 
+    // TODO(b/253081345): Remove this method.
+    /**
+     * Get the stage parameters for the entry, or null if there are no stage parameters for the
+     * entry.
+     *
+     * @see #getStageParams(NotificationEntry)
+     */
+    public final @Nullable Params tryGetStageParams(@NonNull NotificationEntry entry) {
+        return mContentParams.get(entry);
+    }
+
     /**
      * Create a params entry for the notification for this stage.
      */
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 9958ec7..34935db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -91,7 +91,6 @@
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.MathUtils;
-import android.util.Slog;
 import android.view.Display;
 import android.view.IRemoteAnimationRunner;
 import android.view.IWindowManager;
@@ -270,8 +269,7 @@
  * </b>
  */
 @SysUISingleton
-public class CentralSurfacesImpl extends CoreStartable implements
-        CentralSurfaces {
+public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
 
     private static final String BANNER_ACTION_CANCEL =
             "com.android.systemui.statusbar.banner_action_cancel";
@@ -307,6 +305,7 @@
         ONLY_CORE_APPS = onlyCoreApps;
     }
 
+    private final Context mContext;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
     private float mTransitionToFullShadeProgress = 0f;
@@ -764,7 +763,7 @@
             DeviceStateManager deviceStateManager,
             WiredChargingRippleController wiredChargingRippleController,
             IDreamManager dreamManager) {
-        super(context);
+        mContext = context;
         mNotificationsController = notificationsController;
         mFragmentService = fragmentService;
         mLightBarController = lightBarController;
@@ -3550,7 +3549,7 @@
     public void setBouncerShowingOverDream(boolean bouncerShowingOverDream) {
         mBouncerShowingOverDream = bouncerShowingOverDream;
     }
-  
+
     /**
      * Propagate the bouncer state to status bar components.
      *
@@ -3812,8 +3811,7 @@
         if (mDevicePolicyManager.getCameraDisabled(null,
                 mLockscreenUserManager.getCurrentUserId())) {
             return false;
-        } else if (mStatusBarKeyguardViewManager == null
-                || (isKeyguardShowing() && isKeyguardSecure())) {
+        } else if (isKeyguardShowing() && isKeyguardSecure()) {
             // Check if the admin has disabled the camera specifically for the keyguard
             return (mDevicePolicyManager.getKeyguardDisabledFeatures(null,
                     mLockscreenUserManager.getCurrentUserId())
@@ -4221,14 +4219,6 @@
 
     @Override
     public boolean isKeyguardSecure() {
-        if (mStatusBarKeyguardViewManager == null) {
-            // startKeyguard() hasn't been called yet, so we don't know.
-            // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
-            // value onVisibilityChanged().
-            Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
-                    new Throwable());
-            return false;
-        }
         return mStatusBarKeyguardViewManager.isSecure();
     }
     @Override
@@ -4288,11 +4278,6 @@
             .Callback() {
         @Override
         public void onFinished() {
-            if (mStatusBarKeyguardViewManager == null) {
-                Log.w(TAG, "Tried to notify keyguard visibility when "
-                        + "mStatusBarKeyguardViewManager was null");
-                return;
-            }
             if (mKeyguardStateController.isKeyguardFadingAway()) {
                 mStatusBarKeyguardViewManager.onKeyguardFadedAway();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index e3e8572..5e26cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -47,7 +47,7 @@
     private val asyncSensorManager: AsyncSensorManager,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val dumpManager: DumpManager
-) : Dumpable, CoreStartable(context) {
+) : Dumpable, CoreStartable {
 
     private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
     private var isListening = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
index 5b2d695..2f0ebf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
@@ -35,8 +35,8 @@
     protected val controller: UserSwitcherController,
 ) : BaseAdapter() {
 
-    protected open val users: ArrayList<UserRecord>
-        get() = controller.users
+    protected open val users: List<UserRecord>
+        get() = controller.users.filter { !controller.isKeyguardShowing || !it.isRestricted }
 
     init {
         controller.addAdapter(WeakReference(this))
@@ -112,6 +112,7 @@
                     item.isGuest,
                     item.isAddSupervisedUser,
                     isTablet,
+                    item.isManageUsers,
                 )
             return checkNotNull(context.getDrawable(iconRes))
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index 494a4bb..c150654 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -53,6 +53,7 @@
 import com.android.systemui.util.ViewController;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import javax.inject.Inject;
 
@@ -456,7 +457,7 @@
         }
 
         void refreshUserOrder() {
-            ArrayList<UserRecord> users = super.getUsers();
+            List<UserRecord> users = super.getUsers();
             mUsersOrdered = new ArrayList<>(users.size());
             for (int i = 0; i < users.size(); i++) {
                 UserRecord record = users.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
index af39eee..935fc7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
@@ -249,7 +249,7 @@
 
     override fun startActivity(intent: Intent) {
         if (useInteractor) {
-            activityStarter.startActivity(intent, /* dismissShade= */ false)
+            activityStarter.startActivity(intent, /* dismissShade= */ true)
         } else {
             _oldImpl.startActivity(intent)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java
index 46d2f3a..c294c37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java
@@ -49,6 +49,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.users.UserCreatingDialog;
 import com.android.systemui.GuestResetOrExitSessionReceiver;
 import com.android.systemui.GuestResumeSessionReceiver;
@@ -399,6 +400,16 @@
                 records.add(userRecord);
             }
 
+            if (canManageUsers()) {
+                records.add(LegacyUserDataHelper.createRecord(
+                        mContext,
+                        KeyguardUpdateMonitor.getCurrentUser(),
+                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+                        /* isRestricted= */ false,
+                        /* isSwitchToEnabled= */ true
+                ));
+            }
+
             mUiExecutor.execute(() -> {
                 if (records != null) {
                     mUsers = records;
@@ -438,6 +449,14 @@
                 && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
     }
 
+    @VisibleForTesting
+    boolean canManageUsers() {
+        UserInfo currentUser = mUserTracker.getUserInfo();
+        return mUserSwitcherEnabled
+                && ((currentUser != null && currentUser.isAdmin())
+                || mAddUsersFromLockScreen.getValue());
+    }
+
     private boolean createIsRestricted() {
         return !mAddUsersFromLockScreen.getValue();
     }
@@ -525,6 +544,8 @@
             showAddUserDialog(dialogShower);
         } else if (record.isAddSupervisedUser) {
             startSupervisedUserActivity();
+        } else if (record.isManageUsers) {
+            startActivity(new Intent(Settings.ACTION_USER_SETTINGS));
         } else {
             onUserListItemClicked(record.info.id, record, dialogShower);
         }
@@ -984,7 +1005,7 @@
 
     @Override
     public void startActivity(Intent intent) {
-        mActivityStarter.startActivity(intent, true);
+        mActivityStarter.startActivity(intent, /* dismissShade= */ true);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 09298b6..b1b8341 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -37,19 +37,20 @@
  * Serves as a collection of UI components, rather than showing its own UI.
  */
 @SysUISingleton
-public class TvStatusBar extends CoreStartable implements CommandQueue.Callbacks {
+public class TvStatusBar implements CoreStartable, CommandQueue.Callbacks {
 
     private static final String ACTION_SHOW_PIP_MENU =
             "com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
     private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
 
+    private final Context mContext;
     private final CommandQueue mCommandQueue;
     private final Lazy<AssistManager> mAssistManagerLazy;
 
     @Inject
     public TvStatusBar(Context context, CommandQueue commandQueue,
             Lazy<AssistManager> assistManagerLazy) {
-        super(context);
+        mContext = context;
         mCommandQueue = commandQueue;
         mAssistManagerLazy = assistManagerLazy;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
index c199744..b938c90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
@@ -35,9 +35,9 @@
  */
 @SysUISingleton
 class VpnStatusObserver @Inject constructor(
-    context: Context,
+    private val context: Context,
     private val securityController: SecurityController
-) : CoreStartable(context),
+) : CoreStartable,
         SecurityController.SecurityControllerCallback {
 
     private var vpnConnected = false
@@ -102,7 +102,7 @@
                     .apply {
                         vpnName?.let {
                             setContentText(
-                                    mContext.getString(
+                                    context.getString(
                                             R.string.notification_disclosure_vpn_text, it
                                     )
                             )
@@ -111,23 +111,23 @@
                     .build()
 
     private fun createVpnConnectedNotificationBuilder() =
-            Notification.Builder(mContext, NOTIFICATION_CHANNEL_TV_VPN)
+            Notification.Builder(context, NOTIFICATION_CHANNEL_TV_VPN)
                     .setSmallIcon(vpnIconId)
                     .setVisibility(Notification.VISIBILITY_PUBLIC)
                     .setCategory(Notification.CATEGORY_SYSTEM)
                     .extend(Notification.TvExtender())
                     .setOngoing(true)
-                    .setContentTitle(mContext.getString(R.string.notification_vpn_connected))
-                    .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext))
+                    .setContentTitle(context.getString(R.string.notification_vpn_connected))
+                    .setContentIntent(VpnConfig.getIntentForStatusPanel(context))
 
     private fun createVpnDisconnectedNotification() =
-            Notification.Builder(mContext, NOTIFICATION_CHANNEL_TV_VPN)
+            Notification.Builder(context, NOTIFICATION_CHANNEL_TV_VPN)
                     .setSmallIcon(vpnIconId)
                     .setVisibility(Notification.VISIBILITY_PUBLIC)
                     .setCategory(Notification.CATEGORY_SYSTEM)
                     .extend(Notification.TvExtender())
                     .setTimeoutAfter(VPN_DISCONNECTED_NOTIFICATION_TIMEOUT_MS)
-                    .setContentTitle(mContext.getString(R.string.notification_vpn_disconnected))
+                    .setContentTitle(context.getString(R.string.notification_vpn_disconnected))
                     .build()
 
     companion object {
@@ -137,4 +137,4 @@
         private const val TAG = "TvVpnNotification"
         private const val VPN_DISCONNECTED_NOTIFICATION_TIMEOUT_MS = 5_000L
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
index 8026ba5..b92725b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
@@ -18,13 +18,13 @@
 
 import android.annotation.Nullable;
 import android.app.Notification;
-import android.content.Context;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.NotificationListener;
 
 import javax.inject.Inject;
@@ -32,7 +32,8 @@
 /**
  * Keeps track of the notifications on TV.
  */
-public class TvNotificationHandler extends CoreStartable implements
+@SysUISingleton
+public class TvNotificationHandler implements CoreStartable,
         NotificationListener.NotificationHandler {
     private static final String TAG = "TvNotificationHandler";
     private final NotificationListener mNotificationListener;
@@ -41,8 +42,7 @@
     private Listener mUpdateListener;
 
     @Inject
-    public TvNotificationHandler(Context context, NotificationListener notificationListener) {
-        super(context);
+    public TvNotificationHandler(NotificationListener notificationListener) {
         mNotificationListener = notificationListener;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
index 892fedc..dbbd0b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
@@ -35,14 +35,15 @@
  * Offers control methods for the notification panel handler on TV devices.
  */
 @SysUISingleton
-public class TvNotificationPanel extends CoreStartable implements CommandQueue.Callbacks {
+public class TvNotificationPanel implements CoreStartable, CommandQueue.Callbacks {
     private static final String TAG = "TvNotificationPanel";
+    private final Context mContext;
     private final CommandQueue mCommandQueue;
     private final String mNotificationHandlerPackage;
 
     @Inject
     public TvNotificationPanel(Context context, CommandQueue commandQueue) {
-        super(context);
+        mContext = context;
         mCommandQueue = commandQueue;
         mNotificationHandlerPackage = mContext.getResources().getString(
                 com.android.internal.R.string.config_notificationHandlerPackage);
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 4450b76..5cbdf7c 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -62,7 +62,7 @@
     @LayoutRes private val viewLayoutRes: Int,
     private val windowTitle: String,
     private val wakeReason: String,
-) : CoreStartable(context) {
+) : CoreStartable {
     /**
      * Window layout params that will be used as a starting point for the [windowLayoutParams] of
      * all subclasses.
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index a345d99..3d56f23 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -100,7 +100,7 @@
  * associated work profiles
  */
 @SysUISingleton
-public class ThemeOverlayController extends CoreStartable implements Dumpable {
+public class ThemeOverlayController implements CoreStartable, Dumpable {
     protected static final String TAG = "ThemeOverlayController";
     private static final boolean DEBUG = true;
 
@@ -114,6 +114,7 @@
     private final SecureSettings mSecureSettings;
     private final Executor mMainExecutor;
     private final Handler mBgHandler;
+    private final Context mContext;
     private final boolean mIsMonetEnabled;
     private final UserTracker mUserTracker;
     private final DeviceProvisionedController mDeviceProvisionedController;
@@ -361,8 +362,7 @@
             UserManager userManager, DeviceProvisionedController deviceProvisionedController,
             UserTracker userTracker, DumpManager dumpManager, FeatureFlags featureFlags,
             @Main Resources resources, WakefulnessLifecycle wakefulnessLifecycle) {
-        super(context);
-
+        mContext = context;
         mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
         mDeviceProvisionedController = deviceProvisionedController;
         mBroadcastDispatcher = broadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 9eb34a4..ed14c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -50,13 +50,14 @@
  * Controls display of text toasts.
  */
 @SysUISingleton
-public class ToastUI extends CoreStartable implements CommandQueue.Callbacks {
+public class ToastUI implements CoreStartable, CommandQueue.Callbacks {
     // values from NotificationManagerService#LONG_DELAY and NotificationManagerService#SHORT_DELAY
     private static final int TOAST_LONG_TIME = 3500; // 3.5 seconds
     private static final int TOAST_SHORT_TIME = 2000; // 2 seconds
 
     private static final String TAG = "ToastUI";
 
+    private final Context mContext;
     private final CommandQueue mCommandQueue;
     private final INotificationManager mNotificationManager;
     private final IAccessibilityManager mIAccessibilityManager;
@@ -90,7 +91,7 @@
             @Nullable IAccessibilityManager accessibilityManager,
             ToastFactory toastFactory, ToastLogger toastLogger
     ) {
-        super(context);
+        mContext = context;
         mCommandQueue = commandQueue;
         mNotificationManager = notificationManager;
         mIAccessibilityManager = accessibilityManager;
@@ -179,7 +180,7 @@
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigurationChanged(Configuration newConfig) {
         if (newConfig.orientation != mOrientation) {
             mOrientation = newConfig.orientation;
             if (mToast != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 3ce5ca3..10a09dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -203,9 +203,9 @@
 
     @Provides
     @SysUISingleton
-    static TvNotificationHandler provideTvNotificationHandler(Context context,
+    static TvNotificationHandler provideTvNotificationHandler(
             NotificationListener notificationListener) {
-        return new TvNotificationHandler(context, notificationListener);
+        return new TvNotificationHandler(notificationListener);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 4dc78f9..bf70673 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -21,7 +21,6 @@
 import android.app.Notification.Action;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -56,11 +55,12 @@
 
 /** */
 @SysUISingleton
-public class StorageNotification extends CoreStartable {
+public class StorageNotification implements CoreStartable {
     private static final String TAG = "StorageNotification";
 
     private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
     private static final String ACTION_FINISH_WIZARD = "com.android.systemui.action.FINISH_WIZARD";
+    private final Context mContext;
 
     // TODO: delay some notifications to avoid bumpy fast operations
 
@@ -69,7 +69,7 @@
 
     @Inject
     public StorageNotification(Context context) {
-        super(context);
+        mContext = context;
     }
 
     private static class MoveInfo {
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 108ab43..7f1195b 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -16,426 +16,41 @@
 
 package com.android.systemui.user
 
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.graphics.drawable.BitmapDrawable
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.GradientDrawable
-import android.graphics.drawable.LayerDrawable
 import android.os.Bundle
-import android.os.UserManager
-import android.provider.Settings
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.MotionEvent
 import android.view.View
-import android.view.ViewGroup
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import android.widget.ImageView
-import android.widget.TextView
-import android.window.OnBackInvokedCallback
-import android.window.OnBackInvokedDispatcher
 import androidx.activity.ComponentActivity
-import androidx.constraintlayout.helper.widget.Flow
 import androidx.lifecycle.ViewModelProvider
-import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.util.UserIcons
-import com.android.settingslib.Utils
-import com.android.systemui.Gefingerpoken
 import com.android.systemui.R
-import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter
-import com.android.systemui.statusbar.policy.UserSwitcherController
-import com.android.systemui.user.data.source.UserRecord
 import com.android.systemui.user.ui.binder.UserSwitcherViewBinder
 import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
 import dagger.Lazy
 import javax.inject.Inject
-import kotlin.math.ceil
-
-private const val USER_VIEW = "user_view"
 
 /** Support a fullscreen user switcher */
 open class UserSwitcherActivity
 @Inject
 constructor(
-    private val userSwitcherController: UserSwitcherController,
-    private val broadcastDispatcher: BroadcastDispatcher,
     private val falsingCollector: FalsingCollector,
-    private val falsingManager: FalsingManager,
-    private val userManager: UserManager,
-    private val userTracker: UserTracker,
-    private val flags: FeatureFlags,
     private val viewModelFactory: Lazy<UserSwitcherViewModel.Factory>,
 ) : ComponentActivity() {
 
-    private lateinit var parent: UserSwitcherRootView
-    private lateinit var broadcastReceiver: BroadcastReceiver
-    private var popupMenu: UserSwitcherPopupMenu? = null
-    private lateinit var addButton: View
-    private var addUserRecords = mutableListOf<UserRecord>()
-    private val onBackCallback = OnBackInvokedCallback { finish() }
-    private val userSwitchedCallback: UserTracker.Callback =
-        object : UserTracker.Callback {
-            override fun onUserChanged(newUser: Int, userContext: Context) {
-                finish()
-            }
-        }
-    // When the add users options become available, insert another option to manage users
-    private val manageUserRecord =
-        UserRecord(
-            null /* info */,
-            null /* picture */,
-            false /* isGuest */,
-            false /* isCurrent */,
-            false /* isAddUser */,
-            false /* isRestricted */,
-            false /* isSwitchToEnabled */,
-            false /* isAddSupervisedUser */
-        )
-
-    private val adapter: UserAdapter by lazy { UserAdapter() }
-
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        createActivity()
-    }
-
-    @VisibleForTesting
-    fun createActivity() {
         setContentView(R.layout.user_switcher_fullscreen)
         window.decorView.systemUiVisibility =
             (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
-        if (isUsingModernArchitecture()) {
-            Log.d(TAG, "Using modern architecture.")
-            val viewModel =
-                ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
-            UserSwitcherViewBinder.bind(
-                view = requireViewById(R.id.user_switcher_root),
-                viewModel = viewModel,
-                lifecycleOwner = this,
-                layoutInflater = layoutInflater,
-                falsingCollector = falsingCollector,
-                onFinish = this::finish,
-            )
-            return
-        } else {
-            Log.d(TAG, "Not using modern architecture.")
-        }
-
-        parent = requireViewById<UserSwitcherRootView>(R.id.user_switcher_root)
-
-        parent.touchHandler =
-            object : Gefingerpoken {
-                override fun onTouchEvent(ev: MotionEvent?): Boolean {
-                    falsingCollector.onTouchEvent(ev)
-                    return false
-                }
-            }
-
-        requireViewById<View>(R.id.cancel).apply { setOnClickListener { _ -> finish() } }
-
-        addButton =
-            requireViewById<View>(R.id.add).apply { setOnClickListener { _ -> showPopupMenu() } }
-
-        onBackInvokedDispatcher.registerOnBackInvokedCallback(
-            OnBackInvokedDispatcher.PRIORITY_DEFAULT,
-            onBackCallback
+        val viewModel =
+            ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
+        UserSwitcherViewBinder.bind(
+            view = requireViewById(R.id.user_switcher_root),
+            viewModel = viewModel,
+            lifecycleOwner = this,
+            layoutInflater = layoutInflater,
+            falsingCollector = falsingCollector,
+            onFinish = this::finish,
         )
-
-        userSwitcherController.init(parent)
-        initBroadcastReceiver()
-
-        parent.post { buildUserViews() }
-        userTracker.addCallback(userSwitchedCallback, mainExecutor)
-    }
-
-    private fun showPopupMenu() {
-        val items = mutableListOf<UserRecord>()
-        addUserRecords.forEach { items.add(it) }
-
-        var popupMenuAdapter =
-            ItemAdapter(
-                this,
-                R.layout.user_switcher_fullscreen_popup_item,
-                layoutInflater,
-                { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item, true) },
-                { item: UserRecord ->
-                    adapter.findUserIcon(item, true).mutate().apply {
-                        setTint(
-                            resources.getColor(
-                                R.color.user_switcher_fullscreen_popup_item_tint,
-                                getTheme()
-                            )
-                        )
-                    }
-                }
-            )
-        popupMenuAdapter.addAll(items)
-
-        popupMenu =
-            UserSwitcherPopupMenu(this).apply {
-                setAnchorView(addButton)
-                setAdapter(popupMenuAdapter)
-                setOnItemClickListener { parent: AdapterView<*>, view: View, pos: Int, id: Long ->
-                    if (falsingManager.isFalseTap(LOW_PENALTY) || !view.isEnabled()) {
-                        return@setOnItemClickListener
-                    }
-                    // -1 for the header
-                    val item = popupMenuAdapter.getItem(pos - 1)
-                    if (item == manageUserRecord) {
-                        val i = Intent().setAction(Settings.ACTION_USER_SETTINGS)
-                        this@UserSwitcherActivity.startActivity(i)
-                    } else {
-                        adapter.onUserListItemClicked(item)
-                    }
-
-                    dismiss()
-                    popupMenu = null
-
-                    if (!item.isAddUser) {
-                        this@UserSwitcherActivity.finish()
-                    }
-                }
-
-                show()
-            }
-    }
-
-    private fun buildUserViews() {
-        var count = 0
-        var start = 0
-        for (i in 0 until parent.getChildCount()) {
-            if (parent.getChildAt(i).getTag() == USER_VIEW) {
-                if (count == 0) start = i
-                count++
-            }
-        }
-        parent.removeViews(start, count)
-        addUserRecords.clear()
-        val flow = requireViewById<Flow>(R.id.flow)
-        val totalWidth = parent.width
-        val userViewCount = adapter.getTotalUserViews()
-        val maxColumns = getMaxColumns(userViewCount)
-        val horizontalGap =
-            resources.getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap)
-        val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap
-        val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns
-
-        flow.setMaxElementsWrap(maxColumns)
-
-        for (i in 0 until adapter.getCount()) {
-            val item = adapter.getItem(i)
-            if (adapter.doNotRenderUserView(item)) {
-                addUserRecords.add(item)
-            } else {
-                val userView = adapter.getView(i, null, parent)
-                userView.requireViewById<ImageView>(R.id.user_switcher_icon).apply {
-                    val lp = layoutParams
-                    if (maxWidgetDiameter < lp.width) {
-                        lp.width = maxWidgetDiameter
-                        lp.height = maxWidgetDiameter
-                        layoutParams = lp
-                    }
-                }
-
-                userView.setId(View.generateViewId())
-                parent.addView(userView)
-
-                // Views must have an id and a parent in order for Flow to lay them out
-                flow.addView(userView)
-
-                userView.setOnClickListener { v ->
-                    if (falsingManager.isFalseTap(LOW_PENALTY) || !v.isEnabled()) {
-                        return@setOnClickListener
-                    }
-
-                    if (!item.isCurrent || item.isGuest) {
-                        adapter.onUserListItemClicked(item)
-                    }
-                }
-            }
-        }
-
-        if (!addUserRecords.isEmpty()) {
-            addUserRecords.add(manageUserRecord)
-            addButton.visibility = View.VISIBLE
-        } else {
-            addButton.visibility = View.GONE
-        }
-    }
-
-    override fun onBackPressed() {
-        if (isUsingModernArchitecture()) {
-            return super.onBackPressed()
-        }
-
-        finish()
-    }
-
-    override fun onDestroy() {
-        super.onDestroy()
-        if (isUsingModernArchitecture()) {
-            return
-        }
-        destroyActivity()
-    }
-
-    @VisibleForTesting
-    fun destroyActivity() {
-        onBackInvokedDispatcher.unregisterOnBackInvokedCallback(onBackCallback)
-        broadcastDispatcher.unregisterReceiver(broadcastReceiver)
-        userTracker.removeCallback(userSwitchedCallback)
-    }
-
-    private fun initBroadcastReceiver() {
-        broadcastReceiver =
-            object : BroadcastReceiver() {
-                override fun onReceive(context: Context, intent: Intent) {
-                    val action = intent.getAction()
-                    if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                        finish()
-                    }
-                }
-            }
-
-        val filter = IntentFilter()
-        filter.addAction(Intent.ACTION_SCREEN_OFF)
-        broadcastDispatcher.registerReceiver(broadcastReceiver, filter)
-    }
-
-    @VisibleForTesting
-    fun getMaxColumns(userCount: Int): Int {
-        return if (userCount < 5) 4 else ceil(userCount / 2.0).toInt()
-    }
-
-    private fun isUsingModernArchitecture(): Boolean {
-        return flags.isEnabled(Flags.MODERN_USER_SWITCHER_ACTIVITY)
-    }
-
-    /** Provides views to populate the option menu. */
-    private class ItemAdapter(
-        val parentContext: Context,
-        val resource: Int,
-        val layoutInflater: LayoutInflater,
-        val textGetter: (UserRecord) -> String,
-        val iconGetter: (UserRecord) -> Drawable
-    ) : ArrayAdapter<UserRecord>(parentContext, resource) {
-
-        override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
-            val item = getItem(position)
-            val view = convertView ?: layoutInflater.inflate(resource, parent, false)
-
-            view.requireViewById<ImageView>(R.id.icon).apply { setImageDrawable(iconGetter(item)) }
-            view.requireViewById<TextView>(R.id.text).apply { setText(textGetter(item)) }
-
-            return view
-        }
-    }
-
-    private inner class UserAdapter : BaseUserSwitcherAdapter(userSwitcherController) {
-        override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
-            val item = getItem(position)
-            var view = convertView as ViewGroup?
-            if (view == null) {
-                view =
-                    layoutInflater.inflate(R.layout.user_switcher_fullscreen_item, parent, false)
-                        as ViewGroup
-            }
-            (view.getChildAt(0) as ImageView).apply { setImageDrawable(getDrawable(item)) }
-            (view.getChildAt(1) as TextView).apply { setText(getName(getContext(), item)) }
-
-            view.setEnabled(item.isSwitchToEnabled)
-            UserSwitcherController.setSelectableAlpha(view)
-            view.setTag(USER_VIEW)
-            return view
-        }
-
-        override fun getName(context: Context, item: UserRecord, isTablet: Boolean): String {
-            return if (item == manageUserRecord) {
-                getString(R.string.manage_users)
-            } else {
-                super.getName(context, item, isTablet)
-            }
-        }
-
-        fun findUserIcon(item: UserRecord, isTablet: Boolean = false): Drawable {
-            if (item == manageUserRecord) {
-                return getDrawable(R.drawable.ic_manage_users)
-            }
-            if (item.info == null) {
-                return getIconDrawable(this@UserSwitcherActivity, item, isTablet)
-            }
-            val userIcon = userManager.getUserIcon(item.info.id)
-            if (userIcon != null) {
-                return BitmapDrawable(userIcon)
-            }
-            return UserIcons.getDefaultUserIcon(resources, item.info.id, false)
-        }
-
-        fun getTotalUserViews(): Int {
-            return users.count { item -> !doNotRenderUserView(item) }
-        }
-
-        fun doNotRenderUserView(item: UserRecord): Boolean {
-            return item.isAddUser || item.isAddSupervisedUser || item.isGuest && item.info == null
-        }
-
-        private fun getDrawable(item: UserRecord): Drawable {
-            var drawable =
-                if (item.isGuest) {
-                    getDrawable(R.drawable.ic_account_circle)
-                } else {
-                    findUserIcon(item)
-                }
-            drawable.mutate()
-
-            if (!item.isCurrent && !item.isSwitchToEnabled) {
-                drawable.setTint(
-                    resources.getColor(
-                        R.color.kg_user_switcher_restricted_avatar_icon_color,
-                        getTheme()
-                    )
-                )
-            }
-
-            val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() as LayerDrawable
-            if (item == userSwitcherController.currentUserRecord) {
-                (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
-                    val stroke =
-                        resources.getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
-                    val color =
-                        Utils.getColorAttrDefaultColor(
-                            this@UserSwitcherActivity,
-                            com.android.internal.R.attr.colorAccentPrimary
-                        )
-
-                    setStroke(stroke, color)
-                }
-            }
-
-            ld.setDrawableByLayerId(R.id.user_avatar, drawable)
-            return ld
-        }
-
-        override fun notifyDataSetChanged() {
-            super.notifyDataSetChanged()
-            buildUserViews()
-        }
-    }
-
-    companion object {
-        private const val TAG = "UserSwitcherActivity"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
index 9370286..d4fb563 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
@@ -47,6 +47,9 @@
      * If not disabled, this is `null`.
      */
     @JvmField val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin? = null,
+
+    /** Whether this record is to go to the Settings page to manage users. */
+    @JvmField val isManageUsers: Boolean = false
 ) {
     /** Returns a new instance of [UserRecord] with its [isCurrent] set to the given value. */
     fun copyWithIsCurrent(isCurrent: Boolean): UserRecord {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserActionsUtil.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserActionsUtil.kt
index 1b4746a..dc004f3 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserActionsUtil.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserActionsUtil.kt
@@ -82,6 +82,15 @@
         )
     }
 
+    fun canManageUsers(
+        repository: UserRepository,
+        isUserSwitcherEnabled: Boolean,
+        isAddUsersFromLockScreenEnabled: Boolean,
+    ): Boolean {
+        return isUserSwitcherEnabled &&
+            (repository.getSelectedUserInfo().isAdmin || isAddUsersFromLockScreenEnabled)
+    }
+
     /**
      * Returns `true` if the current user is allowed to add users to the device; `false` otherwise.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index 142a328..ba5a82a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -102,6 +102,7 @@
     interface UserCallback {
         /** Returns `true` if this callback can be cleaned-up. */
         fun isEvictable(): Boolean = false
+
         /** Notifies that the state of users on the device has changed. */
         fun onUserStateChanged()
     }
@@ -164,10 +165,11 @@
         get() =
             if (isNewImpl) {
                 combine(
+                    repository.selectedUserInfo,
                     repository.userInfos,
                     repository.userSwitcherSettings,
                     keyguardInteractor.isKeyguardShowing,
-                ) { userInfos, settings, isDeviceLocked ->
+                ) { _, userInfos, settings, isDeviceLocked ->
                     buildList {
                         val hasGuestUser = userInfos.any { it.isGuest }
                         if (
@@ -183,35 +185,45 @@
                             add(UserActionModel.ENTER_GUEST_MODE)
                         }
 
-                        if (isDeviceLocked && !settings.isAddUsersFromLockscreen) {
+                        if (!isDeviceLocked || settings.isAddUsersFromLockscreen) {
                             // The device is locked and our setting to allow actions that add users
                             // from the lock-screen is not enabled. The guest action from above is
                             // always allowed, even when the device is locked, but the various "add
                             // user" actions below are not. We can finish building the list here.
-                            return@buildList
+
+                            val canCreateUsers =
+                                UserActionsUtil.canCreateUser(
+                                    manager,
+                                    repository,
+                                    settings.isUserSwitcherEnabled,
+                                    settings.isAddUsersFromLockscreen,
+                                )
+
+                            if (canCreateUsers) {
+                                add(UserActionModel.ADD_USER)
+                            }
+
+                            if (
+                                UserActionsUtil.canCreateSupervisedUser(
+                                    manager,
+                                    repository,
+                                    settings.isUserSwitcherEnabled,
+                                    settings.isAddUsersFromLockscreen,
+                                    supervisedUserPackageName,
+                                )
+                            ) {
+                                add(UserActionModel.ADD_SUPERVISED_USER)
+                            }
                         }
 
                         if (
-                            UserActionsUtil.canCreateUser(
-                                manager,
+                            UserActionsUtil.canManageUsers(
                                 repository,
                                 settings.isUserSwitcherEnabled,
                                 settings.isAddUsersFromLockscreen,
                             )
                         ) {
-                            add(UserActionModel.ADD_USER)
-                        }
-
-                        if (
-                            UserActionsUtil.canCreateSupervisedUser(
-                                manager,
-                                repository,
-                                settings.isUserSwitcherEnabled,
-                                settings.isAddUsersFromLockscreen,
-                                supervisedUserPackageName,
-                            )
-                        ) {
-                            add(UserActionModel.ADD_SUPERVISED_USER)
+                            add(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
                         }
                     }
                 }
@@ -264,7 +276,10 @@
                                 toRecord(
                                     action = it,
                                     selectedUserId = selectedUserInfo.id,
-                                    isAddFromLockscreenEnabled = settings.isAddUsersFromLockscreen,
+                                    isRestricted =
+                                        it != UserActionModel.ENTER_GUEST_MODE &&
+                                            it != UserActionModel.NAVIGATE_TO_USER_MANAGEMENT &&
+                                            !settings.isAddUsersFromLockscreen,
                                 )
                             }
                     )
@@ -482,12 +497,12 @@
                             .setAction(UserManager.ACTION_CREATE_SUPERVISED_USER)
                             .setPackage(supervisedUserPackageName)
                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
-                        /* dismissShade= */ false,
+                        /* dismissShade= */ true,
                     )
                 UserActionModel.NAVIGATE_TO_USER_MANAGEMENT ->
                     activityStarter.startActivity(
                         Intent(Settings.ACTION_USER_SETTINGS),
-                        /* dismissShade= */ false,
+                        /* dismissShade= */ true,
                     )
             }
         } else {
@@ -575,20 +590,13 @@
     private suspend fun toRecord(
         action: UserActionModel,
         selectedUserId: Int,
-        isAddFromLockscreenEnabled: Boolean,
+        isRestricted: Boolean,
     ): UserRecord {
         return LegacyUserDataHelper.createRecord(
             context = applicationContext,
             selectedUserId = selectedUserId,
             actionType = action,
-            isRestricted =
-                if (action == UserActionModel.ENTER_GUEST_MODE) {
-                    // Entering guest mode is never restricted, so it's allowed to happen from the
-                    // lockscreen even if the "add from lockscreen" system setting is off.
-                    false
-                } else {
-                    !isAddFromLockscreenEnabled
-                },
+            isRestricted = isRestricted,
             isSwitchToEnabled =
                 canSwitchUsers(selectedUserId) &&
                     // If the user is auto-created is must not be currently resetting.
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
index 137de15..03a7470 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
@@ -80,6 +80,7 @@
                     context = context,
                     selectedUserId = selectedUserId,
                 ),
+            isManageUsers = actionType == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
         )
     }
 
@@ -90,6 +91,7 @@
             record.isAddUser -> UserActionModel.ADD_USER
             record.isAddSupervisedUser -> UserActionModel.ADD_SUPERVISED_USER
             record.isGuest -> UserActionModel.ENTER_GUEST_MODE
+            record.isManageUsers -> UserActionModel.NAVIGATE_TO_USER_MANAGEMENT
             else -> error("Not a known action: $record")
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
index 15fdc35..e74232d 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
@@ -22,7 +22,6 @@
 import androidx.annotation.StringRes
 import com.android.systemui.R
 import com.android.systemui.user.data.source.UserRecord
-import kotlin.math.ceil
 
 /**
  * Defines utility functions for helping with legacy UI code for users.
@@ -33,16 +32,6 @@
  */
 object LegacyUserUiHelper {
 
-    /** Returns the maximum number of columns for user items in the user switcher. */
-    fun getMaxUserSwitcherItemColumns(userCount: Int): Int {
-        // TODO(b/243844097): remove this once we remove the old user switcher implementation.
-        return if (userCount < 5) {
-            4
-        } else {
-            ceil(userCount / 2.0).toInt()
-        }
-    }
-
     @JvmStatic
     @DrawableRes
     fun getUserSwitcherActionIconResourceId(
@@ -50,6 +39,7 @@
         isGuest: Boolean,
         isAddSupervisedUser: Boolean,
         isTablet: Boolean = false,
+        isManageUsers: Boolean,
     ): Int {
         return if (isAddUser && isTablet) {
             R.drawable.ic_account_circle_filled
@@ -59,6 +49,8 @@
             R.drawable.ic_account_circle
         } else if (isAddSupervisedUser) {
             R.drawable.ic_add_supervised_user
+        } else if (isManageUsers) {
+            R.drawable.ic_manage_users
         } else {
             R.drawable.ic_avatar_user
         }
@@ -85,6 +77,7 @@
                         isAddUser = record.isAddUser,
                         isAddSupervisedUser = record.isAddSupervisedUser,
                         isTablet = isTablet,
+                        isManageUsers = record.isManageUsers,
                     )
                 )
         }
@@ -114,8 +107,9 @@
         isAddUser: Boolean,
         isAddSupervisedUser: Boolean,
         isTablet: Boolean = false,
+        isManageUsers: Boolean,
     ): Int {
-        check(isGuest || isAddUser || isAddSupervisedUser)
+        check(isGuest || isAddUser || isAddSupervisedUser || isManageUsers)
 
         return when {
             isGuest && isGuestUserAutoCreated && isGuestUserResetting ->
@@ -125,6 +119,7 @@
             isGuest -> com.android.internal.R.string.guest_name
             isAddUser -> com.android.settingslib.R.string.user_add_user
             isAddSupervisedUser -> R.string.add_user_supervised
+            isManageUsers -> R.string.manage_users
             else -> error("This should never happen!")
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
index 6e7b523..91c5921 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
@@ -48,7 +48,7 @@
     private val dialogLaunchAnimator: DialogLaunchAnimator,
     private val interactor: UserInteractor,
     private val featureFlags: FeatureFlags,
-) : CoreStartable(context) {
+) : CoreStartable {
 
     private var currentDialog: Dialog? = null
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 5b83df7..219dae2 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -19,7 +19,6 @@
 
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
-import com.android.systemui.R
 import com.android.systemui.common.ui.drawable.CircularDrawable
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
@@ -30,6 +29,7 @@
 import com.android.systemui.user.shared.model.UserActionModel
 import com.android.systemui.user.shared.model.UserModel
 import javax.inject.Inject
+import kotlin.math.ceil
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
@@ -52,8 +52,7 @@
         userInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
 
     /** The maximum number of columns that the user selection grid should use. */
-    val maximumUserColumns: Flow<Int> =
-        users.map { LegacyUserUiHelper.getMaxUserSwitcherItemColumns(it.size) }
+    val maximumUserColumns: Flow<Int> = users.map { getMaxUserSwitcherItemColumns(it.size) }
 
     private val _isMenuVisible = MutableStateFlow(false)
     /**
@@ -118,6 +117,15 @@
         _isMenuVisible.value = false
     }
 
+    /** Returns the maximum number of columns for user items in the user switcher. */
+    private fun getMaxUserSwitcherItemColumns(userCount: Int): Int {
+        return if (userCount < 5) {
+            4
+        } else {
+            ceil(userCount / 2.0).toInt()
+        }
+    }
+
     private fun createFinishRequestedFlow(): Flow<Boolean> {
         var mostRecentSelectedUserId: Int? = null
         var mostRecentIsInteractive: Boolean? = null
@@ -171,27 +179,23 @@
         return UserActionViewModel(
             viewKey = model.ordinal.toLong(),
             iconResourceId =
-                if (model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
-                    R.drawable.ic_manage_users
-                } else {
-                    LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
-                        isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
-                        isAddUser = model == UserActionModel.ADD_USER,
-                        isGuest = model == UserActionModel.ENTER_GUEST_MODE,
-                    )
-                },
+                LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
+                    isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
+                    isAddUser = model == UserActionModel.ADD_USER,
+                    isGuest = model == UserActionModel.ENTER_GUEST_MODE,
+                    isManageUsers = model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+                    isTablet = true,
+                ),
             textResourceId =
-                if (model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
-                    R.string.manage_users
-                } else {
-                    LegacyUserUiHelper.getUserSwitcherActionTextResourceId(
-                        isGuest = model == UserActionModel.ENTER_GUEST_MODE,
-                        isGuestUserAutoCreated = guestUserInteractor.isGuestUserAutoCreated,
-                        isGuestUserResetting = guestUserInteractor.isGuestUserResetting,
-                        isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
-                        isAddUser = model == UserActionModel.ADD_USER,
-                    )
-                },
+                LegacyUserUiHelper.getUserSwitcherActionTextResourceId(
+                    isGuest = model == UserActionModel.ENTER_GUEST_MODE,
+                    isGuestUserAutoCreated = guestUserInteractor.isGuestUserAutoCreated,
+                    isGuestUserResetting = guestUserInteractor.isGuestUserResetting,
+                    isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
+                    isAddUser = model == UserActionModel.ADD_USER,
+                    isManageUsers = model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+                    isTablet = true,
+                ),
             onClicked = {
                 userInteractor.executeAction(action = model)
                 // We don't finish because we want to show a dialog over the full-screen UI and
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 53da213..2efeda9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -32,7 +32,8 @@
 import javax.inject.Inject;
 
 // NOT Singleton. Started per-user.
-public class NotificationChannels extends CoreStartable {
+/** */
+public class NotificationChannels implements CoreStartable {
     public static String ALERTS      = "ALR";
     public static String SCREENSHOTS_HEADSUP = "SCN_HEADSUP";
     // Deprecated. Please use or create a more specific channel that users will better understand
@@ -45,9 +46,11 @@
     public static String INSTANT     = "INS";
     public static String SETUP       = "STP";
 
+    private final Context mContext;
+
     @Inject
     public NotificationChannels(Context context) {
-        super(context);
+        mContext = context;
     }
 
     public static void createAll(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 619e50b..a0a0372 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -564,12 +564,13 @@
 
     /** */
     @SysUISingleton
-    public static class Service extends CoreStartable implements Dumpable {
+    public static class Service implements CoreStartable,  Dumpable {
+        private final Context mContext;
         private final GarbageMonitor mGarbageMonitor;
 
         @Inject
         public Service(Context context, GarbageMonitor garbageMonitor) {
-            super(context);
+            mContext = context;
             mGarbageMonitor = garbageMonitor;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 87fb2a6..0b3521b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -31,18 +31,19 @@
 import javax.inject.Inject;
 
 @SysUISingleton
-public class VolumeUI extends CoreStartable {
+public class VolumeUI implements CoreStartable {
     private static final String TAG = "VolumeUI";
     private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Handler mHandler = new Handler();
 
     private boolean mEnabled;
+    private final Context mContext;
     private VolumeDialogComponent mVolumeComponent;
 
     @Inject
     public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) {
-        super(context);
+        mContext = context;
         mVolumeComponent = volumeDialogComponent;
     }
 
@@ -59,8 +60,7 @@
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
+    public void onConfigurationChanged(Configuration newConfig) {
         if (!mEnabled) return;
         mVolumeComponent.onConfigurationChanged(newConfig);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 3472cb1..fbc6a58 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -89,8 +89,10 @@
  *       -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces
  */
 @SysUISingleton
-public final class WMShell extends CoreStartable
-        implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
+public final class WMShell implements
+        CoreStartable,
+        CommandQueue.Callbacks,
+        ProtoTraceable<SystemUiTraceProto> {
     private static final String TAG = WMShell.class.getName();
     private static final int INVALID_SYSUI_STATE_MASK =
             SYSUI_STATE_DIALOG_SHOWING
@@ -102,6 +104,7 @@
                     | SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED
                     | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 
+    private final Context mContext;
     // Shell interfaces
     private final ShellInterface mShell;
     private final Optional<Pip> mPipOptional;
@@ -163,7 +166,8 @@
     private WakefulnessLifecycle.Observer mWakefulnessObserver;
 
     @Inject
-    public WMShell(Context context,
+    public WMShell(
+            Context context,
             ShellInterface shell,
             Optional<Pip> pipOptional,
             Optional<SplitScreen> splitScreenOptional,
@@ -179,7 +183,7 @@
             WakefulnessLifecycle wakefulnessLifecycle,
             UserTracker userTracker,
             @Main Executor sysUiMainExecutor) {
-        super(context);
+        mContext = context;
         mShell = shell;
         mCommandQueue = commandQueue;
         mConfigurationController = configurationController;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
index aa671d1..91b544b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
@@ -17,7 +17,6 @@
 package com.android.keyguard
 
 import android.hardware.biometrics.BiometricSourceType
-import org.mockito.Mockito.verify
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
@@ -30,9 +29,10 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.Captor
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
@@ -63,7 +63,6 @@
         whenever(keyguardUpdateMonitor.strongAuthTracker).thenReturn(strongAuthTracker)
         whenever(sessionTracker.getSessionId(anyInt())).thenReturn(sessionId)
         keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger(
-                mContext,
                 uiEventLogger,
                 keyguardUpdateMonitor,
                 sessionTracker)
@@ -195,4 +194,4 @@
         verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallbackCaptor.capture())
         updateMonitorCallback = updateMonitorCallbackCaptor.value
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index c6ebaa8..48e8239 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -221,15 +221,17 @@
     public void onResourcesUpdate_callsThroughOnRotationChange() {
         // Rotation is the same, shouldn't cause an update
         mKeyguardSecurityContainerController.updateResources();
-        verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+        verify(mView, never()).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager),
+                eq(mUserSwitcherController),
+                any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class));
 
         // Update rotation. Should trigger update
         mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
 
         mKeyguardSecurityContainerController.updateResources();
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+        verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager),
+                eq(mUserSwitcherController),
+                any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class));
     }
 
     private void touchDown() {
@@ -263,8 +265,9 @@
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+        verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager),
+                eq(mUserSwitcherController),
+                any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class));
     }
 
     @Test
@@ -275,8 +278,9 @@
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
-        verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+        verify(mView).initMode(eq(MODE_ONE_HANDED), eq(mGlobalSettings), eq(mFalsingManager),
+                eq(mUserSwitcherController),
+                any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class));
     }
 
     @Test
@@ -285,8 +289,26 @@
         setupGetSecurityView();
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+        verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager),
+                eq(mUserSwitcherController),
+                any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class));
+    }
+
+    @Test
+    public void addUserSwitcherCallback() {
+        ArgumentCaptor<KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback>
+                captor = ArgumentCaptor.forClass(
+                KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class);
+
+        setupGetSecurityView();
+
+        mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
+        verify(mView).initMode(anyInt(), any(GlobalSettings.class), any(FalsingManager.class),
+                any(UserSwitcherController.class),
+                captor.capture());
+        captor.getValue().showUnlockToContinueMessage();
+        verify(mKeyguardPasswordViewControllerMock).showMessage(
+                getContext().getString(R.string.keyguard_unlock_to_continue), null);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 52f8825..82d3ca7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -119,7 +119,7 @@
         int systemBarInsetAmount = 0;
 
         mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+                mUserSwitcherController, () -> {});
 
         Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
         Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -141,7 +141,7 @@
         int systemBarInsetAmount = paddingBottom + 1;
 
         mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+                mUserSwitcherController, () -> {});
 
         Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
         Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -158,9 +158,10 @@
     @Test
     public void testDefaultViewMode() {
         mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+                mUserSwitcherController, () -> {
+                });
         mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+                mUserSwitcherController, () -> {});
         ConstraintSet.Constraint viewFlipperConstraint =
                 getViewConstraint(mSecurityViewFlipper.getId());
         assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
@@ -377,7 +378,7 @@
     private void setupUserSwitcher() {
         when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_RIGHT);
         mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
-                mGlobalSettings, mFalsingManager, mUserSwitcherController);
+                mGlobalSettings, mFalsingManager, mUserSwitcherController, () -> {});
     }
 
     private ArrayList<UserRecord> buildUserRecords(int count) {
@@ -387,7 +388,8 @@
                     0 /* flags */);
             users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */,
                     false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */,
-                    false /* isAddSupervisedUser */, null /* enforcedAdmin */));
+                    false /* isAddSupervisedUser */, null /* enforcedAdmin */,
+                    false /* isManageUsers */));
         }
         return users;
     }
@@ -395,7 +397,7 @@
     private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
         int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
         mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
-                mUserSwitcherController);
+                mUserSwitcherController, () -> {});
     }
 
     /** Get the ConstraintLayout constraint of the view. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index df10dfe..2319f43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -231,7 +231,7 @@
             }
 
             @Override
-            protected void onConfigurationChanged(Configuration newConfig) {
+            public void onConfigurationChanged(Configuration newConfig) {
                 super.onConfigurationChanged(newConfig);
                 mExecutor.runAllReady();
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
index 571dd3d..9f4a7c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
@@ -71,7 +71,7 @@
         MockitoAnnotations.initMocks(this);
         when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>());
 
-        mController = new ComplicationTypesUpdater(mContext, mDreamBackend, mExecutor,
+        mController = new ComplicationTypesUpdater(mDreamBackend, mExecutor,
                 mSecureSettings, mDreamOverlayStateController);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
index 314a30b..ec448f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
@@ -82,7 +82,6 @@
     public void testComplicationAdded() {
         final DreamClockTimeComplication.Registrant registrant =
                 new DreamClockTimeComplication.Registrant(
-                        mContext,
                         mDreamOverlayStateController,
                         mComplication);
         registrant.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index db6082d..aa8c93e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -115,7 +115,7 @@
     @Test
     public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
-                new DreamHomeControlsComplication.Registrant(mContext, mComplication,
+                new DreamHomeControlsComplication.Registrant(mComplication,
                         mDreamOverlayStateController, mControlsComponent);
         registrant.start();
 
@@ -128,7 +128,7 @@
     @Test
     public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
-                new DreamHomeControlsComplication.Registrant(mContext, mComplication,
+                new DreamHomeControlsComplication.Registrant(mComplication,
                         mDreamOverlayStateController, mControlsComponent);
         registrant.start();
 
@@ -141,7 +141,7 @@
     @Test
     public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
-                new DreamHomeControlsComplication.Registrant(mContext, mComplication,
+                new DreamHomeControlsComplication.Registrant(mComplication,
                         mDreamOverlayStateController, mControlsComponent);
         registrant.start();
 
@@ -154,7 +154,7 @@
     @Test
     public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
-                new DreamHomeControlsComplication.Registrant(mContext, mComplication,
+                new DreamHomeControlsComplication.Registrant(mComplication,
                         mDreamOverlayStateController, mControlsComponent);
         registrant.start();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
index fa8f88a..c8b2b25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
@@ -24,7 +24,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.smartspace.SmartspaceTarget;
-import android.content.Context;
 import android.testing.AndroidTestingRunner;
 import android.view.View;
 
@@ -48,8 +47,6 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class SmartSpaceComplicationTest extends SysuiTestCase {
-    @Mock
-    private Context mContext;
 
     @Mock
     private DreamSmartspaceController mSmartspaceController;
@@ -80,7 +77,6 @@
 
     private SmartSpaceComplication.Registrant getRegistrant() {
         return new SmartSpaceComplication.Registrant(
-                mContext,
                 mDreamOverlayStateController,
                 mComplication,
                 mSmartspaceController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorParameterizedTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index b6d7559..b4d5464 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -12,20 +12,20 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
+ *
  */
 
-package com.android.systemui.keyguard.domain.usecase
+package com.android.systemui.keyguard.domain.interactor
 
 import android.content.Intent
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
@@ -195,6 +195,7 @@
     @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
+    @Mock private lateinit var expandable: Expandable
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
 
@@ -208,6 +209,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        whenever(expandable.activityLaunchController()).thenReturn(animationController)
 
         homeControls = object : FakeKeyguardQuickAffordanceConfig() {}
         underTest =
@@ -259,7 +261,7 @@
 
         underTest.onQuickAffordanceClicked(
             configKey = homeControls::class,
-            animationController = animationController,
+            expandable = expandable,
         )
 
         if (startActivity) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 1dd919a..65fd6e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -12,9 +12,10 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
+ *
  */
 
-package com.android.systemui.keyguard.domain.usecase
+package com.android.systemui.keyguard.domain.interactor
 
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
@@ -22,13 +23,12 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -103,6 +103,7 @@
         homeControls.setState(
             KeyguardQuickAffordanceConfig.State.Visible(
                 icon = ICON,
+                toggle = KeyguardQuickAffordanceToggleState.On,
             )
         )
 
@@ -123,6 +124,7 @@
         assertThat(visibleModel.icon).isEqualTo(ICON)
         assertThat(visibleModel.icon.contentDescription)
             .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
+        assertThat(visibleModel.toggle).isEqualTo(KeyguardQuickAffordanceToggleState.On)
         job.cancel()
     }
 
@@ -152,6 +154,7 @@
         assertThat(visibleModel.icon).isEqualTo(ICON)
         assertThat(visibleModel.icon.contentDescription)
             .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
+        assertThat(visibleModel.toggle).isEqualTo(KeyguardQuickAffordanceToggleState.NotSupported)
         job.cancel()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index 6ea1daa..e99c139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -17,7 +17,7 @@
 
 package com.android.systemui.keyguard.domain.quickaffordance
 
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -40,7 +40,7 @@
     override val state: Flow<KeyguardQuickAffordanceConfig.State> = _state
 
     override fun onQuickAffordanceClicked(
-        animationController: ActivityLaunchAnimator.Controller?,
+        expandable: Expandable?,
     ): OnClickedResult {
         return onClickedResult
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index dede4ec..a809f05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -20,7 +20,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
@@ -44,7 +44,7 @@
 class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock private lateinit var component: ControlsComponent
-    @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
+    @Mock private lateinit var expandable: Expandable
 
     private lateinit var underTest: HomeControlsKeyguardQuickAffordanceConfig
 
@@ -103,7 +103,7 @@
     fun `onQuickAffordanceClicked - canShowWhileLockedSetting is true`() = runBlockingTest {
         whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
 
-        val onClickedResult = underTest.onQuickAffordanceClicked(animationController)
+        val onClickedResult = underTest.onQuickAffordanceClicked(expandable)
 
         assertThat(onClickedResult).isInstanceOf(OnClickedResult.StartActivity::class.java)
         assertThat((onClickedResult as OnClickedResult.StartActivity).canShowWhileLocked).isTrue()
@@ -113,7 +113,7 @@
     fun `onQuickAffordanceClicked - canShowWhileLockedSetting is false`() = runBlockingTest {
         whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
 
-        val onClickedResult = underTest.onQuickAffordanceClicked(animationController)
+        val onClickedResult = underTest.onQuickAffordanceClicked(expandable)
 
         assertThat(onClickedResult).isInstanceOf(OnClickedResult.StartActivity::class.java)
         assertThat((onClickedResult as OnClickedResult.StartActivity).canShowWhileLocked).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 0a4478f..98dc4c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -24,11 +24,13 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.wallet.controller.QuickAccessWalletController
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.launchIn
@@ -40,7 +42,6 @@
 import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -135,8 +136,11 @@
     @Test
     fun onQuickAffordanceClicked() {
         val animationController: ActivityLaunchAnimator.Controller = mock()
+        val expandable: Expandable = mock {
+            whenever(this.activityLaunchController()).thenReturn(animationController)
+        }
 
-        assertThat(underTest.onQuickAffordanceClicked(animationController))
+        assertThat(underTest.onQuickAffordanceClicked(expandable))
             .isEqualTo(KeyguardQuickAffordanceConfig.OnClickedResult.Handled)
         verify(walletController)
             .startQuickAccessUiIntent(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 96544e7..d674c89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -20,7 +20,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.doze.util.BurnInHelperWrapper
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -31,6 +31,7 @@
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordanceToggleState
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -59,7 +60,7 @@
 @RunWith(JUnit4::class)
 class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
 
-    @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
+    @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var keyguardStateController: KeyguardStateController
@@ -130,6 +131,7 @@
             TestConfig(
                 isVisible = true,
                 isClickable = true,
+                isActivated = true,
                 icon = mock(),
                 canShowWhileLocked = false,
                 intent = Intent("action"),
@@ -505,6 +507,12 @@
                 }
                 KeyguardQuickAffordanceConfig.State.Visible(
                     icon = testConfig.icon ?: error("Icon is unexpectedly null!"),
+                    toggle =
+                        when (testConfig.isActivated) {
+                            true -> KeyguardQuickAffordanceToggleState.On
+                            false -> KeyguardQuickAffordanceToggleState.Off
+                            null -> KeyguardQuickAffordanceToggleState.NotSupported
+                        }
                 )
             } else {
                 KeyguardQuickAffordanceConfig.State.Hidden
@@ -521,12 +529,13 @@
         checkNotNull(viewModel)
         assertThat(viewModel.isVisible).isEqualTo(testConfig.isVisible)
         assertThat(viewModel.isClickable).isEqualTo(testConfig.isClickable)
+        assertThat(viewModel.isActivated).isEqualTo(testConfig.isActivated)
         if (testConfig.isVisible) {
             assertThat(viewModel.icon).isEqualTo(testConfig.icon)
             viewModel.onClicked.invoke(
                 KeyguardQuickAffordanceViewModel.OnClickedParameters(
                     configKey = configKey,
-                    animationController = animationController,
+                    expandable = expandable,
                 )
             )
             if (testConfig.intent != null) {
@@ -542,6 +551,7 @@
     private data class TestConfig(
         val isVisible: Boolean,
         val isClickable: Boolean = false,
+        val isActivated: Boolean = false,
         val icon: Icon? = null,
         val canShowWhileLocked: Boolean = false,
         val intent: Intent? = null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
index b8e9cf4..dc5522e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
@@ -82,7 +82,6 @@
         MockitoAnnotations.initMocks(this);
 
         mSessionTracker = new SessionTracker(
-                mContext,
                 mStatusBarService,
                 mAuthController,
                 mKeyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 2f52950..af53016 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -73,7 +73,7 @@
 
     @Test
     public void testOnMediaDataLoaded_complicationAddition() {
-        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
+        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mMediaDataManager,
                 mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
         sentinel.start();
 
@@ -94,7 +94,7 @@
 
     @Test
     public void testOnMediaDataRemoved_complicationRemoval() {
-        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
+        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mMediaDataManager,
                 mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
         sentinel.start();
 
@@ -114,7 +114,7 @@
 
     @Test
     public void testOnMediaDataLoaded_complicationRemoval() {
-        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
+        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mMediaDataManager,
                 mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
         sentinel.start();
 
@@ -139,7 +139,7 @@
     public void testOnMediaDataLoaded_mediaComplicationDisabled_doesNotAddComplication() {
         when(mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)).thenReturn(false);
 
-        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
+        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mMediaDataManager,
                 mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
 
         sentinel.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 0e9d279..6adce7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -80,6 +80,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
 import com.android.systemui.navigationbar.buttons.DeadZone;
@@ -197,6 +198,8 @@
     @Mock
     private UserContextProvider mUserContextProvider;
     @Mock
+    private WakefulnessLifecycle mWakefulnessLifecycle;
+    @Mock
     private Resources mResources;
     private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
     private DeviceConfigProxyFake mDeviceConfigProxyFake = new DeviceConfigProxyFake();
@@ -474,7 +477,8 @@
                 mNavigationBarTransitions,
                 mEdgeBackGestureHandler,
                 Optional.of(mock(BackAnimation.class)),
-                mUserContextProvider));
+                mUserContextProvider,
+                mWakefulnessLifecycle));
     }
 
     private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 4e9b232..c377c37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.power.PowerUI.WarningsUI;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -84,6 +85,7 @@
     private PowerUI mPowerUI;
     @Mock private EnhancedEstimates mEnhancedEstimates;
     @Mock private PowerManager mPowerManager;
+    @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock private IThermalService mThermalServiceMock;
     private IThermalEventListener mUsbThermalEventListener;
     private IThermalEventListener mSkinThermalEventListener;
@@ -680,7 +682,7 @@
     private void createPowerUi() {
         mPowerUI = new PowerUI(
                 mContext, mBroadcastDispatcher, mCommandQueue, mCentralSurfacesOptionalLazy,
-                mMockWarnings, mEnhancedEstimates, mPowerManager);
+                mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager);
         mPowerUI.mThermalService = mThermalServiceMock;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 2a4996f..760bb9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -192,16 +192,6 @@
         // UserManager change.
         assertThat(iconTint()).isNull()
 
-        // Trigger a user info change: there should now be a tint.
-        userInfoController.updateInfo { userAccount = "doe" }
-        assertThat(iconTint())
-            .isEqualTo(
-                Utils.getColorAttrDefaultColor(
-                    context,
-                    android.R.attr.colorForeground,
-                )
-            )
-
         // Make sure we don't tint the icon if it is a user image (and not the default image), even
         // in guest mode.
         userInfoController.updateInfo { this.picture = mock<UserIconDrawable>() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java
new file mode 100644
index 0000000..c6ce51a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class DraggableConstraintLayoutTest extends SysuiTestCase {
+
+    @Mock
+    DraggableConstraintLayout.SwipeDismissCallbacks mCallbacks;
+
+    private DraggableConstraintLayout mDraggableConstraintLayout;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mDraggableConstraintLayout = new DraggableConstraintLayout(mContext, null, 0);
+    }
+
+    @Test
+    public void test_dismissDoesNotCallSwipeInitiated() {
+        mDraggableConstraintLayout.setCallbacks(mCallbacks);
+
+        mDraggableConstraintLayout.dismiss();
+
+        verify(mCallbacks, never()).onSwipeDismissInitiated(any());
+    }
+
+    @Test
+    public void test_onTouchCallsOnInteraction() {
+        mDraggableConstraintLayout.setCallbacks(mCallbacks);
+
+        mDraggableConstraintLayout.onInterceptTouchEvent(
+                MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+
+        verify(mCallbacks).onInteraction();
+    }
+
+    @Test
+    public void test_callbacksNotSet() {
+        // just test that it doesn't throw an NPE
+        mDraggableConstraintLayout.onInterceptTouchEvent(
+                MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+        mDraggableConstraintLayout.onInterceptHoverEvent(
+                MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0));
+        mDraggableConstraintLayout.dismiss();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 37be343..c0dae03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -716,6 +716,40 @@
     }
 
     @Test
+    public void test_pulsing_onTouchEvent_noTracking() {
+        // GIVEN device is pulsing
+        mNotificationPanelViewController.setPulsing(true);
+
+        // WHEN touch DOWN & MOVE events received
+        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+                0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
+                0 /* metaState */));
+        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+                0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
+                0 /* metaState */));
+
+        // THEN touch is NOT tracked (since the device is pulsing)
+        assertThat(mNotificationPanelViewController.isTracking()).isFalse();
+    }
+
+    @Test
+    public void test_onTouchEvent_startTracking() {
+        // GIVEN device is NOT pulsing
+        mNotificationPanelViewController.setPulsing(false);
+
+        // WHEN touch DOWN & MOVE events received
+        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+                0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
+                0 /* metaState */));
+        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+                0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
+                0 /* metaState */));
+
+        // THEN touch is tracked
+        assertThat(mNotificationPanelViewController.isTracking()).isTrue();
+    }
+
+    @Test
     public void handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
         when(mCommandQueue.panelsEnabled()).thenReturn(false);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
index 3f641df..ca65987 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
@@ -91,6 +91,8 @@
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
 
+        when(mBindStage.tryGetStageParams(eq(mEntry))).thenReturn(new RowContentBindParams());
+
         mViewBinder.unbindHeadsUpView(mEntry);
         verify(mLogger).entryContentViewMarkedFreeable(eq(mEntry));
         verifyNoMoreInteractions(mLogger);
@@ -139,6 +141,8 @@
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
 
+        when(mBindStage.tryGetStageParams(eq(mEntry))).thenReturn(new RowContentBindParams());
+
         mViewBinder.unbindHeadsUpView(mEntry);
         verify(mLogger).currentOngoingBindingAborted(eq(mEntry));
         verify(mLogger).entryContentViewMarkedFreeable(eq(mEntry));
@@ -150,4 +154,30 @@
         verifyNoMoreInteractions(mLogger);
         clearInvocations(mLogger);
     }
+
+    @Test
+    public void testLoggingForLateUnbindFlow() {
+        AtomicReference<NotifBindPipeline.BindCallback> callback = new AtomicReference<>();
+        when(mBindStage.requestRebind(any(), any())).then(i -> {
+            callback.set(i.getArgument(1));
+            return new CancellationSignal();
+        });
+
+        mViewBinder.bindHeadsUpView(mEntry, null);
+        verify(mLogger).startBindingHun(eq(mEntry));
+        verifyNoMoreInteractions(mLogger);
+        clearInvocations(mLogger);
+
+        callback.get().onBindFinished(mEntry);
+        verify(mLogger).entryBoundSuccessfully(eq(mEntry));
+        verifyNoMoreInteractions(mLogger);
+        clearInvocations(mLogger);
+
+        when(mBindStage.tryGetStageParams(eq(mEntry))).thenReturn(null);
+
+        mViewBinder.unbindHeadsUpView(mEntry);
+        verify(mLogger).entryBindStageParamsNullOnUnbind(eq(mEntry));
+        verifyNoMoreInteractions(mLogger);
+        clearInvocations(mLogger);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index ad3bd71..7c99568 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -21,6 +21,10 @@
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -31,6 +35,7 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.Log;
 
 import androidx.test.filters.SmallTest;
 
@@ -100,6 +105,67 @@
         verify(mBinder).unbindContent(eq(mEntry), any(), eq(flags));
     }
 
+    class CountingWtfHandler implements Log.TerribleFailureHandler {
+        private Log.TerribleFailureHandler mOldHandler = null;
+        private int mWtfCount = 0;
+
+        public void register() {
+            mOldHandler = Log.setWtfHandler(this);
+        }
+
+        public void unregister() {
+            Log.setWtfHandler(mOldHandler);
+            mOldHandler = null;
+        }
+
+        @Override
+        public void onTerribleFailure(String tag, Log.TerribleFailure what, boolean system) {
+            mWtfCount++;
+        }
+
+        public int getWtfCount() {
+            return mWtfCount;
+        }
+    }
+
+    @Test
+    public void testGetStageParamsAfterCleanUp() {
+        // GIVEN an entry whose params have already been deleted.
+        RowContentBindParams originalParams = mRowContentBindStage.getStageParams(mEntry);
+        mRowContentBindStage.deleteStageParams(mEntry);
+
+        // WHEN a caller calls getStageParams.
+        CountingWtfHandler countingWtfHandler = new CountingWtfHandler();
+        countingWtfHandler.register();
+
+        RowContentBindParams blankParams = mRowContentBindStage.getStageParams(mEntry);
+
+        countingWtfHandler.unregister();
+
+        // THEN getStageParams logs a WTF and returns blank params created to avoid a crash.
+        assertEquals(1, countingWtfHandler.getWtfCount());
+        assertNotNull(blankParams);
+        assertNotSame(originalParams, blankParams);
+    }
+
+    @Test
+    public void testTryGetStageParamsAfterCleanUp() {
+        // GIVEN an entry whose params have already been deleted.
+        mRowContentBindStage.deleteStageParams(mEntry);
+
+        // WHEN a caller calls getStageParams.
+        CountingWtfHandler countingWtfHandler = new CountingWtfHandler();
+        countingWtfHandler.register();
+
+        RowContentBindParams nullParams = mRowContentBindStage.tryGetStageParams(mEntry);
+
+        countingWtfHandler.unregister();
+
+        // THEN getStageParams does NOT log a WTF and returns null to indicate missing params.
+        assertEquals(0, countingWtfHandler.getWtfCount());
+        assertNull(nullParams);
+    }
+
     @Test
     public void testRebindAllContentViews() {
         // GIVEN a view with content bound.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
index 76ecc1c..169f4fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
@@ -57,14 +57,18 @@
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.telephony.TelephonyListenerManager
 import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.legacyhelper.data.LegacyUserDataHelper
+import com.android.systemui.user.shared.model.UserActionModel
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.settings.GlobalSettings
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotNull
@@ -123,7 +127,7 @@
     private val ownerId = UserHandle.USER_SYSTEM
     private val ownerInfo = UserInfo(ownerId, "Owner", null,
             UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL or UserInfo.FLAG_INITIALIZED or
-                    UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM,
+                    UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM or UserInfo.FLAG_ADMIN,
             UserManager.USER_TYPE_FULL_SYSTEM)
     private val guestId = 1234
     private val guestInfo = UserInfo(guestId, "Guest", null,
@@ -597,6 +601,76 @@
     }
 
     @Test
+    fun testCanManageUser_userSwitcherEnabled_addUserWhenLocked() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(1)
+
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.ADD_USERS_WHEN_LOCKED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(1)
+        setupController()
+        assertTrue(userSwitcherController.canManageUsers())
+    }
+
+    @Test
+    fun testCanManageUser_userSwitcherDisabled_addUserWhenLocked() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.ADD_USERS_WHEN_LOCKED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(1)
+        setupController()
+        assertFalse(userSwitcherController.canManageUsers())
+    }
+
+    @Test
+    fun testCanManageUser_userSwitcherEnabled_isAdmin() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(1)
+
+        setupController()
+        assertTrue(userSwitcherController.canManageUsers())
+    }
+
+    @Test
+    fun testCanManageUser_userSwitcherDisabled_isAdmin() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+
+        setupController()
+        assertFalse(userSwitcherController.canManageUsers())
+    }
+
+    @Test
     fun addUserSwitchCallback() {
         val broadcastReceiverCaptor = argumentCaptor<BroadcastReceiver>()
         verify(broadcastDispatcher).registerReceiver(
@@ -632,4 +706,22 @@
         bgExecutor.runAllReady()
         verify(userManager).createGuest(context)
     }
+
+    @Test
+    fun onUserItemClicked_manageUsers() {
+        val manageUserRecord = LegacyUserDataHelper.createRecord(
+            mContext,
+            ownerId,
+            UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+            isRestricted = false,
+            isSwitchToEnabled = true
+        )
+
+        userSwitcherController.onUserListItemClicked(manageUserRecord, null)
+        val intentCaptor = kotlinArgumentCaptor<Intent>()
+        verify(activityStarter).startActivity(intentCaptor.capture(),
+            eq(true)
+        )
+        Truth.assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_USER_SETTINGS)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
deleted file mode 100644
index 3968bb7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.user
-
-import android.app.Application
-import android.os.UserManager
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
-import android.view.LayoutInflater
-import android.view.View
-import android.view.Window
-import android.window.OnBackInvokedCallback
-import android.window.OnBackInvokedDispatcher
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.policy.UserSwitcherController
-import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.any
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.doNothing
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@RunWithLooper(setAsMainLooper = true)
-class UserSwitcherActivityTest : SysuiTestCase() {
-    @Mock
-    private lateinit var activity: UserSwitcherActivity
-    @Mock
-    private lateinit var userSwitcherController: UserSwitcherController
-    @Mock
-    private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Mock
-    private lateinit var layoutInflater: LayoutInflater
-    @Mock
-    private lateinit var falsingCollector: FalsingCollector
-    @Mock
-    private lateinit var falsingManager: FalsingManager
-    @Mock
-    private lateinit var userManager: UserManager
-    @Mock
-    private lateinit var userTracker: UserTracker
-    @Mock
-    private lateinit var flags: FeatureFlags
-    @Mock
-    private lateinit var viewModelFactoryLazy: dagger.Lazy<UserSwitcherViewModel.Factory>
-    @Mock
-    private lateinit var onBackDispatcher: OnBackInvokedDispatcher
-    @Mock
-    private lateinit var decorView: View
-    @Mock
-    private lateinit var window: Window
-    @Mock
-    private lateinit var userSwitcherRootView: UserSwitcherRootView
-    @Captor
-    private lateinit var onBackInvokedCallback: ArgumentCaptor<OnBackInvokedCallback>
-    var isFinished = false
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        activity = spy(object : UserSwitcherActivity(
-            userSwitcherController,
-            broadcastDispatcher,
-            falsingCollector,
-            falsingManager,
-            userManager,
-            userTracker,
-            flags,
-            viewModelFactoryLazy,
-        ) {
-            override fun getOnBackInvokedDispatcher() = onBackDispatcher
-            override fun getMainExecutor(): Executor = FakeExecutor(FakeSystemClock())
-            override fun finish() {
-                isFinished = true
-            }
-        })
-        `when`(activity.window).thenReturn(window)
-        `when`(window.decorView).thenReturn(decorView)
-        `when`(activity.findViewById<UserSwitcherRootView>(R.id.user_switcher_root))
-                .thenReturn(userSwitcherRootView)
-        `when`(activity.findViewById<View>(R.id.cancel)).thenReturn(mock(View::class.java))
-        `when`(activity.findViewById<View>(R.id.add)).thenReturn(mock(View::class.java))
-        `when`(activity.application).thenReturn(mock(Application::class.java))
-        doNothing().`when`(activity).setContentView(anyInt())
-    }
-
-    @Test
-    fun testMaxColumns() {
-        assertThat(activity.getMaxColumns(3)).isEqualTo(4)
-        assertThat(activity.getMaxColumns(4)).isEqualTo(4)
-        assertThat(activity.getMaxColumns(5)).isEqualTo(3)
-        assertThat(activity.getMaxColumns(6)).isEqualTo(3)
-        assertThat(activity.getMaxColumns(7)).isEqualTo(4)
-        assertThat(activity.getMaxColumns(9)).isEqualTo(5)
-    }
-
-    @Test
-    fun onCreate_callbackRegistration() {
-        activity.createActivity()
-        verify(onBackDispatcher).registerOnBackInvokedCallback(
-                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any())
-
-        activity.destroyActivity()
-        verify(onBackDispatcher).unregisterOnBackInvokedCallback(any())
-    }
-
-    @Test
-    fun onBackInvokedCallback_finishesActivity() {
-        activity.createActivity()
-        verify(onBackDispatcher).registerOnBackInvokedCallback(
-                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), onBackInvokedCallback.capture())
-
-        onBackInvokedCallback.value.onBackInvoked()
-        assertThat(isFinished).isTrue()
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt
index 37c378c..1540f85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorRefactoredTest.kt
@@ -202,6 +202,7 @@
     fun `actions - device unlocked`() =
         runBlocking(IMMEDIATE) {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
+
             userRepository.setUserInfos(userInfos)
             userRepository.setSelectedUserInfo(userInfos[0])
             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
@@ -215,6 +216,7 @@
                         UserActionModel.ENTER_GUEST_MODE,
                         UserActionModel.ADD_USER,
                         UserActionModel.ADD_SUPERVISED_USER,
+                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                     )
                 )
 
@@ -276,6 +278,7 @@
                         UserActionModel.ENTER_GUEST_MODE,
                         UserActionModel.ADD_USER,
                         UserActionModel.ADD_SUPERVISED_USER,
+                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                     )
                 )
 
@@ -283,7 +286,7 @@
         }
 
     @Test
-    fun `actions - device locked - only guest action is shown`() =
+    fun `actions - device locked - only guest action and manage user is shown`() =
         runBlocking(IMMEDIATE) {
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
@@ -293,7 +296,13 @@
             var value: List<UserActionModel>? = null
             val job = underTest.actions.onEach { value = it }.launchIn(this)
 
-            assertThat(value).isEqualTo(listOf(UserActionModel.ENTER_GUEST_MODE))
+            assertThat(value)
+                .isEqualTo(
+                    listOf(
+                        UserActionModel.ENTER_GUEST_MODE,
+                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT
+                    )
+                )
 
             job.cancel()
         }
@@ -330,7 +339,7 @@
             underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
 
             val intentCaptor = kotlinArgumentCaptor<Intent>()
-            verify(activityStarter).startActivity(intentCaptor.capture(), eq(false))
+            verify(activityStarter).startActivity(intentCaptor.capture(), eq(true))
             assertThat(intentCaptor.value.action)
                 .isEqualTo(UserManager.ACTION_CREATE_SUPERVISED_USER)
             assertThat(intentCaptor.value.`package`).isEqualTo(SUPERVISED_USER_CREATION_APP_PACKAGE)
@@ -342,7 +351,7 @@
             underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
 
             val intentCaptor = kotlinArgumentCaptor<Intent>()
-            verify(activityStarter).startActivity(intentCaptor.capture(), eq(false))
+            verify(activityStarter).startActivity(intentCaptor.capture(), eq(true))
             assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_USER_SETTINGS)
         }
 
@@ -561,6 +570,7 @@
                         UserActionModel.ENTER_GUEST_MODE,
                         UserActionModel.ADD_USER,
                         UserActionModel.ADD_SUPERVISED_USER,
+                        UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
                     ),
             )
         }
@@ -705,7 +715,7 @@
             name,
             /* iconPath= */ "",
             /* flags= */ if (isPrimary) {
-                UserInfo.FLAG_PRIMARY
+                UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN
             } else {
                 0
             },
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index ca7fe0c..14cfce7 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -3722,21 +3722,34 @@
             Slog.w(TAG, "agentDisconnected: the backup agent for " + packageName
                     + " died: cancel current operations");
 
-            // handleCancel() causes the PerformFullTransportBackupTask to go on to
-            // tearDownAgentAndKill: that will unbindBackupAgent in the Activity Manager, so
-            // that the package being backed up doesn't get stuck in restricted mode until the
-            // backup time-out elapses.
-            for (int token : mOperationStorage.operationTokensForPackage(packageName)) {
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "agentDisconnected: will handleCancel(all) for token:"
-                            + Integer.toHexString(token));
+            // Offload operation cancellation off the main thread as the cancellation callbacks
+            // might call out to BackupTransport. Other operations started on the same package
+            // before the cancellation callback has executed will also be cancelled by the callback.
+            Runnable cancellationRunnable = () -> {
+                // handleCancel() causes the PerformFullTransportBackupTask to go on to
+                // tearDownAgentAndKill: that will unbindBackupAgent in the Activity Manager, so
+                // that the package being backed up doesn't get stuck in restricted mode until the
+                // backup time-out elapses.
+                for (int token : mOperationStorage.operationTokensForPackage(packageName)) {
+                    if (MORE_DEBUG) {
+                        Slog.d(TAG, "agentDisconnected: will handleCancel(all) for token:"
+                                + Integer.toHexString(token));
+                    }
+                    handleCancel(token, true /* cancelAll */);
                 }
-                handleCancel(token, true /* cancelAll */);
-            }
+            };
+            getThreadForAsyncOperation(/* operationName */ "agent-disconnected",
+                    cancellationRunnable).start();
+
             mAgentConnectLock.notifyAll();
         }
     }
 
+    @VisibleForTesting
+    Thread getThreadForAsyncOperation(String operationName, Runnable operation) {
+        return new Thread(operation, operationName);
+    }
+
     /**
      * An application being installed will need a restore pass, then the {@link PackageManager} will
      * need to be told when the restore is finished.
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index bccd8a0b..9ae8922 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -61,6 +62,7 @@
 public class UserBackupManagerServiceTest {
     private static final String TEST_PACKAGE = "package1";
     private static final String[] TEST_PACKAGES = new String[] { TEST_PACKAGE };
+    private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 1;
 
     @Mock Context mContext;
     @Mock IBackupManagerMonitor mBackupManagerMonitor;
@@ -179,6 +181,7 @@
 
         mService.agentDisconnected("com.android.foo");
 
+        mService.waitForAsyncOperation();
         verify(mOperationStorage).cancelOperation(eq(123), eq(true), any(IntConsumer.class));
         verify(mOperationStorage).cancelOperation(eq(456), eq(true), any());
         verify(mOperationStorage).cancelOperation(eq(789), eq(true), any());
@@ -207,6 +210,8 @@
         boolean isEnabledStatePersisted = false;
         boolean shouldUseNewBackupEligibilityRules = false;
 
+        private volatile Thread mWorkerThread = null;
+
         TestBackupService(Context context, PackageManager packageManager,
                 LifecycleOperationStorage operationStorage) {
             super(context, packageManager, operationStorage);
@@ -229,5 +234,23 @@
         boolean shouldUseNewBackupEligibilityRules() {
             return shouldUseNewBackupEligibilityRules;
         }
+
+        @Override
+        Thread getThreadForAsyncOperation(String operationName, Runnable operation) {
+            mWorkerThread = super.getThreadForAsyncOperation(operationName, operation);
+            return mWorkerThread;
+        }
+
+        private void waitForAsyncOperation() {
+            if (mWorkerThread == null) {
+                return;
+            }
+
+            try {
+                mWorkerThread.join(/* millis */ WORKER_THREAD_TIMEOUT_MILLISECONDS);
+            } catch (InterruptedException e) {
+                fail("Failed waiting for worker thread to complete: " + e.getMessage());
+            }
+        }
     }
 }