Merge "Add power stats collector and processor for Camera and GNSS" into main
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index ffb920b..15b13dc 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -757,15 +757,6 @@
     void addStartInfoTimestamp(int key, long timestampNs, int userId);
 
     /**
-    * Reports view related timestamps to be added to the calling apps most
-    * recent {@link ApplicationStartInfo}.
-    *
-    * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start
-    * @param framePresentedTimeNs        Clock monotonic time in nanoseconds of frame presented
-    */
-    oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs);
-
-    /**
      * Return a list of {@link ApplicationExitInfo} records.
      *
      * <p class="note"> Note: System stores these historical information in a ring buffer, older
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 61f1ee1..e2159f7 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,6 +38,9 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Binder;
@@ -193,7 +196,11 @@
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) {
-            mPromptInfo.setLogoRes(logoRes);
+            if (mPromptInfo.getLogoBitmap() != null) {
+                throw new IllegalStateException(
+                        "Exclusively one of logo resource or logo bitmap can be set");
+            }
+            mPromptInfo.setLogo(logoRes, convertDrawableToBitmap(mContext.getDrawable(logoRes)));
             return this;
         }
 
@@ -212,7 +219,11 @@
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
-            mPromptInfo.setLogoBitmap(logoBitmap);
+            if (mPromptInfo.getLogoRes() != -1) {
+                throw new IllegalStateException(
+                        "Exclusively one of logo resource or logo bitmap can be set");
+            }
+            mPromptInfo.setLogo(-1, logoBitmap);
             return this;
         }
 
@@ -1516,4 +1527,29 @@
     private static boolean isCredentialAllowed(@Authenticators.Types int allowedAuthenticators) {
         return (allowedAuthenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
     }
+
+    /** Converts {@code drawable} to a {@link Bitmap}. */
+    private static Bitmap convertDrawableToBitmap(Drawable drawable) {
+        if (drawable == null) {
+            return null;
+        }
+
+        if (drawable instanceof BitmapDrawable) {
+            return ((BitmapDrawable) drawable).getBitmap();
+        }
+
+        Bitmap bitmap;
+        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+            // Single color bitmap will be created of 1x1 pixel
+        } else {
+            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        }
+
+        final Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+        return bitmap;
+    }
 }
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index bb07b9b..f4a3c87 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -217,14 +217,17 @@
     // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)
 
     // Setters
-    public void setLogoRes(@DrawableRes int logoRes) {
-        mLogoRes = logoRes;
-        checkOnlyOneLogoSet();
-    }
 
-    public void setLogoBitmap(@NonNull Bitmap logoBitmap) {
+    /**
+     * Sets logo res and bitmap
+     *
+     * @param logoRes    The logo res set by the app; Or -1 if the app sets bitmap directly.
+     * @param logoBitmap The bitmap from logoRes if the app sets logoRes; Or the bitmap set by the
+     *                   app directly.
+     */
+    public void setLogo(@DrawableRes int logoRes, @NonNull Bitmap logoBitmap) {
+        mLogoRes = logoRes;
         mLogoBitmap = logoBitmap;
-        checkOnlyOneLogoSet();
     }
 
     public void setLogoDescription(@NonNull String logoDescription) {
@@ -326,13 +329,29 @@
     }
 
     // Getters
+
+    /**
+     * Returns the logo bitmap either from logo resource or bitmap passed in from the app.
+     */
+    public Bitmap getLogo() {
+        return mLogoBitmap;
+    }
+
+    /**
+     * Returns the logo res set by the app.
+     */
     @DrawableRes
     public int getLogoRes() {
         return mLogoRes;
     }
 
+    /**
+     * Returns the logo bitmap set by the app.
+     */
     public Bitmap getLogoBitmap() {
-        return mLogoBitmap;
+        // If mLogoRes has been set, return null since mLogoBitmap is from the res, but not from
+        // the app directly.
+        return mLogoRes == -1 ? mLogoBitmap : null;
     }
 
     public String getLogoDescription() {
@@ -436,10 +455,4 @@
         return mComponentNameForConfirmDeviceCredentialActivity;
     }
 
-    private void checkOnlyOneLogoSet() {
-        if (mLogoRes != -1 && mLogoBitmap != null) {
-            throw new IllegalStateException(
-                    "Exclusively one of logo resource or logo bitmap can be set");
-        }
-    }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a11bb78..a26150c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -177,7 +177,6 @@
 import android.graphics.RenderNode;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
-import android.hardware.SyncFence;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.display.DisplayManagerGlobal;
@@ -220,7 +219,6 @@
 import android.view.InputDevice.InputSourceClass;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceControl.TransactionStats;
 import android.view.View.AttachInfo;
 import android.view.View.FocusDirection;
 import android.view.View.MeasureSpec;
@@ -295,7 +293,6 @@
 import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -1192,13 +1189,6 @@
     private String mFpsTraceName;
     private String mLargestViewTraceName;
 
-    private final boolean mAppStartInfoTimestampsFlagValue;
-    @GuardedBy("this")
-    private boolean mAppStartTimestampsSent = false;
-    private boolean mAppStartTrackingStarted = false;
-    private long mRenderThreadDrawStartTimeNs = -1;
-    private long mFirstFramePresentedTimeNs = -1;
-
     private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
     private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue;
     private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
@@ -1316,8 +1306,6 @@
         } else {
             mSensitiveContentProtectionService = null;
         }
-
-        mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps();
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -2590,12 +2578,6 @@
                     notifySurfaceDestroyed();
                 }
                 destroySurface();
-
-                // Reset so they can be sent again for warm starts.
-                mAppStartTimestampsSent = false;
-                mAppStartTrackingStarted = false;
-                mRenderThreadDrawStartTimeNs = -1;
-                mFirstFramePresentedTimeNs = -1;
             }
         }
     }
@@ -4394,30 +4376,6 @@
                 reportDrawFinished(t, seqId);
             }
         });
-
-        // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if
-        // {link mTransactionCompletedTimeNs} has already been set.
-        if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) {
-            mAppStartTrackingStarted = true;
-            Transaction transaction = new Transaction();
-            transaction.addTransactionCompletedListener(mExecutor,
-                    new Consumer<TransactionStats>() {
-                        @Override
-                        public void accept(TransactionStats transactionStats) {
-                            SyncFence presentFence = transactionStats.getPresentFence();
-                            if (presentFence.awaitForever()) {
-                                if (mFirstFramePresentedTimeNs == -1) {
-                                    // Only trigger once per {@link ViewRootImpl} instance.
-                                    mFirstFramePresentedTimeNs = presentFence.getSignalTime();
-                                    maybeSendAppStartTimes();
-                                }
-                            }
-                            presentFence.close();
-                        }
-                    });
-            applyTransactionOnDraw(transaction);
-        }
-
         if (DEBUG_BLAST) {
             Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName());
         }
@@ -4425,45 +4383,6 @@
         mWmsRequestSyncGroup.add(this, null /* runnable */);
     }
 
-    private void maybeSendAppStartTimes() {
-        synchronized (this) {
-            if (mAppStartTimestampsSent) {
-                // Don't send timestamps more than once.
-                return;
-            }
-
-            // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not
-            // post to main thread and check if we have it there.
-            if (mRenderThreadDrawStartTimeNs != -1) {
-                sendAppStartTimesLocked();
-            } else {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        synchronized (ViewRootImpl.this) {
-                            if (mRenderThreadDrawStartTimeNs == -1) {
-                                return;
-                            }
-                            sendAppStartTimesLocked();
-                        }
-                    }
-                });
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    private void sendAppStartTimesLocked() {
-        try {
-            ActivityManager.getService().reportStartInfoViewTimestamps(
-                    mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs);
-            mAppStartTimestampsSent = true;
-        } catch (RemoteException e) {
-            // Ignore, timestamps may be lost.
-            if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e);
-        }
-    }
-
     /**
      * Helper used to notify the service to block projection when a sensitive
      * view (the view displays sensitive content) is attached to the window.
@@ -5650,13 +5569,7 @@
                     registerCallbackForPendingTransactions();
                 }
 
-                long timeNs = SystemClock.uptimeNanos();
                 mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
-
-                // Only trigger once per {@link ViewRootImpl} instance.
-                if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) {
-                    mRenderThreadDrawStartTimeNs = timeNs;
-                }
             } else {
                 // If we get here with a disabled & requested hardware renderer, something went
                 // wrong (an invalidate posted right before we destroyed the hardware surface
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 8718fb1..5b09a8b 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -181,26 +181,6 @@
     }
 
     /**
-     * Converts {@link Settings.Secure} key to {@link UserShortcutType}.
-     *
-     * @param key The shortcut key.
-     * @return Mapping type in Settings.
-     */
-    public static int convertToType(String key) {
-        return switch (key) {
-            case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE;
-            case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS;
-            case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE;
-            case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED ->
-                    UserShortcutType.TRIPLETAP;
-            case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED ->
-                    UserShortcutType.TWOFINGER_DOUBLETAP;
-            default -> throw new IllegalArgumentException(
-                    "Unsupported user shortcut key: " + key);
-        };
-    }
-
-    /**
      * Updates an accessibility state if the accessibility service is a Always-On a11y service,
      * a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON
      * <p>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4dfe000..f43351a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -7076,4 +7076,8 @@
 
     <!-- Whether the system uses auto-suspend mode. -->
     <bool name="config_useAutoSuspend">true</bool>
+
+    <!-- Whether to show GAIA education screen during account login of private space setup.
+         OEM/Partner can explicitly opt to disable the screen. -->
+    <bool name="config_enableGaiaEducationInPrivateSpace">true</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cc74d02..639b746 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -521,6 +521,7 @@
   <java-symbol type="bool" name="config_preferKeepClearForFocus" />
   <java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/>
   <java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/>
+  <java-symbol type="bool" name="config_enableGaiaEducationInPrivateSpace"/>
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
index fc66ad6..d14234e 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
@@ -20,7 +20,7 @@
 import kotlin.random.Random
 
 // artificially speed up or slow down the simulation
-const val TIME_SCALE = 1f
+const val TIME_SCALE = 1f // simulation seconds per wall clock second
 
 // if it's been over 1 real second since our last timestep, don't simulate that elapsed time.
 // this allows the simulation to "pause" when, for example, the activity pauses
@@ -36,6 +36,19 @@
     fun postUpdate(sim: Simulator, dt: Float)
 }
 
+interface Removable {
+    fun canBeRemoved(): Boolean
+}
+
+class Fuse(var lifetime: Float) : Removable {
+    fun update(dt: Float) {
+        lifetime -= dt
+    }
+    override fun canBeRemoved(): Boolean {
+        return lifetime < 0
+    }
+}
+
 open class Body(var name: String = "Unknown") : Entity {
     var pos = Vec2.Zero
     var opos = Vec2.Zero
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
index 1e54569..d6fbc11 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
@@ -43,11 +43,9 @@
 const val MAIN_ENGINE_ACCEL = 1000f // thrust effect, pixels per second squared
 const val LAUNCH_MECO = 2f // how long to suspend gravity when launching
 
-const val SCALED_THRUST = true
+const val LANDING_REMOVAL_TIME = 3600f // one hour of simulation time
 
-interface Removable {
-    fun canBeRemoved(): Boolean
-}
+const val SCALED_THRUST = true
 
 open class Planet(
     val orbitCenter: Vec2,
@@ -321,7 +319,7 @@
                         //
                         (1..10).forEach {
                             Spark(
-                                    lifetime = rng.nextFloatInRange(0.5f, 2f),
+                                    ttl = rng.nextFloatInRange(0.5f, 2f),
                                     style = Spark.Style.DOT,
                                     color = Color.White,
                                     size = 1f
@@ -359,13 +357,22 @@
         entities
             .filterIsInstance<Removable>()
             .filter(predicate = Removable::canBeRemoved)
-            .filterIsInstance<Entity>()
-            .forEach { remove(it) }
+            .forEach { remove(it as Entity) }
+
+        constraints
+            .filterIsInstance<Removable>()
+            .filter(predicate = Removable::canBeRemoved)
+            .forEach { remove(it as Constraint) }
     }
 }
 
-class Landing(var ship: Spacecraft?, val planet: Planet, val angle: Float, val text: String = "") :
-    Constraint {
+class Landing(
+    var ship: Spacecraft?,
+    val planet: Planet,
+    val angle: Float,
+    val text: String = "",
+    private val fuse: Fuse = Fuse(LANDING_REMOVAL_TIME)
+) : Constraint, Removable by fuse {
     override fun solve(sim: Simulator, dt: Float) {
         ship?.let { ship ->
             val landingVector = Vec2.makeWithAngleMag(angle, ship.radius + planet.radius)
@@ -373,17 +380,20 @@
             ship.pos = (ship.pos * 0.5f) + (desiredPos * 0.5f) // @@@ FIXME
             ship.angle = angle
         }
+
+        fuse.update(dt)
     }
 }
 
 class Spark(
-    var lifetime: Float,
+    var ttl: Float,
     collides: Boolean = false,
     mass: Float = 0f,
     val style: Style = Style.LINE,
     val color: Color = Color.Gray,
-    val size: Float = 2f
-) : Removable, Body() {
+    val size: Float = 2f,
+    val fuse: Fuse = Fuse(ttl)
+) : Removable by fuse, Body(name = "Spark") {
     enum class Style {
         LINE,
         LINE_ABSOLUTE,
@@ -398,10 +408,7 @@
     }
     override fun update(sim: Simulator, dt: Float) {
         super.update(sim, dt)
-        lifetime -= dt
-    }
-    override fun canBeRemoved(): Boolean {
-        return lifetime < 0
+        fuse.update(dt)
     }
 }
 
@@ -486,11 +493,11 @@
             // exhaust
             sim.add(
                 Spark(
-                        lifetime = sim.rng.nextFloatInRange(0.5f, 1f),
+                        ttl = sim.rng.nextFloatInRange(0.5f, 1f),
                         collides = true,
                         mass = 1f,
                         style = Spark.Style.RING,
-                        size = 3f,
+                        size = 1f,
                         color = Color(0x40FFFFFF)
                     )
                     .also { spark ->
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
index 974784d..ed3ebc7 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
@@ -30,6 +30,7 @@
 import androidx.core.math.MathUtils.clamp
 import com.android.egg.flags.Flags.flagFlag
 import java.lang.Float.max
+import kotlin.math.exp
 import kotlin.math.sqrt
 
 const val DRAW_ORBITS = true
@@ -289,7 +290,8 @@
 
 fun ZoomedDrawScope.drawSpark(spark: Spark) {
     with(spark) {
-        if (lifetime < 0) return
+        if (fuse.lifetime < 0) return
+        val life = 1f - fuse.lifetime / ttl
         when (style) {
             Spark.Style.LINE ->
                 if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size)
@@ -297,7 +299,13 @@
                 if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size / zoom)
             Spark.Style.DOT -> drawCircle(color, size, pos)
             Spark.Style.DOT_ABSOLUTE -> drawCircle(color, size, pos / zoom)
-            Spark.Style.RING -> drawCircle(color, size, pos, style = Stroke(width = 1f / zoom))
+            Spark.Style.RING ->
+                drawCircle(
+                    color = color.copy(alpha = color.alpha * (1f - life)),
+                    radius = exp(lerp(size, 3f * size, life)) - 1f,
+                    center = pos,
+                    style = Stroke(width = 1f / zoom)
+                )
         }
     }
 }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 29b57c9..85aa33a 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -981,6 +981,16 @@
 }
 
 flag {
+  name: "media_controls_lockscreen_shade_bug_fix"
+  namespace: "systemui"
+  description: "Use ShadeInteractor for media location changes"
+  bug: "319244625"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   namespace: "systemui"
   name: "enable_view_capture_tracing"
   description: "Enables view capture tracing in System UI."
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
similarity index 87%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
index f561c53..d84d151 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
 
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
@@ -44,6 +44,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.core.FakeLogBuffer;
 import com.android.systemui.res.R;
@@ -54,7 +58,6 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
-import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.time.DateFormatUtil;
 
 import org.junit.Before;
@@ -72,14 +75,12 @@
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidJUnit4.class)
-public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+public class AmbientStatusBarViewControllerTest extends SysuiTestCase {
     private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING =
             "{count, plural, =1 {# notification} other {# notifications}}";
 
     @Mock
-    MockDreamOverlayStatusBarView mView;
-    @Mock
-    TouchInsetManager.TouchInsetSession mTouchSession;
+    MockAmbientStatusBarView mView;
     @Mock
     Resources mResources;
     @Mock
@@ -114,9 +115,11 @@
 
     private final Executor mMainExecutor = Runnable::run;
 
-    private final FakeWifiRepository mWifiRepository = new FakeWifiRepository();
+    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
 
-    DreamOverlayStatusBarViewController mController;
+    private final FakeWifiRepository mWifiRepository = mKosmos.getFakeWifiRepository();
+
+    AmbientStatusBarViewController mController;
 
     @Before
     public void setup() {
@@ -128,11 +131,10 @@
         doCallRealMethod().when(mView).getVisibility();
         when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
 
-        mController = new DreamOverlayStatusBarViewController(
+        mController = new AmbientStatusBarViewController(
                 mView,
                 mResources,
                 mMainExecutor,
-                mTouchSession,
                 mAlarmManager,
                 mNextAlarmController,
                 mDateFormatUtil,
@@ -143,7 +145,7 @@
                 mDreamOverlayStatusBarItemsProvider,
                 mDreamOverlayStateController,
                 mUserTracker,
-                mWifiRepository,
+                mKosmos.getWifiInteractor(),
                 mLogBuffer);
     }
 
@@ -164,7 +166,7 @@
         mController.updateWifiUnavailableStatusIcon(false);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
+                AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
     }
 
     @Test
@@ -173,7 +175,7 @@
         mController.updateWifiUnavailableStatusIcon(true);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
+                AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
     }
 
     @Test
@@ -183,7 +185,7 @@
         when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(alarmClockInfo);
         mController.onViewAttached();
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any());
+                eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any());
     }
 
     @Test
@@ -191,7 +193,7 @@
         when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(null);
         mController.onViewAttached();
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull());
+                eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull());
     }
 
     @Test
@@ -202,7 +204,7 @@
                 .thenReturn(false);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, true, null);
+                AmbientStatusBarView.STATUS_ICON_MIC_DISABLED, true, null);
     }
 
     @Test
@@ -213,7 +215,7 @@
                 .thenReturn(true);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null);
+                AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null);
     }
 
     @Test
@@ -224,7 +226,7 @@
                 .thenReturn(true);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
+                AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
     }
 
     @Test
@@ -237,7 +239,7 @@
         callbackCapture.getValue().onNotificationCountChanged(1);
 
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
     }
 
     @Test
@@ -250,16 +252,15 @@
         callbackCapture.getValue().onNotificationCountChanged(0);
 
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull());
     }
 
     @Test
     public void testNotificationsIconNotShownWhenCountProviderAbsent() {
-        DreamOverlayStatusBarViewController controller = new DreamOverlayStatusBarViewController(
+        AmbientStatusBarViewController controller = new AmbientStatusBarViewController(
                 mView,
                 mResources,
                 mMainExecutor,
-                mTouchSession,
                 mAlarmManager,
                 mNextAlarmController,
                 mDateFormatUtil,
@@ -270,11 +271,11 @@
                 mDreamOverlayStatusBarItemsProvider,
                 mDreamOverlayStateController,
                 mUserTracker,
-                mWifiRepository,
+                mKosmos.getWifiInteractor(),
                 mLogBuffer);
         controller.onViewAttached();
         verify(mView, never()).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
     }
 
     @Test
@@ -283,7 +284,7 @@
                 Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
     }
 
     @Test
@@ -292,7 +293,7 @@
                 Settings.Global.ZEN_MODE_OFF);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
     }
 
     @Test
@@ -322,7 +323,7 @@
         callbackCapture.getValue().onNotificationCountChanged(1);
 
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
     }
 
     @Test
@@ -335,7 +336,7 @@
         callbackCapture.getValue().onNotificationCountChanged(0);
 
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any());
     }
 
     @Test
@@ -354,7 +355,7 @@
                 SensorPrivacyManager.Sensors.MICROPHONE, true);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
+                AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
     }
 
     @Test
@@ -369,7 +370,7 @@
         callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
     }
 
     @Test
@@ -384,7 +385,7 @@
         callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
     }
 
     @Test
@@ -399,7 +400,7 @@
         callbackCapture.getValue().onStateChanged();
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null);
+                AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null);
     }
 
     @Test
@@ -460,7 +461,7 @@
 
         final ArgumentCaptor<DreamOverlayStatusBarItemsProvider.Callback>
                 callbackCapture = ArgumentCaptor.forClass(
-                        DreamOverlayStatusBarItemsProvider.Callback.class);
+                DreamOverlayStatusBarItemsProvider.Callback.class);
         verify(mDreamOverlayStatusBarItemsProvider).addCallback(callbackCapture.capture());
         callbackCapture.getValue().onStatusBarItemsChanged(List.of(mStatusBarItem));
 
@@ -532,10 +533,10 @@
         callback.onStateChanged();
     }
 
-    private static class MockDreamOverlayStatusBarView extends DreamOverlayStatusBarView {
+    private static class MockAmbientStatusBarView extends AmbientStatusBarView {
         private int mVisibility = View.VISIBLE;
 
-        private MockDreamOverlayStatusBarView(Context context) {
+        private MockAmbientStatusBarView(Context context) {
             super(context);
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 9c2791f..75a77cf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -131,8 +131,9 @@
     negativeButton: String = "neg",
 ): PromptInfo {
     val info = PromptInfo()
-    info.logoRes = logoRes
-    info.logoBitmap = logoBitmap
+    if (logoBitmap != null) {
+        info.setLogo(logoRes, logoBitmap)
+    }
     info.logoDescription = logoDescription
     info.title = title
     info.subtitle = subtitle
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 86fdaa5..73ef775 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -7,6 +7,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
 import com.android.systemui.complication.ComplicationHostViewController
 import com.android.systemui.dreams.ui.viewmodel.DreamViewModel
 import com.android.systemui.log.core.FakeLogBuffer
@@ -43,7 +44,7 @@
     @Mock private lateinit var mockAnimator: AnimatorSet
     @Mock private lateinit var blurUtils: BlurUtils
     @Mock private lateinit var hostViewController: ComplicationHostViewController
-    @Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController
+    @Mock private lateinit var statusBarViewController: AmbientStatusBarViewController
     @Mock private lateinit var stateController: DreamOverlayStateController
     @Mock private lateinit var transitionViewModel: DreamViewModel
     private val logBuffer = FakeLogBuffer.Factory.create()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index f5c86e0..4c8c113 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -47,6 +47,7 @@
 import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
 import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
@@ -55,6 +56,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.touch.TouchInsetManager;
 
 import kotlinx.coroutines.CoroutineDispatcher;
 
@@ -80,7 +82,7 @@
     ViewTreeObserver mViewTreeObserver;
 
     @Mock
-    DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
+    AmbientStatusBarViewController mAmbientStatusBarViewController;
 
     @Mock
     LowLightTransitionCoordinator mLowLightTransitionCoordinator;
@@ -131,6 +133,8 @@
     CommunalInteractor mCommunalInteractor;
     @Mock
     private DreamManager mDreamManager;
+    @Mock
+    private TouchInsetManager.TouchInsetSession mTouchInsetSession;
 
     DreamOverlayContainerViewController mController;
 
@@ -150,8 +154,9 @@
                 mComplicationHostViewController,
                 mDreamOverlayContentView,
                 mHubGestureIndicatorView,
-                mDreamOverlayStatusBarViewController,
+                mAmbientStatusBarViewController,
                 mLowLightTransitionCoordinator,
+                mTouchInsetSession,
                 mBlurUtils,
                 mHandler,
                 mDispatcher,
@@ -190,7 +195,7 @@
     @Test
     public void testDreamOverlayStatusBarViewControllerInitialized() {
         mController.init();
-        verify(mDreamOverlayStatusBarViewController).init();
+        verify(mAmbientStatusBarViewController).init();
     }
 
     @Test
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/ambient_status_bar_view.xml
similarity index 97%
rename from packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
rename to packages/SystemUI/res/layout/ambient_status_bar_view.xml
index ec2edb5..7d765ce 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/ambient_status_bar_view.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.dreams.DreamOverlayStatusBarView
+<com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/dream_overlay_status_bar"
@@ -118,4 +118,4 @@
             android:contentDescription="@string/assistant_attention_content_description" />
 
     </LinearLayout>
-</com.android.systemui.dreams.DreamOverlayStatusBarView>
+</com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView>
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 4234fca5..dcd3fa6 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -44,5 +44,5 @@
         app:layout_constraintBottom_toBottomOf="parent"
         />
 
-    <include layout="@layout/dream_overlay_status_bar_view" />
+    <include layout="@layout/ambient_status_bar_view" />
 </com.android.systemui.dreams.DreamOverlayContainerView>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
index 8979ef1..12d881b 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -25,7 +25,11 @@
 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
 import android.content.Context
 import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.Canvas
 import android.graphics.Insets
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
 import android.hardware.biometrics.BiometricManager.Authenticators
 import android.hardware.biometrics.PromptInfo
 import android.hardware.biometrics.SensorPropertiesInternal
@@ -122,4 +126,26 @@
         return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars())
             ?: Insets.NONE
     }
+
+    /** Converts `drawable` to a [Bitmap]. */
+    @JvmStatic
+    fun Drawable?.toBitmap(): Bitmap? {
+        if (this == null) {
+            return null
+        }
+        if (this is BitmapDrawable) {
+            return bitmap
+        }
+        val bitmap: Bitmap =
+            if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
+                Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+                // Single color bitmap will be created of 1x1 pixel
+            } else {
+                Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
+            }
+        val canvas = Canvas(bitmap)
+        setBounds(0, 0, canvas.width, canvas.height)
+        draw(canvas)
+        return bitmap
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
index ea00398..b0314d8 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
@@ -16,11 +16,19 @@
 
 package com.android.systemui.ambient.dagger
 
+import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
 import com.android.systemui.ambient.touch.dagger.InputSessionComponent
 import dagger.Module
 
-@Module(subcomponents = [AmbientTouchComponent::class, InputSessionComponent::class])
+@Module(
+    subcomponents =
+        [
+            AmbientStatusBarComponent::class,
+            AmbientTouchComponent::class,
+            InputSessionComponent::class,
+        ]
+)
 interface AmbientModule {
     companion object {
         const val TOUCH_HANDLERS = "touch_handlers"
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt
new file mode 100644
index 0000000..8ad4d00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.ambient.statusbar.dagger
+
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+/**
+ * [AmbientStatusBarComponent] can be used for displaying a status bar over ambient surfaces like
+ * the dream or communal hub.
+ */
+@Subcomponent
+interface AmbientStatusBarComponent {
+    @Subcomponent.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance view: AmbientStatusBarView,
+        ): AmbientStatusBarComponent
+    }
+
+    /** Builds a [AmbientStatusBarViewController] */
+    fun getController(): AmbientStatusBarViewController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
rename to packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
index 8e77079..aa96231 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -39,10 +39,10 @@
 import java.util.Objects;
 
 /**
- * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
+ * {@link AmbientStatusBarView} is the view responsible for displaying the status bar in a
  * dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
  */
-public class DreamOverlayStatusBarView extends ConstraintLayout {
+public class AmbientStatusBarView extends ConstraintLayout {
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "STATUS_ICON_" }, value = {
@@ -76,20 +76,20 @@
     private static final float KEY_SHADOW_ALPHA = 0.35f;
     private static final float AMBIENT_SHADOW_ALPHA = 0.4f;
 
-    public DreamOverlayStatusBarView(Context context) {
+    public AmbientStatusBarView(Context context) {
         this(context, null);
     }
 
-    public DreamOverlayStatusBarView(Context context, AttributeSet attrs) {
+    public AmbientStatusBarView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AmbientStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
         mContext = context;
     }
 
-    public DreamOverlayStatusBarView(
+    public AmbientStatusBarView(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
rename to packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
index da72a56..a242d5a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
 
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
@@ -31,20 +31,22 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamLogger;
+import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider;
 import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
-import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.dagger.DreamLog;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
-import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.time.DateFormatUtil;
 
@@ -59,13 +61,11 @@
 import javax.inject.Inject;
 
 /**
- * View controller for {@link DreamOverlayStatusBarView}.
+ * View controller for {@link AmbientStatusBarView}.
  */
-@DreamOverlayComponent.DreamOverlayScope
-public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
+public class AmbientStatusBarViewController extends ViewController<AmbientStatusBarView> {
     private static final String TAG = "DreamStatusBarCtrl";
 
-    private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
     private final NextAlarmController mNextAlarmController;
     private final AlarmManager mAlarmManager;
     private final Resources mResources;
@@ -76,7 +76,7 @@
     private final ZenModeController mZenModeController;
     private final DreamOverlayStateController mDreamOverlayStateController;
     private final UserTracker mUserTracker;
-    private final WifiRepository mWifiRepository;
+    private final WifiInteractor mWifiInteractor;
     private final StatusBarWindowStateController mStatusBarWindowStateController;
     private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider;
     private final Executor mMainExecutor;
@@ -115,7 +115,7 @@
 
     private final DreamOverlayNotificationCountProvider.Callback mNotificationCountCallback =
             notificationCount -> showIcon(
-                    DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS,
+                    AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS,
                     notificationCount > 0,
                     notificationCount > 0
                             ? buildNotificationsContentDescription(notificationCount)
@@ -125,11 +125,10 @@
             this::onStatusBarItemsChanged;
 
     @Inject
-    public DreamOverlayStatusBarViewController(
-            DreamOverlayStatusBarView view,
+    public AmbientStatusBarViewController(
+            AmbientStatusBarView view,
             @Main Resources resources,
             @Main Executor mainExecutor,
-            TouchInsetManager.TouchInsetSession touchInsetSession,
             AlarmManager alarmManager,
             NextAlarmController nextAlarmController,
             DateFormatUtil dateFormatUtil,
@@ -140,12 +139,11 @@
             DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
             DreamOverlayStateController dreamOverlayStateController,
             UserTracker userTracker,
-            WifiRepository wifiRepository,
+            WifiInteractor wifiInteractor,
             @DreamLog LogBuffer logBuffer) {
         super(view);
         mResources = resources;
         mMainExecutor = mainExecutor;
-        mTouchInsetSession = touchInsetSession;
         mAlarmManager = alarmManager;
         mNextAlarmController = nextAlarmController;
         mDateFormatUtil = dateFormatUtil;
@@ -156,7 +154,7 @@
         mZenModeController = zenModeController;
         mDreamOverlayStateController = dreamOverlayStateController;
         mUserTracker = userTracker;
-        mWifiRepository = wifiRepository;
+        mWifiInteractor = wifiInteractor;
         mLogger = new DreamLogger(logBuffer, TAG);
 
         // Register to receive show/hide updates for the system status bar. Our custom status bar
@@ -170,7 +168,7 @@
 
         collectFlow(
                 mView,
-                mWifiRepository.getWifiNetwork(),
+                mWifiInteractor.getWifiNetwork(),
                 network -> updateWifiUnavailableStatusIcon(
                         network instanceof WifiNetworkModel.Active));
 
@@ -202,7 +200,6 @@
         mView.removeAllExtraStatusBarItemViews();
         mDreamOverlayStateController.setDreamOverlayStatusBarVisible(false);
         mDreamOverlayStateController.removeCallback(mDreamOverlayStateCallback);
-        mTouchInsetSession.clear();
 
         mIsAttached = false;
     }
@@ -212,7 +209,7 @@
      *
      * No-op if the dream overlay status bar should not be shown.
      */
-    protected void setFadeAmount(float fadeAmount, boolean fadingOut) {
+    public void setFadeAmount(float fadeAmount, boolean fadingOut) {
         updateVisibility();
 
         if (mView.getVisibility() != View.VISIBLE) {
@@ -240,7 +237,7 @@
 
     @VisibleForTesting
     void updateWifiUnavailableStatusIcon(boolean available) {
-        showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
+        showIcon(AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
                 R.string.wifi_unavailable_dream_overlay_content_description);
     }
 
@@ -249,13 +246,13 @@
                 mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
         final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET,
+                AmbientStatusBarView.STATUS_ICON_ALARM_SET,
                 hasAlarm,
                 hasAlarm ? buildAlarmContentDescription(alarm) : null);
     }
 
     private void updateAssistantAttentionIcon() {
-        showIcon(DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
+        showIcon(AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
                 mDreamOverlayStateController.hasAssistantAttention(),
                 R.string.assistant_attention_content_description);
     }
@@ -284,17 +281,17 @@
                 .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE);
         final boolean cameraBlocked = mSensorPrivacyController
                 .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
-        @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
+        @AmbientStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
+                AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED,
                 !micBlocked && cameraBlocked,
                 R.string.camera_blocked_dream_overlay_content_description);
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
+                AmbientStatusBarView.STATUS_ICON_MIC_DISABLED,
                 micBlocked && !cameraBlocked,
                 R.string.microphone_blocked_dream_overlay_content_description);
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+                AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
                 micBlocked && cameraBlocked,
                 R.string.camera_and_microphone_blocked_dream_overlay_content_description);
     }
@@ -308,24 +305,24 @@
 
     private void updatePriorityModeStatusIcon() {
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
                 mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF,
                 R.string.priority_mode_dream_overlay_content_description);
     }
 
-    private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show,
+    private void showIcon(@AmbientStatusBarView.StatusIconType int iconType, boolean show,
             int contentDescriptionResId) {
         showIcon(iconType, show, mResources.getString(contentDescriptionResId));
     }
 
     private void showIcon(
-            @DreamOverlayStatusBarView.StatusIconType int iconType,
+            @AmbientStatusBarView.StatusIconType int iconType,
             boolean show,
             @Nullable String contentDescription) {
         mMainExecutor.execute(() -> {
             if (mIsAttached) {
                 mLogger.logShowOrHideStatusBarItem(
-                        show, DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
+                        show, AmbientStatusBarView.getLoggableStatusIconType(iconType));
                 mView.showIcon(iconType, show, contentDescription);
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index 9b14d6f..2fa4a89 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -139,9 +139,10 @@
             }
             .stateIn(
                 backgroundScope,
-                started = SharingStarted.WhileSubscribed(),
+                started = SharingStarted.Eagerly,
                 initialValue = false,
             )
+
     private fun dpiFromPx(size: Float, densityDpi: Int): Float {
         val densityRatio = densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT
         return size / densityRatio
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 4f96c1e..348b423 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -40,8 +40,7 @@
             operationInfo = operationInfo,
             showEmergencyCallButton = info.isShowEmergencyCallButton
         ) {
-        val logoRes: Int = info.logoRes
-        val logoBitmap: Bitmap? = info.logoBitmap
+        val logoBitmap: Bitmap? = info.logo
         val logoDescription: String? = info.logoDescription
         val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
         val componentNameForConfirmDeviceCredentialActivity: ComponentName? =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index a39a74f..d95a893 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -504,7 +504,6 @@
             .map {
                 when {
                     !(customBiometricPrompt() && constraintBp()) || it == null -> null
-                    it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme)
                     it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap)
                     else -> context.getUserBadgedIcon(it, iconProvider, activityTaskManager)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index f860893..3294c81 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -27,11 +27,13 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
 import com.android.dream.lowlight.util.TruncatedInterpolator
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
 import com.android.systemui.complication.ComplicationHostViewController
 import com.android.systemui.complication.ComplicationLayoutParams
 import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM
 import com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP
 import com.android.systemui.complication.ComplicationLayoutParams.Position
+import com.android.systemui.dreams.dagger.DreamOverlayComponent.DreamOverlayScope
 import com.android.systemui.dreams.dagger.DreamOverlayModule
 import com.android.systemui.dreams.ui.viewmodel.DreamViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -45,12 +47,13 @@
 import kotlinx.coroutines.launch
 
 /** Controller for dream overlay animations. */
+@DreamOverlayScope
 class DreamOverlayAnimationsController
 @Inject
 constructor(
     private val mBlurUtils: BlurUtils,
     private val mComplicationHostViewController: ComplicationHostViewController,
-    private val mStatusBarViewController: DreamOverlayStatusBarViewController,
+    private val mStatusBarViewController: AmbientStatusBarViewController,
     private val mOverlayStateController: DreamOverlayStateController,
     @Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int,
     private val dreamViewModel: DreamViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 1e725eb..245def8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -40,6 +40,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.dream.lowlight.LowLightTransitionCoordinator;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
 import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
@@ -55,6 +56,7 @@
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.ViewController;
 
 import kotlinx.coroutines.CoroutineDispatcher;
@@ -72,10 +74,12 @@
 public class DreamOverlayContainerViewController extends
         ViewController<DreamOverlayContainerView> implements
         LowLightTransitionCoordinator.LowLightEnterListener {
-    private final DreamOverlayStatusBarViewController mStatusBarViewController;
+    private final AmbientStatusBarViewController mStatusBarViewController;
+    private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
     private final BlurUtils mBlurUtils;
     private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
     private final DreamOverlayStateController mStateController;
+
     private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final ShadeInteractor mShadeInteractor;
@@ -188,8 +192,9 @@
             ComplicationHostViewController complicationHostViewController,
             @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
             @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView,
-            DreamOverlayStatusBarViewController statusBarViewController,
+            AmbientStatusBarViewController statusBarViewController,
             LowLightTransitionCoordinator lowLightTransitionCoordinator,
+            TouchInsetManager.TouchInsetSession touchInsetSession,
             BlurUtils blurUtils,
             @Main Handler handler,
             @Background CoroutineDispatcher backgroundDispatcher,
@@ -209,6 +214,7 @@
         super(containerView);
         mDreamOverlayContentView = contentView;
         mStatusBarViewController = statusBarViewController;
+        mTouchInsetSession = touchInsetSession;
         mBlurUtils = blurUtils;
         mDreamOverlayAnimationsController = animationsController;
         mStateController = stateController;
@@ -294,6 +300,7 @@
         mHandler.removeCallbacksAndMessages(null);
         mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
         mBouncerlessScrimController.removeCallback(mBouncerlessExpansionCallback);
+        mTouchInsetSession.clear();
 
         mDreamOverlayAnimationsController.cancelAnimations();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
index 409b196..0833518 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
@@ -31,12 +31,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.AlphaOptimizedImageView;
 
 /**
  * An {@link AlphaOptimizedImageView} that is responsible for rendering a dot. Used by
- * {@link DreamOverlayStatusBarView}.
+ * {@link AmbientStatusBarView}.
  */
 public class DreamOverlayDotImageView extends AlphaOptimizedImageView {
     private final @ColorInt int mDotColor;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 789b7f8..76fcabd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -25,9 +25,11 @@
 import androidx.lifecycle.LifecycleOwner;
 
 import com.android.internal.util.Preconditions;
+import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayContainerView;
-import com.android.systemui.dreams.DreamOverlayStatusBarView;
 import com.android.systemui.res.R;
 import com.android.systemui.touch.TouchInsetManager;
 
@@ -60,7 +62,7 @@
     public static DreamOverlayContainerView providesDreamOverlayContainerView(
             LayoutInflater layoutInflater) {
         return Preconditions.checkNotNull((DreamOverlayContainerView)
-                layoutInflater.inflate(R.layout.dream_overlay_container, null),
+                        layoutInflater.inflate(R.layout.dream_overlay_container, null),
                 "R.layout.dream_layout_container could not be properly inflated");
     }
 
@@ -95,13 +97,23 @@
     /** */
     @Provides
     @DreamOverlayComponent.DreamOverlayScope
-    public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
+    public static AmbientStatusBarView providesDreamOverlayStatusBarView(
             DreamOverlayContainerView view) {
         return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
                 "R.id.status_bar must not be null");
     }
 
-    /** */
+    /**
+     * Provides the view controller for the {@link AmbientStatusBarView}
+     */
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    public static AmbientStatusBarViewController providesStatusBarViewController(
+            AmbientStatusBarView view, AmbientStatusBarComponent.Factory factory) {
+        return factory.create(view).getController();
+    }
+
+    /**  */
     @Provides
     @DreamOverlayComponent.DreamOverlayScope
     @Named(MAX_BURN_IN_OFFSET)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 180afb2..1b342ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -142,17 +142,15 @@
         nonApps: Array<RemoteAnimationTarget>,
         finishedCallback: IRemoteAnimationFinishedCallback
     ) {
-        // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
-        // going away animation on its own, if an activity launches and then requests dismissing the
-        // keyguard. In this case, this is the first and only signal we'll receive to start
-        // a transition to GONE. This transition needs to start even if we're not provided an app
-        // animation target - it's possible the app is destroyed on creation, etc. but we'll still
-        // be unlocking.
-        keyguardTransitionInteractor.startDismissKeyguardTransition(
-            reason = "Going away remote animation started"
-        )
-
         if (apps.isNotEmpty()) {
+            // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
+            // going away animation on its own, if an activity launches and then requests dismissing
+            // the keyguard. In this case, this is the first and only signal we'll receive to start
+            // a transition to GONE.
+            keyguardTransitionInteractor.startDismissKeyguardTransition(
+                reason = "Going away remote animation started"
+            )
+
             goingAwayRemoteAnimationFinishedCallback = finishedCallback
             keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
         } else {
@@ -185,11 +183,25 @@
     /**
      * Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown.
      *
-     * If [lockscreenShowing] is null, it means we don't know if the lockscreen is showing yet. This
-     * will be decided by the [KeyguardTransitionBootInteractor] shortly.
+     * [lockscreenShowing] defaults to true, since it's only ever null during the boot sequence,
+     * when we haven't yet called ATMS#setLockScreenShown. Typically,
+     * setWmLockscreenState(lockscreenShowing = true) is called early in the boot sequence, before
+     * setWmLockscreenState(aodVisible = true), so we don't expect to need to use this default, but
+     * if so, true should be the right choice.
      */
     private fun setWmLockscreenState(
-        lockscreenShowing: Boolean? = this.isLockscreenShowing,
+        lockscreenShowing: Boolean =
+            this.isLockscreenShowing
+                ?: true.also {
+                    Log.d(
+                        TAG,
+                        "Using isLockscreenShowing=true default in setWmLockscreenState, " +
+                            "because setAodVisible was called before the first " +
+                            "setLockscreenShown call during boot. This is not typical, but is " +
+                            "theoretically possible. If you're investigating the lockscreen " +
+                            "showing unexpectedly, start here."
+                    )
+                },
         aodVisible: Boolean = this.isAodVisible
     ) {
         Log.d(
@@ -199,27 +211,10 @@
                 "aodVisible=$aodVisible)."
         )
 
-        if (lockscreenShowing == null) {
-            Log.d(
-                TAG,
-                "isAodVisible=$aodVisible, but lockscreenShowing=null. Waiting for" +
-                    "non-null lockscreenShowing before calling ATMS#setLockScreenShown, which" +
-                    "will happen once KeyguardTransitionBootInteractor starts the boot transition."
-            )
-            this.isAodVisible = aodVisible
-            return
-        }
-
         if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) {
             return
         }
 
-        Log.d(
-            TAG,
-            "ATMS#setLockScreenShown(" +
-                "isLockscreenShowing=$lockscreenShowing, " +
-                "aodVisible=$aodVisible)."
-        )
         activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
         this.isLockscreenShowing = lockscreenShowing
         this.isAodVisible = aodVisible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 3a43b1c..88e6602 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -229,14 +229,11 @@
     val aodVisibility: Flow<Boolean> =
         combine(
                 keyguardInteractor.isDozing,
-                keyguardInteractor.isAodAvailable,
                 keyguardInteractor.biometricUnlockState,
-            ) { isDozing, isAodAvailable, biometricUnlockState ->
+            ) { isDozing, biometricUnlockState ->
                 // AOD is visible if we're dozing, unless we are wake and unlocking (where we go
                 // directly from AOD to unlocked while dozing).
-                isDozing &&
-                    isAodAvailable &&
-                    !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
+                isDozing && !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
             }
             .distinctUntilChanged()
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 601d563..88a28bf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -36,6 +36,7 @@
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.traceSection
 import com.android.keyguard.KeyguardViewController
+import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix
 import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -483,8 +484,7 @@
             object : StatusBarStateController.StateListener {
                 override fun onStatePreChange(oldState: Int, newState: Int) {
                     // We're updating the location before the state change happens, since we want
-                    // the
-                    // location of the previous state to still be up to date when the animation
+                    // the location of the previous state to still be up to date when the animation
                     // starts
                     if (
                         newState == StatusBarState.SHADE_LOCKED &&
@@ -588,6 +588,17 @@
             }
         }
 
+        if (mediaControlsLockscreenShadeBugFix()) {
+            coroutineScope.launch {
+                shadeInteractor.shadeExpansion.collect { expansion ->
+                    if (expansion >= 1f || expansion <= 0f) {
+                        // Shade has fully expanded or collapsed: force transition amount update
+                        setTransitionToFullShadeAmount(expansion)
+                    }
+                }
+            }
+        }
+
         val settingsObserver: ContentObserver =
             object : ContentObserver(handler) {
                 override fun onChange(selfChange: Boolean, uri: Uri?) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
index 1f6a8b8..08f139c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
@@ -7,6 +7,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.Utils.toBitmap
 import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
 import com.android.systemui.biometrics.promptInfo
 import com.android.systemui.biometrics.shared.model.BiometricModalities
@@ -28,6 +29,7 @@
     @Test
     fun biometricRequestFromPromptInfo() {
         val logoRes = R.drawable.ic_cake
+        val logoBitmapFromRes = context.getDrawable(logoRes).toBitmap()
         val logoDescription = "test cake"
         val title = "what"
         val subtitle = "a"
@@ -44,6 +46,7 @@
             BiometricPromptRequest.Biometric(
                 promptInfo(
                     logoRes = logoRes,
+                    logoBitmap = logoBitmapFromRes,
                     logoDescription = logoDescription,
                     title = title,
                     subtitle = subtitle,
@@ -56,7 +59,8 @@
                 OP_PACKAGE_NAME,
             )
 
-        assertThat(request.logoRes).isEqualTo(logoRes)
+        assertThat(request.logoBitmap).isNotNull()
+        assertThat(request.logoBitmap!!.sameAs(logoBitmapFromRes)).isTrue()
         assertThat(request.logoDescription).isEqualTo(logoDescription)
         assertThat(request.title).isEqualTo(title)
         assertThat(request.subtitle).isEqualTo(subtitle)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index db6aba3..7076954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -48,6 +48,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.UdfpsUtils
+import com.android.systemui.biometrics.Utils.toBitmap
 import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
@@ -129,7 +130,7 @@
     private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
     private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add)
     private val logoResFromApp = R.drawable.ic_cake
-    private val logoFromApp = context.getDrawable(logoResFromApp)
+    private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp)
     private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)
     private val defaultLogoDescription = "Test Android App"
     private val logoDescriptionFromApp = "Test Cake App"
@@ -223,7 +224,7 @@
 
         context.setMockPackageManager(packageManager)
         val resources = context.getOrCreateTestableResources()
-        resources.addOverride(logoResFromApp, logoFromApp)
+        resources.addOverride(logoResFromApp, logoDrawableFromAppRes)
         resources.addOverride(
             R.array.biometric_dialog_package_names_for_logo_with_overrides,
             arrayOf(packageNameForLogoWithOverrides)
@@ -1251,7 +1252,7 @@
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun descriptionOverriddenByVerticalListContentView() =
-        runGenericTest(contentView = promptContentView, description = "test description") {
+        runGenericTest(description = "test description", contentView = promptContentView) {
             val contentView by collectLastValue(viewModel.contentView)
             val description by collectLastValue(viewModel.description)
 
@@ -1263,8 +1264,8 @@
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
         runGenericTest(
-            contentView = promptContentViewWithMoreOptionsButton,
-            description = "test description"
+            description = "test description",
+            contentView = promptContentViewWithMoreOptionsButton
         ) {
             val contentView by collectLastValue(viewModel.contentView)
             val description by collectLastValue(viewModel.description)
@@ -1324,8 +1325,9 @@
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logo_resSetByApp() =
         runGenericTest(logoRes = logoResFromApp) {
+            val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
             val logo by collectLastValue(viewModel.logo)
-            assertThat(logo).isEqualTo(logoFromApp)
+            assertThat((logo as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue()
         }
 
     @Test
@@ -1438,7 +1440,8 @@
             descriptionFromApp = description,
             contentViewFromApp = contentView,
             logoResFromApp = logoRes,
-            logoBitmapFromApp = logoBitmap,
+            logoBitmapFromApp =
+                if (logoRes != -1) logoDrawableFromAppRes.toBitmap() else logoBitmap,
             logoDescriptionFromApp = logoDescription,
             packageName = packageName,
         )
@@ -1631,8 +1634,6 @@
 ) {
     val info =
         PromptInfo().apply {
-            logoRes = logoResFromApp
-            logoBitmap = logoBitmapFromApp
             logoDescription = logoDescriptionFromApp
             title = "t"
             subtitle = "s"
@@ -1642,6 +1643,9 @@
             isDeviceCredentialAllowed = allowCredentialFallback
             isConfirmationRequested = requireConfirmation
         }
+    if (logoBitmapFromApp != null) {
+        info.setLogo(logoResFromApp, logoBitmapFromApp)
+    }
 
     setPrompt(
         info,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 4e1b12f..1f13298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -100,11 +100,9 @@
     }
 
     @Test
-    fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater() {
+    fun testAodVisible_noLockscreenShownCallYet_defaultsToShowLockscreen() {
         underTest.setAodVisible(false)
-        verifyNoMoreInteractions(activityTaskManagerService)
 
-        underTest.setLockscreenShown(true)
         verify(activityTaskManagerService).setLockScreenShown(true, false)
         verifyNoMoreInteractions(activityTaskManagerService)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 6d2d04a..45a14ad 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -62,6 +62,8 @@
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.statusbar.phone.scrimController
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.fakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiInteractor
 import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.util.time.systemClock
@@ -130,6 +132,8 @@
     val shadeController by lazy { kosmos.shadeController }
     val shadeRepository by lazy { kosmos.shadeRepository }
     val shadeInteractor by lazy { kosmos.shadeInteractor }
+    val wifiInteractor by lazy { kosmos.wifiInteractor }
+    val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
 
     val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
     val scrimController by lazy { kosmos.scrimController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
new file mode 100644
index 0000000..8e656cf
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeConnectivityRepository: FakeConnectivityRepository by
+    Kosmos.Fixture { FakeConnectivityRepository() }
+val Kosmos.connectivityRepository: ConnectivityRepository by
+    Kosmos.Fixture { fakeConnectivityRepository }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 97c8d5f..709be5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt
new file mode 100644
index 0000000..e44061a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeWifiRepository: FakeWifiRepository by Kosmos.Fixture { FakeWifiRepository() }
+val Kosmos.wifiRepository: WifiRepository by Kosmos.Fixture { fakeWifiRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt
new file mode 100644
index 0000000..7036199
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.wifiRepository
+
+val Kosmos.wifiInteractor: WifiInteractor by
+    Kosmos.Fixture {
+        WifiInteractorImpl(
+            connectivityRepository,
+            wifiRepository,
+            applicationCoroutineScope,
+        )
+    }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 02a7b0f..4f9db8b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -47,11 +47,6 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
 import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityShortcutActivated;
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -928,11 +923,25 @@
                                         newValue, restoredFromSdk);
                             }
                         }
-                        case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-                                Settings.Secure.ACCESSIBILITY_QS_TARGETS,
-                                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE ->
-                                restoreAccessibilityShortcutTargets(previousValue, newValue,
-                                        ShortcutUtils.convertToType(which));
+                        case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> {
+                            synchronized (mLock) {
+                                restoreAccessibilityButtonTargetsLocked(
+                                        previousValue, newValue);
+                            }
+                        }
+                        case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> {
+                            if (!android.view.accessibility.Flags.a11yQsShortcut()) {
+                                return;
+                            }
+                            restoreAccessibilityQsTargets(newValue);
+                        }
+                        case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> {
+                            if (!android.view.accessibility.Flags
+                                    .restoreA11yShortcutTargetService()) {
+                                return;
+                            }
+                            restoreAccessibilityShortcutTargetService(previousValue, newValue);
+                        }
                     }
                 }
             }
@@ -1031,7 +1040,7 @@
         }
         persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 userState.mUserId, targetsFromSetting, str -> str);
-        readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
+        readAccessibilityButtonTargetsLocked(userState);
         onUserStateChangedLocked(userState);
     }
 
@@ -1711,12 +1720,12 @@
         }
         // Turn on/off a11y qs shortcut for the a11y features based on the change in QS Panel
         if (!a11yFeaturesToEnable.isEmpty()) {
-            enableShortcutForTargets(/* enable= */ true, QUICK_SETTINGS,
+            enableShortcutForTargets(/* enable= */ true, UserShortcutType.QUICK_SETTINGS,
                     a11yFeaturesToEnable, userId);
         }
 
         if (!a11yFeaturesToRemove.isEmpty()) {
-            enableShortcutForTargets(/* enable= */ false, QUICK_SETTINGS,
+            enableShortcutForTargets(/* enable= */ false, UserShortcutType.QUICK_SETTINGS,
                     a11yFeaturesToRemove, userId);
         }
     }
@@ -2048,70 +2057,99 @@
     }
 
     /**
-     * User could configure accessibility shortcut during the SUW before restoring user data.
-     * Merges the current value and the new value to make sure we don't lost the setting the user's
-     * preferences of accessibility shortcut updated in SUW are not lost.
-     * Called only during settings restore; currently supports only the owner user.
-     * <P>
-     * Throws an exception if used with {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}.
-     * </P>
+     * User could enable accessibility services and configure accessibility button during the SUW.
+     * Merges current value of accessibility button settings into the restored one to make sure
+     * user's preferences of accessibility button updated in SUW are not lost.
+     *
+     * Called only during settings restore; currently supports only the owner user
      * TODO: http://b/22388012
      */
-    private void restoreAccessibilityShortcutTargets(String oldValue, String newValue,
-            @UserShortcutType int shortcutType) {
-        assertNoTapShortcut(shortcutType);
-        if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) {
-            return;
-        }
-        if (shortcutType == HARDWARE && !android.view.accessibility.Flags
-                .restoreA11yShortcutTargetService()) {
-            return;
-        }
+    void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) {
+        final Set<String> targetsFromSetting = new ArraySet<>();
+        readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting,
+                /* doMerge = */false);
+        readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting,
+                /* doMerge = */true);
 
+        final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+        userState.mAccessibilityButtonTargets.clear();
+        userState.mAccessibilityButtonTargets.addAll(targetsFromSetting);
+        persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+                UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str);
+
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+        onUserStateChangedLocked(userState);
+    }
+
+    /**
+     * User could configure accessibility shortcut during the SUW before restoring user data.
+     * Merges the current value and the new value to make sure we don't lost the setting the user's
+     * preferences of accessibility qs shortcut updated in SUW are not lost.
+     *
+     * Called only during settings restore; currently supports only the owner user
+     * TODO: http://b/22388012
+     */
+    private void restoreAccessibilityQsTargets(String newValue) {
         synchronized (mLock) {
             final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
-            final Set<String> mergedTargets = userState.getShortcutTargetsLocked(shortcutType);
-            readColonDelimitedStringToSet(oldValue, str -> str, mergedTargets,
+            final Set<String> mergedTargets = userState.getA11yQsTargets();
+            readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
                     /* doMerge = */ true);
 
-            // If dealing with the hardware shortcut,
-            // remove the default service if it wasn't present before restore.
-            // Otherwise, merge the old and new targets normally.
-            if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()
-                    && shortcutType == HARDWARE) {
-                final String defaultService =
-                        mContext.getString(R.string.config_defaultAccessibilityService);
-                final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
-                        ? null : ComponentName.unflattenFromString(defaultService);
-                boolean shouldClearDefaultService = defaultServiceComponent != null
-                        && !stringSetContainsComponentName(mergedTargets, defaultServiceComponent);
-                readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
-                        /* doMerge = */ true);
-
-                if (shouldClearDefaultService && stringSetContainsComponentName(
-                        mergedTargets, defaultServiceComponent)) {
-                    Slog.i(LOG_TAG, "Removing default service " + defaultService
-                            + " from restore of "
-                            + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
-                    mergedTargets.removeIf(str ->
-                            defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
-                }
-                if (mergedTargets.isEmpty()) {
-                    return;
-                }
-            } else {
-                readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
-                        /* doMerge = */ true);
-            }
-
-            userState.updateShortcutTargetsLocked(mergedTargets, shortcutType);
-            persistColonDelimitedSetToSettingLocked(ShortcutUtils.convertToKey(shortcutType),
+            userState.updateA11yQsTargetLocked(mergedTargets);
+            persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
                     UserHandle.USER_SYSTEM, mergedTargets, str -> str);
             scheduleNotifyClientsOfServicesStateChangeLocked(userState);
             onUserStateChangedLocked(userState);
         }
     }
 
+    /**
+     * Merges the old and restored value of
+     * {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
+     *
+     * <p>Also clears out {@link R.string#config_defaultAccessibilityService} from
+     * the merged set if it was not present before restoring.
+     */
+    private void restoreAccessibilityShortcutTargetService(
+            String oldValue, String restoredValue) {
+        final Set<String> targetsFromSetting = new ArraySet<>();
+        readColonDelimitedStringToSet(oldValue, str -> str,
+                targetsFromSetting, /*doMerge=*/false);
+        final String defaultService =
+                mContext.getString(R.string.config_defaultAccessibilityService);
+        final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
+                ? null : ComponentName.unflattenFromString(defaultService);
+        boolean shouldClearDefaultService = defaultServiceComponent != null
+                && !stringSetContainsComponentName(targetsFromSetting, defaultServiceComponent);
+        readColonDelimitedStringToSet(restoredValue, str -> str,
+                targetsFromSetting, /*doMerge=*/true);
+        if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()) {
+            if (shouldClearDefaultService && stringSetContainsComponentName(
+                    targetsFromSetting, defaultServiceComponent)) {
+                Slog.i(LOG_TAG, "Removing default service " + defaultService
+                        + " from restore of "
+                        + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+                targetsFromSetting.removeIf(str ->
+                        defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
+            }
+            if (targetsFromSetting.isEmpty()) {
+                return;
+            }
+        }
+        synchronized (mLock) {
+            final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+            final Set<String> shortcutTargets =
+                    userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
+            shortcutTargets.clear();
+            shortcutTargets.addAll(targetsFromSetting);
+            persistColonDelimitedSetToSettingLocked(
+                    Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                    UserHandle.USER_SYSTEM, targetsFromSetting, str -> str);
+            scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+            onUserStateChangedLocked(userState);
+        }
+    }
 
     /**
      * Returns {@code true} if the set contains the provided non-null {@link ComponentName}.
@@ -2225,7 +2263,7 @@
     private void showAccessibilityTargetsSelection(int displayId,
             @UserShortcutType int shortcutType) {
         final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
-        final String chooserClassName = (shortcutType == HARDWARE)
+        final String chooserClassName = (shortcutType == UserShortcutType.HARDWARE)
                 ? AccessibilityShortcutChooserActivity.class.getName()
                 : AccessibilityButtonChooserActivity.class.getName();
         intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
@@ -3198,9 +3236,9 @@
         somethingChanged |= readAudioDescriptionEnabledSettingLocked(userState);
         somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
         somethingChanged |= readAutoclickEnabledSettingLocked(userState);
-        somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, HARDWARE);
-        somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
-        somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
+        somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState);
+        somethingChanged |= readAccessibilityQsTargetsLocked(userState);
+        somethingChanged |= readAccessibilityButtonTargetsLocked(userState);
         somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState);
         somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
         somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
@@ -3348,31 +3386,60 @@
         userState.setSendMotionEventsEnabled(sendMotionEvents);
     }
 
-    /**
-     * Throws an exception for {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} types.
-     */
-    private boolean readAccessibilityShortcutTargetsLocked(AccessibilityUserState userState,
-            @UserShortcutType int shortcutType) {
-        assertNoTapShortcut(shortcutType);
+    private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
         final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                ShortcutUtils.convertToKey(shortcutType), userState.mUserId);
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
         final Set<String> targetsFromSetting = new ArraySet<>();
-        // If dealing with an empty hardware shortcut, fall back to the default value.
-        if (shortcutType == HARDWARE && settingValue == null) {
+        readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
+        // Fall back to device's default a11y service, only when setting is never updated.
+        if (settingValue == null) {
             final String defaultService = mContext.getString(
                     R.string.config_defaultAccessibilityService);
             if (!TextUtils.isEmpty(defaultService)) {
                 targetsFromSetting.add(defaultService);
             }
-        } else {
-            readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
         }
 
-        if (userState.updateShortcutTargetsLocked(targetsFromSetting, shortcutType)) {
-            scheduleNotifyClientsOfServicesStateChangeLocked(userState);
-            return true;
+        final Set<String> currentTargets =
+                userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
+        if (targetsFromSetting.equals(currentTargets)) {
+            return false;
         }
-        return false;
+        currentTargets.clear();
+        currentTargets.addAll(targetsFromSetting);
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+        return true;
+    }
+
+    private boolean readAccessibilityQsTargetsLocked(AccessibilityUserState userState) {
+        final Set<String> targetsFromSetting = new ArraySet<>();
+        readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+                userState.mUserId, str -> str, targetsFromSetting);
+
+        final Set<String> currentTargets =
+                userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
+        if (targetsFromSetting.equals(currentTargets)) {
+            return false;
+        }
+        userState.updateA11yQsTargetLocked(targetsFromSetting);
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+        return true;
+    }
+
+    private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
+        final Set<String> targetsFromSetting = new ArraySet<>();
+        readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+                userState.mUserId, str -> str, targetsFromSetting);
+
+        final Set<String> currentTargets =
+                userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
+        if (targetsFromSetting.equals(currentTargets)) {
+            return false;
+        }
+        currentTargets.clear();
+        currentTargets.addAll(targetsFromSetting);
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+        return true;
     }
 
     private boolean readAccessibilityButtonTargetComponentLocked(AccessibilityUserState userState) {
@@ -3420,7 +3487,7 @@
      */
     private void updateAccessibilityShortcutKeyTargetsLocked(AccessibilityUserState userState) {
         final Set<String> currentTargets =
-                userState.getShortcutTargetsLocked(HARDWARE);
+                userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
         final int lastSize = currentTargets.size();
         if (lastSize == 0) {
             return;
@@ -3689,9 +3756,9 @@
         lastSize = buttonTargets.size();
 
         final Set<String> shortcutKeyTargets =
-                userState.getShortcutTargetsLocked(HARDWARE);
+                userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
         final Set<String> qsShortcutTargets =
-                userState.getShortcutTargetsLocked(QUICK_SETTINGS);
+                userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
         userState.mEnabledServices.forEach(componentName -> {
             if (packageName != null && componentName != null
                     && !packageName.equals(componentName.getPackageName())) {
@@ -3748,7 +3815,7 @@
         }
 
         final Set<String> targets =
-                userState.getShortcutTargetsLocked(QUICK_SETTINGS);
+                userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
 
         // Removes the targets that are no longer installed on the device.
         boolean somethingChanged = targets.removeIf(
@@ -3769,7 +3836,7 @@
         if (!somethingChanged) {
             return;
         }
-        userState.updateShortcutTargetsLocked(targets, QUICK_SETTINGS);
+        userState.updateA11yQsTargetLocked(targets);
 
         // Update setting key with new value.
         persistColonDelimitedSetToSettingLocked(
@@ -3795,14 +3862,14 @@
 
         final List<Pair<Integer, String>> shortcutTypeAndShortcutSetting = new ArrayList<>(3);
         shortcutTypeAndShortcutSetting.add(
-                new Pair<>(HARDWARE,
+                new Pair<>(UserShortcutType.HARDWARE,
                         Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
         shortcutTypeAndShortcutSetting.add(
                 new Pair<>(UserShortcutType.SOFTWARE,
                         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS));
         if (android.view.accessibility.Flags.a11yQsShortcut()) {
             shortcutTypeAndShortcutSetting.add(
-                    new Pair<>(QUICK_SETTINGS,
+                    new Pair<>(UserShortcutType.QUICK_SETTINGS,
                             Settings.Secure.ACCESSIBILITY_QS_TARGETS));
         }
 
@@ -3816,7 +3883,7 @@
                         shortcutSettingName,
                         userState.mUserId, currentTargets, str -> str);
 
-                if (shortcutType != QUICK_SETTINGS) {
+                if (shortcutType != UserShortcutType.QUICK_SETTINGS) {
                     continue;
                 }
 
@@ -3901,7 +3968,7 @@
 
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::performAccessibilityShortcutInternal, this,
-                Display.DEFAULT_DISPLAY, HARDWARE, targetName));
+                Display.DEFAULT_DISPLAY, UserShortcutType.HARDWARE, targetName));
     }
 
     /**
@@ -4048,7 +4115,7 @@
             final boolean requestA11yButton = (installedServiceInfo.flags
                     & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
             // Turns on / off the accessibility service
-            if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == HARDWARE)
+            if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == UserShortcutType.HARDWARE)
                     || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
                 if (serviceConnection == null) {
                     logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
@@ -4062,7 +4129,7 @@
                 }
                 return true;
             }
-            if (shortcutType == HARDWARE && targetSdk > Build.VERSION_CODES.Q
+            if (shortcutType == UserShortcutType.HARDWARE && targetSdk > Build.VERSION_CODES.Q
                     && requestA11yButton) {
                 if (!userState.getEnabledServicesLocked().contains(assignedTarget)) {
                     enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
@@ -4155,7 +4222,7 @@
             validNewTargets = newTargets;
 
             // filter out targets that doesn't have qs shortcut
-            if (shortcutType == QUICK_SETTINGS) {
+            if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
                 validNewTargets = newTargets.stream().filter(target -> {
                     ComponentName targetComponent = ComponentName.unflattenFromString(target);
                     return featureToTileMap.containsKey(targetComponent);
@@ -4173,10 +4240,10 @@
                     /* defaultEmptyString= */ ""
             );
 
-            if (shortcutType == QUICK_SETTINGS) {
+            if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
                 int numOfFeatureChanged = Math.abs(currentTargets.size() - validNewTargets.size());
                 logMetricForQsShortcutConfiguration(enable, numOfFeatureChanged);
-                userState.updateShortcutTargetsLocked(validNewTargets, QUICK_SETTINGS);
+                userState.updateA11yQsTargetLocked(validNewTargets);
                 scheduleNotifyClientsOfServicesStateChangeLocked(userState);
                 onUserStateChangedLocked(userState);
             }
@@ -4190,7 +4257,7 @@
         }
 
         // Add or Remove tile in QS Panel
-        if (shortcutType == QUICK_SETTINGS) {
+        if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
             mMainHandler.sendMessage(obtainMessage(
                     AccessibilityManagerService::updateA11yTileServicesInQuickSettingsPanel,
                     this, validNewTargets, currentTargets, userId));
@@ -4199,7 +4266,7 @@
         if (!enable) {
             return;
         }
-        if (shortcutType == HARDWARE) {
+        if (shortcutType == UserShortcutType.HARDWARE) {
             skipVolumeShortcutDialogTimeoutRestriction(userId);
             if (com.android.server.accessibility.Flags.enableHardwareShortcutDisablesWarning()) {
                 persistIntToSetting(
@@ -5605,7 +5672,7 @@
                         || mShowImeWithHardKeyboardUri.equals(uri)) {
                     userState.reconcileSoftKeyboardModeWithSettingsLocked();
                 } else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
-                    if (readAccessibilityShortcutTargetsLocked(userState, HARDWARE)) {
+                    if (readAccessibilityShortcutKeySettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
                 } else if (mAccessibilityButtonComponentIdUri.equals(uri)) {
@@ -5613,7 +5680,7 @@
                         onUserStateChangedLocked(userState);
                     }
                 } else if (mAccessibilityButtonTargetsUri.equals(uri)) {
-                    if (readAccessibilityShortcutTargetsLocked(userState, SOFTWARE)) {
+                    if (readAccessibilityButtonTargetsLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
                 } else if (mUserNonInteractiveUiTimeoutUri.equals(uri)
@@ -6438,10 +6505,4 @@
         String metricId = enable ? METRIC_ID_QS_SHORTCUT_ADD : METRIC_ID_QS_SHORTCUT_REMOVE;
         Counter.logIncrementWithUid(metricId, Binder.getCallingUid(), numOfFeatures);
     }
-
-    private void assertNoTapShortcut(@UserShortcutType int shortcutType) {
-        if ((shortcutType & (TRIPLETAP | TWOFINGER_DOUBLETAP)) != 0) {
-            throw new IllegalArgumentException("Tap shortcuts are not supported.");
-        }
-    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 365d1d4..a37a184 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -777,20 +777,12 @@
      * @return The array set of the strings
      */
     public ArraySet<String> getShortcutTargetsLocked(@UserShortcutType int shortcutType) {
-        ArraySet<String> set = getShortcutTargetsInternalLocked(shortcutType);
-        // if it's Quick Settings, make a defensive copy. Otherwise, return the raw set.
-        if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
-            set = new ArraySet<>(set);
-        }
-        return set;
-    }
-    private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) {
         if (shortcutType == UserShortcutType.HARDWARE) {
             return mAccessibilityShortcutKeyTargets;
         } else if (shortcutType == UserShortcutType.SOFTWARE) {
             return mAccessibilityButtonTargets;
         } else if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
-            return mAccessibilityQsTargets;
+            return getA11yQsTargets();
         } else if ((shortcutType == UserShortcutType.TRIPLETAP
                 && isMagnificationSingleFingerTripleTapEnabledLocked()) || (
                 shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP
@@ -803,32 +795,6 @@
     }
 
     /**
-     * Updates the corresponding shortcut targets with the provided set.
-     * Tap shortcuts don't operate using sets of targets,
-     * so trying to update {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}
-     * will instead throw an {@code IllegalArgumentException}
-     * @param newTargets set of targets to replace the existing set.
-     * @param shortcutType type to be replaced.
-     * @return {@code true} if the set was changed, or {@code false} if the elements are the same.
-     * @throws IllegalArgumentException if {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} is used.
-     */
-    boolean updateShortcutTargetsLocked(
-            Set<String> newTargets, @UserShortcutType int shortcutType) {
-        final int mask = UserShortcutType.TRIPLETAP | UserShortcutType.TWOFINGER_DOUBLETAP;
-        if ((shortcutType & mask) != 0) {
-            throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets.");
-        }
-
-        final Set<String> currentTargets = getShortcutTargetsInternalLocked(shortcutType);
-        if (newTargets.equals(currentTargets)) {
-            return false;
-        }
-        currentTargets.clear();
-        currentTargets.addAll(newTargets);
-        return true;
-    }
-
-    /**
      * Whether or not the given shortcut target is installed in device.
      *
      * @param name The shortcut target name
@@ -878,8 +844,8 @@
             );
         }
 
-        Set<String> targets = getShortcutTargetsInternalLocked(shortcutType);
-        return targets.removeIf(name -> {
+        Set<String> targets = getShortcutTargetsLocked(shortcutType);
+        boolean result = targets.removeIf(name -> {
             ComponentName componentName;
             if (name == null
                     || (componentName = ComponentName.unflattenFromString(name)) == null) {
@@ -887,6 +853,11 @@
             }
             return componentName.equals(target);
         });
+        if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+            updateA11yQsTargetLocked(targets);
+        }
+
+        return result;
     }
 
     /**
@@ -1143,6 +1114,11 @@
         );
     }
 
+    public void updateA11yQsTargetLocked(Set<String> targets) {
+        mAccessibilityQsTargets.clear();
+        mAccessibilityQsTargets.addAll(targets);
+    }
+
     /**
      * Returns a copy of the targets which has qs shortcut turned on
      */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d79d198..73253b2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10207,19 +10207,6 @@
         addStartInfoTimestampInternal(key, timestampNs, userId, callingUid);
     }
 
-    @Override
-    public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs,
-            long framePresentedTimeNs) {
-        int callingUid = Binder.getCallingUid();
-        int userId = UserHandle.getUserId(callingUid);
-        addStartInfoTimestampInternal(
-                ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME,
-                renderThreadDrawStartTimeNs, userId, callingUid);
-        addStartInfoTimestampInternal(
-                ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE,
-                framePresentedTimeNs, userId, callingUid);
-    }
-
     private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) {
         mProcessList.getAppStartInfoTracker().addTimestampToStart(
                 Settings.getPackageNameForUid(mContext, uid),
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 4a7ad31..3042b2a 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -1195,8 +1195,21 @@
 
             // Records are sorted newest to oldest, grab record at index 0.
             ApplicationStartInfo startInfo = mInfos.get(0);
+            int startupState = startInfo.getStartupState();
 
-            if (!isAddTimestampAllowed(startInfo, key, timestampNs)) {
+            // If startup state is error then don't accept any further timestamps.
+            if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) {
+                if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps.");
+                return;
+            }
+
+            // If startup state is first frame drawn then only accept fully drawn timestamp.
+            if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN
+                    && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully "
+                            + "drawn, not accepting new timestamps.");
+                }
                 return;
             }
 
@@ -1209,55 +1222,6 @@
             }
         }
 
-        private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key,
-                long timestampNs) {
-            int startupState = startInfo.getStartupState();
-
-            // If startup state is error then don't accept any further timestamps.
-            if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) {
-                if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps.");
-                return false;
-            }
-
-            Map<Integer, Long> timestamps = startInfo.getStartupTimestamps();
-
-            if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) {
-                switch (key) {
-                    case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN:
-                        // Allowed, continue to confirm it's not already added.
-                        break;
-                    case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME:
-                        Long firstFrameTimeNs = timestamps
-                                .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME);
-                        if (firstFrameTimeNs == null) {
-                            // This should never happen. State can't be first frame drawn if first
-                            // frame timestamp was not provided.
-                            return false;
-                        }
-
-                        if (timestampNs > firstFrameTimeNs) {
-                            // Initial renderthread frame has to occur before first frame.
-                            return false;
-                        }
-
-                        // Allowed, continue to confirm it's not already added.
-                        break;
-                    case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE:
-                        // Allowed, continue to confirm it's not already added.
-                        break;
-                    default:
-                        return false;
-                }
-            }
-
-            if (timestamps.get(key) != null) {
-                // Timestamp should not occur more than once for a given start.
-                return false;
-            }
-
-            return true;
-        }
-
         @GuardedBy("mLock")
         void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
             if (mMonitoringModeEnabled) {
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index 0afda45..11f2059 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -62,6 +62,12 @@
 
     PackageFreezer(String packageName, int userId, String killReason,
             PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request) {
+        this(packageName, userId, killReason, pm, exitInfoReason, request, false);
+    }
+
+    PackageFreezer(String packageName, int userId, String killReason,
+            PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request,
+            boolean waitAppKilled) {
         mPm = pm;
         mPackageName = packageName;
         mInstallRequest = request;
@@ -77,7 +83,7 @@
             ps = mPm.mSettings.getPackageLPr(mPackageName);
         }
         if (ps != null) {
-            if (Flags.waitApplicationKilled()) {
+            if (waitAppKilled && Flags.waitApplicationKilled()) {
                 mPm.killApplicationSync(ps.getPackageName(), ps.getAppId(), userId, killReason,
                         exitInfoReason);
             } else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ca84d68..66a93d7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4347,7 +4347,14 @@
 
     public PackageFreezer freezePackage(String packageName, int userId, String killReason,
             int exitInfoReason, InstallRequest request) {
-        return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request);
+        return freezePackage(packageName, userId, killReason, exitInfoReason, request,
+                /* waitAppKilled= */ false);
+    }
+
+    private PackageFreezer freezePackage(String packageName, int userId, String killReason,
+            int exitInfoReason, InstallRequest request, boolean waitAppKilled) {
+        return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request,
+                waitAppKilled);
     }
 
     public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags,
@@ -4772,7 +4779,8 @@
                     final boolean succeeded;
                     try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL,
                             "clearApplicationUserData",
-                            ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */)) {
+                            ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */,
+                            /* waitAppKilled= */ true)) {
                         try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
                             succeeded = clearApplicationUserDataLIF(snapshotComputer(), packageName,
                                     userId);
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
index 23872d4f..119b659 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
@@ -336,8 +336,13 @@
             final PermissionManagerServiceInternal permissionManagerInternal =
                     LocalServices.getService(PermissionManagerServiceInternal.class);
             for (final int userId : UserManagerService.getInstance().getUserIds()) {
-                packageManagerInternal.forEachPackage(pkg ->
-                        permissionManagerInternal.resetRuntimePermissions(pkg, userId));
+                packageManagerInternal.forEachPackage(pkg -> {
+                    // Filter out packages that don't have app IDs which means they don't have
+                    // permission states either.
+                    if (pkg.getUid() != -1) {
+                        permissionManagerInternal.resetRuntimePermissions(pkg, userId);
+                    }
+                });
             }
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 47258d6..4e8c755 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -25,7 +25,6 @@
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG;
 import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER;
 
@@ -1391,14 +1390,14 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                QUICK_SETTINGS,
+                UserShortcutType.QUICK_SETTINGS,
                 List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
         assertThat(
                 ShortcutUtils.isComponentIdExistingInSettings(
-                        mTestableContext, QUICK_SETTINGS,
+                        mTestableContext, UserShortcutType.QUICK_SETTINGS,
                         TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
         ).isTrue();
         verify(mStatusBarManagerInternal)
@@ -1418,14 +1417,14 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ false,
-                QUICK_SETTINGS,
+                UserShortcutType.QUICK_SETTINGS,
                 List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
         assertThat(
                 ShortcutUtils.isComponentIdExistingInSettings(
-                        mTestableContext, QUICK_SETTINGS,
+                        mTestableContext, UserShortcutType.QUICK_SETTINGS,
                         TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
         ).isFalse();
         verify(mStatusBarManagerInternal)
@@ -1622,7 +1621,7 @@
                 AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
         final AccessibilityUserState userState = new AccessibilityUserState(
                 UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
-        userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
+        userState.updateA11yQsTargetLocked(Set.of(daltonizerTile));
         mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
 
         broadcastSettingRestored(
@@ -1643,7 +1642,7 @@
                 AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
         final AccessibilityUserState userState = new AccessibilityUserState(
                 UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
-        userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
+        userState.updateA11yQsTargetLocked(Set.of(daltonizerTile));
         mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
 
         broadcastSettingRestored(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 9fad14d..b269beb9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -28,7 +28,6 @@
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
 
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -430,20 +429,20 @@
     }
 
     @Test
-    public void updateShortcutTargetsLocked_quickSettings_valueUpdated() {
+    public void updateA11yQsTargetLocked_valueUpdated() {
         Set<String> newTargets = Set.of(
                 AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(),
                 AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString()
         );
 
-        mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS);
+        mUserState.updateA11yQsTargetLocked(newTargets);
 
         assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets);
     }
 
     @Test
     public void getA11yQsTargets_returnsCopiedData() {
-        updateShortcutTargetsLocked_quickSettings_valueUpdated();
+        updateA11yQsTargetLocked_valueUpdated();
 
         Set<String> targets = mUserState.getA11yQsTargets();
         targets.clear();
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index a23f211..379b45c 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -24,7 +24,6 @@
 import android.tools.flicker.legacy.LegacyFlickerTest
 import android.tools.flicker.legacy.LegacyFlickerTestFactory
 import android.tools.traces.parsers.toFlickerComponent
-import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -175,12 +174,6 @@
         }
     }
 
-    @FlakyTest(bugId = 342596801)
-    @Test
-    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
-        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-    }
-
     @Ignore("Not applicable to this CUJ.")
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
 
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
index 56dbde0..fff1dd1 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
@@ -39,11 +39,13 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(BedsteadJUnit4.class)
+@Ignore("b/345557347")
 public final class ConcurrentMultiUserTest {
 
     @ClassRule