Merge "Keep off displays in trace" into udc-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5c1b3ee..2eb6ca7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2840,6 +2840,10 @@
      * @hide
      */
     public void visitUris(@NonNull Consumer<Uri> visitor) {
+        if (publicVersion != null) {
+            publicVersion.visitUris(visitor);
+        }
+
         visitor.accept(sound);
 
         if (tickerView != null) tickerView.visitUris(visitor);
diff --git a/core/java/android/app/ServiceStartArgs.java b/core/java/android/app/ServiceStartArgs.java
index 0b000af5..9c52367 100644
--- a/core/java/android/app/ServiceStartArgs.java
+++ b/core/java/android/app/ServiceStartArgs.java
@@ -49,7 +49,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(taskRemoved ? 1 : 0);
         out.writeInt(startId);
-        out.writeInt(flags);
+        out.writeInt(this.flags);
         if (args != null) {
             out.writeInt(1);
             args.writeToParcel(out, 0);
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 99d4064..b29e73a 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -292,12 +292,12 @@
             packageName = mService.serviceInfo.packageName;
             applicationInfo = mService.serviceInfo.applicationInfo;
         }
-        String contextUriString = pm.getText(
-                packageName, mContextUriResource, applicationInfo).toString();
-        if (contextUriString == null) {
+        CharSequence contextUriCharSequence = pm.getText(
+                packageName, mContextUriResource, applicationInfo);
+        if (contextUriCharSequence == null) {
             return null;
         }
-        return Uri.parse(contextUriString);
+        return Uri.parse(contextUriCharSequence.toString());
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 96118f6..105b38a 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -444,6 +444,10 @@
      * exist, it may be missing native code for the ABIs supported by the
      * device, or it requires a newer SDK version, etc.
      *
+     * Starting in {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, an app with only 32-bit native
+     * code can still be installed on a device that supports both 64-bit and 32-bit ABIs.
+     * However, a warning dialog will be displayed when the app is launched.
+     *
      * @see #EXTRA_STATUS_MESSAGE
      */
     public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 7f0a666..4e086c2 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1391,7 +1391,7 @@
                             Trace.endSection();
                         }
 
-                        if (redrawNeeded) {
+                        if (redrawNeeded || sizeChanged) {
                             Trace.beginSection("WPMS.Engine.onSurfaceRedrawNeeded");
                             onSurfaceRedrawNeeded(mSurfaceHolder);
                             Trace.endSection();
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index dacb25c..bb5dd7f 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -812,7 +812,7 @@
             Intent recognizerIntent,
             Executor callbackExecutor,
             RecognitionSupportCallback recognitionSupportCallback) {
-        if (!maybeInitializeManagerService()) {
+        if (!maybeInitializeManagerService() || !checkOpenConnection()) {
             return;
         }
         try {
@@ -831,7 +831,7 @@
             Intent recognizerIntent,
             @Nullable Executor callbackExecutor,
             @Nullable ModelDownloadListener modelDownloadListener) {
-        if (!maybeInitializeManagerService()) {
+        if (!maybeInitializeManagerService() || !checkOpenConnection()) {
             return;
         }
 
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index d8bff1c..f570c6d 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -658,6 +658,9 @@
     /** Set of inset types which cannot be controlled by the user animation */
     private @InsetsType int mDisabledUserAnimationInsetsTypes;
 
+    /** Set of inset types which are existing */
+    private @InsetsType int mExistingTypes = 0;
+
     /** Set of inset types which are visible */
     private @InsetsType int mVisibleTypes = WindowInsets.Type.defaultVisible();
 
@@ -906,6 +909,12 @@
             }
             mVisibleTypes = visibleTypes;
         }
+        if (mExistingTypes != existingTypes) {
+            if (WindowInsets.Type.hasCompatSystemBars(mExistingTypes ^ existingTypes)) {
+                mCompatSysUiVisibilityStaled = true;
+            }
+            mExistingTypes = existingTypes;
+        }
         InsetsState.traverse(mState, newState, mRemoveGoneSources);
 
         updateDisabledUserAnimationTypes(disabledUserAnimationTypes);
@@ -1662,7 +1671,8 @@
         if (mCompatSysUiVisibilityStaled) {
             mCompatSysUiVisibilityStaled = false;
             mHost.updateCompatSysUiVisibility(
-                    mVisibleTypes, mRequestedVisibleTypes, mControllableTypes);
+                    // Treat non-existing types as controllable types for compatibility.
+                    mVisibleTypes, mRequestedVisibleTypes, mControllableTypes | ~mExistingTypes);
         }
     }
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ddaa71c..c11f497 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -786,7 +786,11 @@
             mReleaseStack = null;
         }
         setUnreleasedWarningCallSite(callsite);
-        addToRegistry();
+        if (nativeObject != 0) {
+            // Only add valid surface controls to the registry. This is called at the end of this
+            // method since its information is dumped if the process threshold is reached.
+            addToRegistry();
+        }
     }
 
     /**
@@ -893,6 +897,10 @@
                         "Only buffer layers can set a valid buffer size.");
             }
 
+            if (mName == null) {
+                Log.w(TAG, "Missing name for SurfaceControl", new Throwable());
+            }
+
             if ((mFlags & FX_SURFACE_MASK) == FX_SURFACE_NORMAL) {
                 setBLASTLayer();
             }
@@ -1254,6 +1262,9 @@
     }
 
     /**
+     * Note: Most callers should use {@link SurfaceControl.Builder} or one of the other constructors
+     *       to build an instance of a SurfaceControl. This constructor is mainly used for
+     *       unparceling and passing into an AIDL call as an out parameter.
      * @hide
      */
     public SurfaceControl() {
@@ -2495,6 +2506,7 @@
     public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
         long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
         SurfaceControl sc = new SurfaceControl();
+        sc.mName = mirrorOf.mName + " (mirror)";
         sc.assignNativeObject(nativeObj, "mirrorSurface");
         return sc;
     }
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index d987217..c8cf7d9 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -171,8 +171,7 @@
         public SurfacePackage(@NonNull SurfacePackage other) {
             SurfaceControl otherSurfaceControl = other.mSurfaceControl;
             if (otherSurfaceControl != null && otherSurfaceControl.isValid()) {
-                mSurfaceControl = new SurfaceControl();
-                mSurfaceControl.copyFrom(otherSurfaceControl, "SurfacePackage");
+                mSurfaceControl = new SurfaceControl(otherSurfaceControl, "SurfacePackage");
             }
             mAccessibilityEmbeddedConnection = other.mAccessibilityEmbeddedConnection;
             mInputToken = other.mInputToken;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e95ba79..e40f8e7 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -555,6 +555,13 @@
     int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT = (1 << 9); // 0x200
 
     /**
+     * Transition flag: The transition is prepared when nothing is visible on screen, e.g. screen
+     * is off. The animation handlers can decide whether to skip animations.
+     * @hide
+     */
+    int TRANSIT_FLAG_INVISIBLE = (1 << 10); // 0x400
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -567,7 +574,8 @@
             TRANSIT_FLAG_KEYGUARD_LOCKED,
             TRANSIT_FLAG_IS_RECENTS,
             TRANSIT_FLAG_KEYGUARD_GOING_AWAY,
-            TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT
+            TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT,
+            TRANSIT_FLAG_INVISIBLE,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionFlags {}
diff --git a/core/java/android/view/contentprotection/OWNERS b/core/java/android/view/contentprotection/OWNERS
new file mode 100644
index 0000000..b3583a7
--- /dev/null
+++ b/core/java/android/view/contentprotection/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 544200
+
+include /core/java/android/view/contentcapture/OWNERS
+
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5525336..7f96266 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -726,6 +726,11 @@
                 mActions.get(i).visitUris(visitor);
             }
         }
+        if (mSizedRemoteViews != null) {
+            for (int i = 0; i < mSizedRemoteViews.size(); i++) {
+                mSizedRemoteViews.get(i).visitUris(visitor);
+            }
+        }
         if (mLandscape != null) {
             mLandscape.visitUris(visitor);
         }
@@ -1845,7 +1850,7 @@
         }
 
         @Override
-        public final void visitUris(@NonNull Consumer<Uri> visitor) {
+        public void visitUris(@NonNull Consumer<Uri> visitor) {
             switch (this.type) {
                 case URI:
                     final Uri uri = (Uri) getParameterValue(null);
@@ -2308,6 +2313,14 @@
         public int getActionTag() {
             return NIGHT_MODE_REFLECTION_ACTION_TAG;
         }
+
+        @Override
+        public void visitUris(@NonNull Consumer<Uri> visitor) {
+            if (this.type == ICON) {
+                visitIconUri((Icon) mDarkValue, visitor);
+                visitIconUri((Icon) mLightValue, visitor);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 52e17ca..f40874b 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -43,6 +43,7 @@
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getNavigationBarRect;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
@@ -183,7 +184,7 @@
 
             // We consider nearly matched dimensions as there can be rounding errors and the user
             // won't notice very minute differences from scaling one dimension more than the other
-            final boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot);
+            boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshot);
 
             // Keep a reference to it such that it doesn't get destroyed when finalized.
             SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
@@ -199,8 +200,20 @@
             // still hidden.
             mTransaction.show(childSurfaceControl);
             if (aspectRatioMismatch) {
-                // Clip off ugly navigation bar.
-                final Rect crop = calculateSnapshotCrop();
+                Rect crop = null;
+                final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
+                if (letterboxInsets.left != 0 || letterboxInsets.top != 0
+                        || letterboxInsets.right != 0 || letterboxInsets.bottom != 0) {
+                    // Clip off letterbox.
+                    crop = calculateSnapshotCrop(letterboxInsets);
+                    // If the snapshot can cover the frame, then no need to draw background.
+                    aspectRatioMismatch = !isAspectRatioMatch(mFrame, crop);
+                }
+                // if letterbox doesn't match window frame, try crop by content insets
+                if (aspectRatioMismatch) {
+                    // Clip off ugly navigation bar.
+                    crop = calculateSnapshotCrop(mSnapshot.getContentInsets());
+                }
                 frame = calculateSnapshotFrame(crop);
                 mTransaction.setWindowCrop(childSurfaceControl, crop);
                 mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
@@ -242,14 +255,13 @@
 
         /**
          * Calculates the snapshot crop in snapshot coordinate space.
-         *
+         * @param insets Content insets or Letterbox insets
          * @return crop rect in snapshot coordinate space.
          */
-        Rect calculateSnapshotCrop() {
+        Rect calculateSnapshotCrop(@NonNull Rect insets) {
             final Rect rect = new Rect();
             final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
             rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
-            final Rect insets = mSnapshot.getContentInsets();
 
             final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
             final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
@@ -334,6 +346,15 @@
                         - ((float) frame.width() / frame.height())) <= 0.01f;
     }
 
+    private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) {
+        if (frame1.isEmpty() || frame2.isEmpty()) {
+            return false;
+        }
+        return Math.abs(
+                ((float) frame2.width() / frame2.height())
+                        - ((float) frame1.width() / frame1.height())) <= 0.01f;
+    }
+
     /**
      * Get or create a TaskDescription from a RunningTaskInfo.
      */
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index dfdff9e..5d14698 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -26,6 +26,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.ArraySet;
@@ -800,22 +801,25 @@
     }
 
     private void addTimeout() {
+        Looper looper = null;
         synchronized (sHandlerThreadLock) {
             if (sHandlerThread == null) {
                 sHandlerThread = new HandlerThread("SurfaceSyncGroupTimer");
                 sHandlerThread.start();
             }
+
+            looper = sHandlerThread.getLooper();
         }
 
         synchronized (mLock) {
-            if (mTimeoutAdded || mTimeoutDisabled) {
+            if (mTimeoutAdded || mTimeoutDisabled || looper == null) {
                 // We only need one timeout for the entire SurfaceSyncGroup since we just want to
                 // ensure it doesn't stay stuck forever.
                 return;
             }
 
             if (mHandler == null) {
-                mHandler = new Handler(sHandlerThread.getLooper());
+                mHandler = new Handler(looper);
             }
 
             mTimeoutAdded = true;
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 1f7640d..7c4252e 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -258,6 +258,10 @@
     }
 
     private static int convertToLoggingMagnificationScale(float scale) {
-        return (int) (scale * 100);
+        // per b/269366674, we make every 10% a bucket for both privacy and readability concern.
+        // For example
+        // 1. both 2.30f(230%) and 2.36f(236%) would return 230 as bucket id.
+        // 2. bucket id 370 means scale range in [370%, 379%]
+        return ((int) (scale * 10)) * 10;
     }
 }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 86c2893..3d95dd3 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -80,7 +80,7 @@
 
         /** Gating the logging of DND state change events. */
         public static final Flag LOG_DND_STATE_EVENTS =
-                devFlag("persist.sysui.notification.log_dnd_state_events");
+                releasedFlag("persist.sysui.notification.log_dnd_state_events");
     }
 
     //// == End of flags.  Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index 2695b9c..1ac5e1f 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -82,7 +82,7 @@
                 .setIncludePad(getIncludeFontPadding())
                 .setUseLineSpacingFromFallbacks(true)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL_FAST);
         int maxLines;
         if (mMaxLinesForHeight > 0) {
             maxLines = mMaxLinesForHeight;
diff --git a/core/res/res/layout/language_picker_section_header.xml b/core/res/res/layout/language_picker_section_header.xml
index 58042f9..54ac677 100644
--- a/core/res/res/layout/language_picker_section_header.xml
+++ b/core/res/res/layout/language_picker_section_header.xml
@@ -25,4 +25,5 @@
           android:textColor="?android:attr/colorAccent"
           android:textStyle="bold"
           android:id="@+id/language_picker_header"
-          tools:text="@string/language_picker_section_all"/>
+          tools:text="@string/language_picker_section_all"
+          android:scrollbars="none"/>
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index a69c6ff..6f38b84 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -5,3 +5,5 @@
 per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
 per-file *ComponentCallbacks* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *ComponentCallbacks* = charlesccchen@google.com
+per-file ContentCaptureOptions* = file:/core/java/android/view/contentcapture/OWNERS
+
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
index 6e8e93a..b2e42ba 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
@@ -26,6 +26,7 @@
 import android.compat.testing.PlatformCompatChangeRule;
 import android.os.Bundle;
 import android.platform.test.annotations.IwTest;
+import android.platform.test.annotations.PlatinumTest;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.util.PollingCheck;
@@ -72,6 +73,7 @@
         restoreSystemFontScaleToDefault();
     }
 
+    @PlatinumTest(focusArea = "accessibility")
     @IwTest(focusArea = "accessibility")
     @Test
     public void testFontsScaleNonLinearly() {
@@ -103,6 +105,7 @@
         )));
     }
 
+    @PlatinumTest(focusArea = "accessibility")
     @IwTest(focusArea = "accessibility")
     @Test
     public void testOnConfigurationChanged_doesNotCrash() {
@@ -117,6 +120,7 @@
         });
     }
 
+    @PlatinumTest(focusArea = "accessibility")
     @IwTest(focusArea = "accessibility")
     @Test
     public void testUpdateConfiguration_doesNotCrash() {
diff --git a/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java b/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
index d10ba7c..e117051 100644
--- a/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
+++ b/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
@@ -104,6 +104,18 @@
     }
 
     @Test
+    public void testInvalidSurfaceControlNotAddedToRegistry() {
+        int hash0 = SurfaceControlRegistry.getProcessInstance().hashCode();
+        // Verify no changes to the registry when dealing with invalid surface controls
+        SurfaceControl sc0 = new SurfaceControl();
+        SurfaceControl sc1 = new SurfaceControl(sc0, "test");
+        assertEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode());
+        sc0.release();
+        sc1.release();
+        assertEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode());
+    }
+
+    @Test
     public void testThresholds() {
         SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance();
         TestReporter reporter = new TestReporter();
diff --git a/core/tests/coretests/src/android/view/contentprotection/OWNERS b/core/tests/coretests/src/android/view/contentprotection/OWNERS
new file mode 100644
index 0000000..b3583a7
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentprotection/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 544200
+
+include /core/java/android/view/contentcapture/OWNERS
+
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 7879801..73aa936 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -58,6 +58,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Consumer;
@@ -716,6 +717,19 @@
     }
 
     @Test
+    public void visitUris_themedIcons() {
+        RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+        final Icon iconLight = Icon.createWithContentUri("content://light/icon");
+        final Icon iconDark = Icon.createWithContentUri("content://dark/icon");
+        views.setIcon(R.id.layout, "setLargeIcon", iconLight, iconDark);
+
+        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+        views.visitUris(visitor);
+        verify(visitor, times(1)).accept(eq(iconLight.getUri()));
+        verify(visitor, times(1)).accept(eq(iconDark.getUri()));
+    }
+
+    @Test
     public void visitUris_nestedViews() {
         final RemoteViews outer = new RemoteViews(mPackage, R.layout.remote_views_test);
 
@@ -774,4 +788,43 @@
         verify(visitor, times(1)).accept(eq(icon3P.getUri()));
         verify(visitor, times(1)).accept(eq(icon4P.getUri()));
     }
+
+    @Test
+    public void visitUris_sizedViews() {
+        final RemoteViews large = new RemoteViews(mPackage, R.layout.remote_views_test);
+        final Uri imageUriL = Uri.parse("content://large/image");
+        final Icon icon1L = Icon.createWithContentUri("content://large/icon1");
+        final Icon icon2L = Icon.createWithContentUri("content://large/icon2");
+        final Icon icon3L = Icon.createWithContentUri("content://large/icon3");
+        final Icon icon4L = Icon.createWithContentUri("content://large/icon4");
+        large.setImageViewUri(R.id.image, imageUriL);
+        large.setTextViewCompoundDrawables(R.id.text, icon1L, icon2L, icon3L, icon4L);
+
+        final RemoteViews small = new RemoteViews(mPackage, 33);
+        final Uri imageUriS = Uri.parse("content://small/image");
+        final Icon icon1S = Icon.createWithContentUri("content://small/icon1");
+        final Icon icon2S = Icon.createWithContentUri("content://small/icon2");
+        final Icon icon3S = Icon.createWithContentUri("content://small/icon3");
+        final Icon icon4S = Icon.createWithContentUri("content://small/icon4");
+        small.setImageViewUri(R.id.image, imageUriS);
+        small.setTextViewCompoundDrawables(R.id.text, icon1S, icon2S, icon3S, icon4S);
+
+        HashMap<SizeF, RemoteViews> sizedViews = new HashMap<>();
+        sizedViews.put(new SizeF(300, 300), large);
+        sizedViews.put(new SizeF(100, 100), small);
+        RemoteViews views = new RemoteViews(sizedViews);
+
+        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+        views.visitUris(visitor);
+        verify(visitor, times(1)).accept(eq(imageUriL));
+        verify(visitor, times(1)).accept(eq(icon1L.getUri()));
+        verify(visitor, times(1)).accept(eq(icon2L.getUri()));
+        verify(visitor, times(1)).accept(eq(icon3L.getUri()));
+        verify(visitor, times(1)).accept(eq(icon4L.getUri()));
+        verify(visitor, times(1)).accept(eq(imageUriS));
+        verify(visitor, times(1)).accept(eq(icon1S.getUri()));
+        verify(visitor, times(1)).accept(eq(icon2S.getUri()));
+        verify(visitor, times(1)).accept(eq(icon3S.getUri()));
+        verify(visitor, times(1)).accept(eq(icon4S.getUri()));
+    }
 }
diff --git a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
index 6764ac8..0361546 100644
--- a/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
+++ b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java
@@ -158,32 +158,42 @@
 
     @Test
     public void testCalculateSnapshotCrop() {
-        setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, new Rect(0, 0, 100, 100));
-        assertEquals(new Rect(0, 0, 100, 90), mSnapshotSurface.calculateSnapshotCrop());
+        final Rect contentInsets = new Rect(0, 10, 0, 10);
+        setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
+        assertEquals(new Rect(0, 0, 100, 90),
+                mSnapshotSurface.calculateSnapshotCrop(contentInsets));
     }
 
     @Test
     public void testCalculateSnapshotCrop_taskNotOnTop() {
-        setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, new Rect(0, 50, 100, 150));
-        assertEquals(new Rect(0, 10, 100, 90), mSnapshotSurface.calculateSnapshotCrop());
+        final Rect contentInsets = new Rect(0, 10, 0, 10);
+        setupSurface(100, 100, contentInsets, 0, new Rect(0, 50, 100, 150));
+        assertEquals(new Rect(0, 10, 100, 90),
+                mSnapshotSurface.calculateSnapshotCrop(contentInsets));
     }
 
     @Test
     public void testCalculateSnapshotCrop_navBarLeft() {
-        setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, new Rect(0, 0, 100, 100));
-        assertEquals(new Rect(10, 0, 100, 100), mSnapshotSurface.calculateSnapshotCrop());
+        final Rect contentInsets = new Rect(0, 10, 0, 0);
+        setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
+        assertEquals(new Rect(10, 0, 100, 100),
+                mSnapshotSurface.calculateSnapshotCrop(contentInsets));
     }
 
     @Test
     public void testCalculateSnapshotCrop_navBarRight() {
-        setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, new Rect(0, 0, 100, 100));
-        assertEquals(new Rect(0, 0, 90, 100), mSnapshotSurface.calculateSnapshotCrop());
+        final Rect contentInsets = new Rect(0, 10, 10, 0);
+        setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
+        assertEquals(new Rect(0, 0, 90, 100),
+                mSnapshotSurface.calculateSnapshotCrop(contentInsets));
     }
 
     @Test
     public void testCalculateSnapshotCrop_waterfall() {
-        setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, new Rect(0, 0, 100, 100));
-        assertEquals(new Rect(5, 0, 95, 90), mSnapshotSurface.calculateSnapshotCrop());
+        final Rect contentInsets = new Rect(5, 10, 5, 10);
+        setupSurface(100, 100, contentInsets, 0, new Rect(0, 0, 100, 100));
+        assertEquals(new Rect(5, 0, 95, 90),
+                mSnapshotSurface.calculateSnapshotCrop(contentInsets));
     }
 
     @Test
diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java
index f9113a2..6705b25 100644
--- a/graphics/java/android/graphics/GraphicBuffer.java
+++ b/graphics/java/android/graphics/GraphicBuffer.java
@@ -57,7 +57,7 @@
     private final int mUsage;
     // Note: do not rename, this field is used by native code
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private final long mNativeObject;
+    private long mNativeObject;
 
     // These two fields are only used by lock/unlockCanvas()
     private Canvas mCanvas;
@@ -219,6 +219,7 @@
         if (!mDestroyed) {
             mDestroyed = true;
             nDestroyGraphicBuffer(mNativeObject);
+            mNativeObject = 0;
         }
     }
 
@@ -239,7 +240,7 @@
     @Override
     protected void finalize() throws Throwable {
         try {
-            if (!mDestroyed) nDestroyGraphicBuffer(mNativeObject);
+            destroy();
         } finally {
             super.finalize();
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 4d8075a..c25352b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -121,21 +121,21 @@
         }
 
         boolean hasOpeningOcclude = false;
+        boolean hasClosingOcclude = false;
         boolean hasOpeningDream = false;
         boolean hasClosingApp = false;
 
         // Check for occluding/dream/closing apps
         for (int i = info.getChanges().size() - 1; i >= 0; i--) {
             final TransitionInfo.Change change = info.getChanges().get(i);
-            if (isOpeningType(change.getMode())) {
-                if (change.hasFlags(FLAG_OCCLUDES_KEYGUARD)) {
-                    hasOpeningOcclude = true;
-                }
-                if (change.getTaskInfo() != null
-                        && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM) {
-                    hasOpeningDream = true;
-                }
+            if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
+                continue;
+            } else if (isOpeningType(change.getMode())) {
+                hasOpeningOcclude |= change.hasFlags(FLAG_OCCLUDES_KEYGUARD);
+                hasOpeningDream |= (change.getTaskInfo() != null
+                        && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM);
             } else if (isClosingType(change.getMode())) {
+                hasClosingOcclude |= change.hasFlags(FLAG_OCCLUDES_KEYGUARD);
                 hasClosingApp = true;
             }
         }
@@ -147,6 +147,11 @@
                     transition, info, startTransaction, finishTransaction, finishCallback);
         }
         if (hasOpeningOcclude || info.getType() == TRANSIT_KEYGUARD_OCCLUDE) {
+            if (hasClosingOcclude) {
+                // Transitions between apps on top of the keyguard can use the default handler.
+                // WM sends a final occlude status update after the transition is finished.
+                return false;
+            }
             if (hasOpeningDream) {
                 return startAnimation(mOccludeByDreamTransition,
                         "occlude-by-dream",
@@ -161,7 +166,7 @@
                     "unocclude",
                     transition, info, startTransaction, finishTransaction, finishCallback);
         } else {
-            Log.wtf(TAG, "Failed to play: " + info);
+            Log.w(TAG, "Failed to play: " + info);
             return false;
         }
     }
@@ -181,6 +186,7 @@
                         public void onTransitionFinished(
                                 WindowContainerTransaction wct, SurfaceControl.Transaction sct) {
                             mMainExecutor.execute(() -> {
+                                mStartedTransitions.remove(transition);
                                 finishCallback.onTransitionFinished(wct, null);
                             });
                         }
@@ -201,7 +207,7 @@
         final IRemoteTransition playing = mStartedTransitions.get(currentTransition);
 
         if (playing == null) {
-            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, 
+            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                     "unknown keyguard transition %s", currentTransition);
             return;
         }
@@ -212,14 +218,17 @@
             // the device sleeping/waking, so it's best to ignore this and keep playing anyway.
             return;
         } else {
-            finishAnimationImmediately(currentTransition);
+            finishAnimationImmediately(currentTransition, playing);
         }
     }
 
     @Override
     public void onTransitionConsumed(IBinder transition, boolean aborted,
             SurfaceControl.Transaction finishTransaction) {
-        finishAnimationImmediately(transition);
+        final IRemoteTransition playing = mStartedTransitions.remove(transition);
+        if (playing != null) {
+            finishAnimationImmediately(transition, playing);
+        }
     }
 
     @Nullable
@@ -229,21 +238,17 @@
         return null;
     }
 
-    private void finishAnimationImmediately(IBinder transition) {
-        final IRemoteTransition playing = mStartedTransitions.get(transition);
-
-        if (playing != null) {
-            final IBinder fakeTransition = new Binder();
-            final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0);
-            final SurfaceControl.Transaction fakeT = new SurfaceControl.Transaction();
-            final FakeFinishCallback fakeFinishCb = new FakeFinishCallback();
-            try {
-                playing.mergeAnimation(fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb);
-            } catch (RemoteException e) {
-                // There is no good reason for this to happen because the player is a local object
-                // implementing an AIDL interface.
-                Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e);
-            }
+    private void finishAnimationImmediately(IBinder transition, IRemoteTransition playing) {
+        final IBinder fakeTransition = new Binder();
+        final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0);
+        final SurfaceControl.Transaction fakeT = new SurfaceControl.Transaction();
+        final FakeFinishCallback fakeFinishCb = new FakeFinishCallback();
+        try {
+            playing.mergeAnimation(fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb);
+        } catch (RemoteException e) {
+            // There is no good reason for this to happen because the player is a local object
+            // implementing an AIDL interface.
+            Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index d2b0e28..9863099 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -348,8 +348,6 @@
             WindowContainerTransaction wct,
             @Nullable RemoteTransition remoteTransition,
             Transitions.TransitionHandler handler,
-            @Nullable TransitionConsumedCallback consumedCallback,
-            @Nullable TransitionFinishedCallback finishedCallback,
             int extraTransitType, boolean resizeAnim) {
         if (mPendingEnter != null) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
@@ -357,20 +355,16 @@
             return null;
         }
         final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
-        setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback,
-                extraTransitType, resizeAnim);
+        setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
         return transition;
     }
 
     /** Sets a transition to enter split. */
     void setEnterTransition(@NonNull IBinder transition,
             @Nullable RemoteTransition remoteTransition,
-            @Nullable TransitionConsumedCallback consumedCallback,
-            @Nullable TransitionFinishedCallback finishedCallback,
             int extraTransitType, boolean resizeAnim) {
         mPendingEnter = new EnterSession(
-                transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType,
-                resizeAnim);
+                transition, remoteTransition, extraTransitType, resizeAnim);
 
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                 + " deduced Enter split screen");
@@ -608,12 +602,10 @@
         final boolean mResizeAnim;
 
         EnterSession(IBinder transition,
-                @Nullable TransitionConsumedCallback consumedCallback,
-                @Nullable TransitionFinishedCallback finishedCallback,
                 @Nullable RemoteTransition remoteTransition,
                 int extraTransitType, boolean resizeAnim) {
-            super(transition, consumedCallback, finishedCallback, remoteTransition,
-                    extraTransitType);
+            super(transition, null /* consumedCallback */, null /* finishedCallback */,
+                    remoteTransition, extraTransitType);
             this.mResizeAnim = resizeAnim;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e0fffff..b2526ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -192,6 +192,9 @@
     private final SplitScreenTransitions mSplitTransitions;
     private final SplitscreenEventLogger mLogger;
     private final ShellExecutor mMainExecutor;
+    // Cache live tile tasks while entering recents, evict them from stages in finish transaction
+    // if user is opening another task(s).
+    private final ArrayList<Integer> mPausingTasks = new ArrayList<>();
     private final Optional<RecentTasksController> mRecentTasks;
 
     private final Rect mTempRect1 = new Rect();
@@ -393,7 +396,7 @@
         prepareEnterSplitScreen(wct, task, stagePosition);
         if (ENABLE_SHELL_TRANSITIONS) {
             mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct,
-                    null, this, null /* consumedCallback */, null /* finishedCallback */,
+                    null, this,
                     isSplitScreenVisible()
                             ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN,
                     !mIsDropEntering);
@@ -504,8 +507,7 @@
         prepareEnterSplitScreen(wct, null /* taskInfo */, position);
 
         mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this,
-                null /* consumedCallback */, null /* finishedCallback */, extraTransitType,
-                !mIsDropEntering);
+                extraTransitType, !mIsDropEntering);
     }
 
     /** Launches an activity into split by legacy transition. */
@@ -658,8 +660,11 @@
         // Add task launch requests
         wct.startTask(mainTaskId, mainOptions);
 
-        mSplitTransitions.startEnterTransition(
-                TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null,
+        // leave recents animation by re-start pausing tasks
+        if (mPausingTasks.contains(mainTaskId)) {
+            mPausingTasks.clear();
+        }
+        mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, remoteTransition, this,
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
         setEnterInstanceId(instanceId);
     }
@@ -709,8 +714,7 @@
             wct.sendPendingIntent(pendingIntent2, fillInIntent2, options2);
         }
 
-        mSplitTransitions.startEnterTransition(
-                TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null,
+        mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, remoteTransition, this,
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
         setEnterInstanceId(instanceId);
     }
@@ -1446,8 +1450,6 @@
         if (!mMainStage.isActive()) return;
         mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
         mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
-        wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
-                false /* reparentLeafTaskIfRelaunch */);
     }
 
     private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
@@ -1630,7 +1632,8 @@
     }
 
     private void updateRecentTasksSplitPair() {
-        if (!mShouldUpdateRecents) {
+        // Preventing from single task update while processing recents.
+        if (!mShouldUpdateRecents || !mPausingTasks.isEmpty()) {
             return;
         }
         mRecentTasks.ifPresent(recentTasks -> {
@@ -2287,14 +2290,28 @@
             out = new WindowContainerTransaction();
             final StageTaskListener stage = getStageOfTask(triggerTask);
             if (stage != null) {
-                // Dismiss split if the last task in one of the stages is going away
                 if (isClosingType(type) && stage.getChildCount() == 1) {
+                    // Dismiss split if the last task in one of the stages is going away
                     // The top should be the opposite side that is closing:
-                    int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
-                            : STAGE_TYPE_MAIN;
+                    int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN
+                            ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
                     prepareExitSplitScreen(dismissTop, out);
                     mSplitTransitions.setDismissTransition(transition, dismissTop,
                             EXIT_REASON_APP_FINISHED);
+                } else if (isOpening && !mPausingTasks.isEmpty()) {
+                    // One of the splitting task is opening while animating the split pair in
+                    // recents, which means to dismiss the split pair to this task.
+                    int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN
+                            ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+                    prepareExitSplitScreen(dismissTop, out);
+                    mSplitTransitions.setDismissTransition(transition, dismissTop,
+                            EXIT_REASON_APP_FINISHED);
+                } else if (!isSplitScreenVisible() && isOpening) {
+                    // If split running backgroud and trigger task is appearing into split,
+                    // prepare to enter split screen.
+                    prepareEnterSplitScreen(out);
+                    mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
+                            TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
                 }
             } else if (isOpening && inFullscreen) {
                 final int activityType = triggerTask.getActivityType();
@@ -2319,8 +2336,7 @@
                 out = new WindowContainerTransaction();
                 prepareEnterSplitScreen(out);
                 mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
-                        null /* consumedCallback */, null /* finishedCallback */,
-                        TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, !mIsDropEntering);
+                        TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
             }
         }
         return out;
@@ -2578,17 +2594,25 @@
         // First, verify that we actually have opened apps in both splits.
         TransitionInfo.Change mainChild = null;
         TransitionInfo.Change sideChild = null;
+        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         for (int iC = 0; iC < info.getChanges().size(); ++iC) {
             final TransitionInfo.Change change = info.getChanges().get(iC);
             final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
             if (taskInfo == null || !taskInfo.hasParentTask()) continue;
+            if (mPausingTasks.contains(taskInfo.taskId)) {
+                continue;
+            }
             final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
-            if (stageType == STAGE_TYPE_MAIN
+            if (mainChild == null && stageType == STAGE_TYPE_MAIN
                     && (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) {
                 // Includes TRANSIT_CHANGE to cover reparenting top-most task to split.
                 mainChild = change;
-            } else if (stageType == STAGE_TYPE_SIDE && isOpeningType(change.getMode())) {
+            } else if (sideChild == null && stageType == STAGE_TYPE_SIDE
+                    && isOpeningType(change.getMode())) {
                 sideChild = change;
+            } else if (stageType != STAGE_TYPE_UNDEFINED && change.getMode() == TRANSIT_TO_BACK) {
+                // Collect all to back task's and evict them when transition finished.
+                evictWct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
             }
         }
 
@@ -2650,10 +2674,15 @@
                     mSideStage.evictInvisibleChildren(callbackWct);
                 }
             }
+            if (!evictWct.isEmpty()) {
+                callbackWct.merge(evictWct, true);
+            }
             if (enterTransition.mResizeAnim) {
                 mShowDecorImmediately = true;
                 mSplitLayout.flingDividerToCenter();
             }
+            callbackWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, false);
+            mPausingTasks.clear();
         });
 
         finishEnterSplitScreen(finishT);
@@ -2803,6 +2832,7 @@
         dismissTransition.setFinishedCallback((callbackWct, callbackT) -> {
             mMainStage.getSplitDecorManager().release(callbackT);
             mSideStage.getSplitDecorManager().release(callbackT);
+            callbackWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, false);
         });
 
         addDividerBarToTransition(info, false /* show */);
@@ -2811,12 +2841,33 @@
 
     /** Call this when starting the open-recents animation while split-screen is active. */
     public void onRecentsInSplitAnimationStart(TransitionInfo info) {
+        if (isSplitScreenVisible()) {
+            // Cache tasks on live tile.
+            for (int i = 0; i < info.getChanges().size(); ++i) {
+                final TransitionInfo.Change change = info.getChanges().get(i);
+                if (TransitionUtil.isClosingType(change.getMode())
+                        && change.getTaskInfo() != null) {
+                    final int taskId = change.getTaskInfo().taskId;
+                    if (mMainStage.getTopVisibleChildTaskId() == taskId
+                            || mSideStage.getTopVisibleChildTaskId() == taskId) {
+                        mPausingTasks.add(taskId);
+                    }
+                }
+            }
+        }
+
         addDividerBarToTransition(info, false /* show */);
     }
 
+    /** Call this when the recents animation canceled during split-screen. */
+    public void onRecentsInSplitAnimationCanceled() {
+        mPausingTasks.clear();
+    }
+
     /** Call this when the recents animation during split-screen finishes. */
     public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
-            SurfaceControl.Transaction finishT, TransitionInfo info) {
+            SurfaceControl.Transaction finishT) {
+        mPausingTasks.clear();
         // Check if the recent transition is finished by returning to the current
         // split, so we can restore the divider bar.
         for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
@@ -2835,11 +2886,31 @@
         }
 
         setSplitsVisible(false);
-        finishWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
-                true /* reparentLeafTaskIfRelaunch */);
+        prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
         logExit(EXIT_REASON_UNKNOWN);
     }
 
+    /** Call this when the recents animation finishes by doing pair-to-pair switch. */
+    public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) {
+        // Pair-to-pair switch happened so here should evict the live tile from its stage.
+        // Otherwise, the task will remain in stage, and occluding the new task when next time
+        // user entering recents.
+        for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
+            final int taskId = mPausingTasks.get(i);
+            if (mMainStage.containsTask(taskId)) {
+                mMainStage.evictChildren(finishWct, taskId);
+            } else if (mSideStage.containsTask(taskId)) {
+                mSideStage.evictChildren(finishWct, taskId);
+            }
+        }
+        // If pending enter hasn't consumed, the mix handler will invoke start pending
+        // animation within following transition.
+        if (mSplitTransitions.mPendingEnter == null) {
+            mPausingTasks.clear();
+            updateRecentTasksSplitPair();
+        }
+    }
+
     private void addDividerBarToTransition(@NonNull TransitionInfo info, boolean show) {
         final SurfaceControl leash = mSplitLayout.getDividerLeash();
         if (leash == null || !leash.isValid()) {
@@ -2892,6 +2963,9 @@
             pw.println(innerPrefix + "SplitLayout");
             mSplitLayout.dump(pw, childPrefix);
         }
+        if (!mPausingTasks.isEmpty()) {
+            pw.println(childPrefix + "mPausingTasks=" + mPausingTasks);
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index da7d186..92ff5fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -377,6 +377,13 @@
         }
     }
 
+    void evictChildren(WindowContainerTransaction wct, int taskId) {
+        final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.get(taskId);
+        if (taskInfo != null) {
+            wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
+        }
+    }
+
     void reparentTopTask(WindowContainerTransaction wct) {
         wct.reparentTasks(null /* currentParent */, mRootTaskInfo.token,
                 CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 863b5ab..64571e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -540,9 +540,12 @@
             mixed.mInFlightSubAnimations = 0;
             mActiveTransitions.remove(mixed);
             // If pair-to-pair switching, the post-recents clean-up isn't needed.
+            wct = wct != null ? wct : new WindowContainerTransaction();
             if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) {
-                wct = wct != null ? wct : new WindowContainerTransaction();
-                mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction, info);
+                mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
+            } else {
+                // notify pair-to-pair recents animation finish
+                mSplitHandler.onRecentsPairToPairAnimationFinish(wct);
             }
             mSplitHandler.onTransitionAnimationComplete();
             finishCallback.onTransitionFinished(wct, wctCB);
@@ -552,6 +555,7 @@
         final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
                 startTransaction, finishTransaction, finishCB);
         if (!handled) {
+            mSplitHandler.onRecentsInSplitAnimationCanceled();
             mActiveTransitions.remove(mixed);
         }
         return handled;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 6a2468a..4c678a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -299,7 +299,8 @@
         }
 
         // Early check if the transition doesn't warrant an animation.
-        if (Transitions.isAllNoAnimation(info) || Transitions.isAllOrderOnly(info)) {
+        if (Transitions.isAllNoAnimation(info) || Transitions.isAllOrderOnly(info)
+                || (info.getFlags() & WindowManager.TRANSIT_FLAG_INVISIBLE) != 0) {
             startTransaction.apply();
             finishTransaction.apply();
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
@@ -326,6 +327,7 @@
         final int wallpaperTransit = getWallpaperTransitType(info);
         boolean isDisplayRotationAnimationStarted = false;
         final boolean isDreamTransition = isDreamTransition(info);
+        final boolean isOnlyTranslucent = isOnlyTranslucent(info);
 
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
@@ -451,6 +453,17 @@
                             final int layer = zSplitLine + numChanges - i;
                             startTransaction.setLayer(change.getLeash(), layer);
                         }
+                    } else if (isOnlyTranslucent && TransitionUtil.isOpeningType(info.getType())
+                                && TransitionUtil.isClosingType(mode)) {
+                        // If there is a closing translucent task in an OPENING transition, we will
+                        // actually select a CLOSING animation, so move the closing task into
+                        // the animating part of the z-order.
+
+                        // See Transitions#setupAnimHierarchy for details about these variables.
+                        final int numChanges = info.getChanges().size();
+                        final int zSplitLine = numChanges + 1;
+                        final int layer = zSplitLine + numChanges - i;
+                        startTransaction.setLayer(change.getLeash(), layer);
                     }
                 }
 
@@ -542,6 +555,29 @@
         return false;
     }
 
+    /**
+     * Does `info` only contain translucent visibility changes (CHANGEs are ignored). We select
+     * different animations and z-orders for these
+     */
+    private static boolean isOnlyTranslucent(@NonNull TransitionInfo info) {
+        int translucentOpen = 0;
+        int translucentClose = 0;
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.getMode() == TRANSIT_CHANGE) continue;
+            if (change.hasFlags(FLAG_TRANSLUCENT)) {
+                if (TransitionUtil.isOpeningType(change.getMode())) {
+                    translucentOpen += 1;
+                } else {
+                    translucentClose += 1;
+                }
+            } else {
+                return false;
+            }
+        }
+        return (translucentOpen + translucentClose) > 0;
+    }
+
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 19d8384..d978eaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
@@ -102,10 +103,11 @@
             // We will translucent open animation for translucent activities and tasks. Choose
             // WindowAnimation_activityOpenEnterAnimation and set translucent here, then
             // TransitionAnimation loads appropriate animation later.
-            if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
-                translucent = true;
-            }
-            if (isTask && !translucent) {
+            translucent = (changeFlags & FLAG_TRANSLUCENT) != 0;
+            if (isTask && translucent && !enter) {
+                // For closing translucent tasks, use the activity close animation
+                animAttr = R.styleable.WindowAnimation_activityCloseExitAnimation;
+            } else if (isTask && !translucent) {
                 animAttr = enter
                         ? R.styleable.WindowAnimation_taskOpenEnterAnimation
                         : R.styleable.WindowAnimation_taskOpenExitAnimation;
@@ -150,6 +152,10 @@
                             .loadAnimationAttr(options.getPackageName(), options.getAnimations(),
                                     animAttr, translucent);
                 }
+            } else if (translucent && !isTask && ((changeFlags & FLAGS_IS_NON_APP_WINDOW) == 0)) {
+                // Un-styled translucent activities technically have undefined animations; however,
+                // as is always the case, some apps now rely on this being no-animation, so skip
+                // loading animations here.
             } else {
                 a = transitionAnimation.loadDefaultAnimationAttr(animAttr, translucent);
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 3b306e7..ce8d792 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -16,9 +16,12 @@
 
 package com.android.wm.shell.transition;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_SLEEP;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -567,8 +570,8 @@
                     layer = zSplitLine + numChanges - i;
                 }
             } else { // CHANGE or other
-                if (isClosing) {
-                    // Put below CLOSE mode.
+                if (isClosing || TransitionUtil.isOrderOnly(change)) {
+                    // Put below CLOSE mode (in the "static" section).
                     layer = zSplitLine - i;
                 } else {
                     // Put above CLOSE mode.
@@ -1081,6 +1084,16 @@
                 }
             }
         }
+        if (request.getType() == TRANSIT_KEYGUARD_OCCLUDE && request.getTriggerTask() != null
+                && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+            // This freeform task is on top of keyguard, so its windowing mode should be changed to
+            // fullscreen.
+            if (wct == null) {
+                wct = new WindowContainerTransaction();
+            }
+            wct.setWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_FULLSCREEN);
+            wct.setBounds(request.getTriggerTask().token, null);
+        }
         mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
         active.mToken = transitionToken;
         // Currently, WMCore only does one transition at a time. If it makes a requestStart, it
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index b7e73ad..72f25f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -18,6 +18,7 @@
 
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.common.traces.component.EdgeExtensionComponentMatcher
@@ -60,6 +61,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 0b0a3da..ed3df9c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -18,6 +18,7 @@
 
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -58,6 +59,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 8cf871f..b4c6afd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
@@ -59,6 +60,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
index 3b2da8d..2cedc35 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
@@ -18,6 +18,7 @@
 
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -62,6 +63,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index a189a3f..a5ad97d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -62,6 +63,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 55ab7b3..fa6a4bf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -68,6 +69,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index c4cfd1a..d2beb67 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -55,6 +56,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index 146287c..e95fd94 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -58,6 +59,7 @@
         Assume.assumeTrue(tapl.isTablet || !flicker.scenario.isLandscapeOrSeascapeAtStart)
     }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
index cc71502..63b74e2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -70,6 +71,7 @@
         Assume.assumeTrue(tapl.isTablet)
     }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
index de78f09..e94da87 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -67,6 +68,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
index a29eb40..f41117f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -70,6 +71,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
index b2395ca..12f610b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -65,6 +66,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index e1d85d0..77818d3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -64,6 +65,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index ba8c460..6ff2290 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.common.Rotation
@@ -134,6 +135,7 @@
         return displayBounds.width > displayBounds.height
     }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index bbb2edc..400adea 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -63,6 +64,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index fa38293..1ec4340 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -61,6 +62,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index 1064bd9..9757153 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.NavBar
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -61,6 +62,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index 8f4393f..c19a38d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.splitscreen.benchmark
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -64,6 +65,7 @@
             thisTransition(this)
         }
 
+    @PlatinumTest(focusArea = "sysui")
     @IwTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() {}
 
     companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 60c0e55..3b05651 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -185,7 +185,7 @@
 
         IBinder transition = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null,
+                new RemoteTransition(testRemote, "Test"), mStageCoordinator,
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
@@ -283,7 +283,7 @@
         // Make sure it cleans-up if recents doesn't restore
         WindowContainerTransaction commitWCT = new WindowContainerTransaction();
         mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
-                mock(SurfaceControl.Transaction.class), mock(TransitionInfo.class));
+                mock(SurfaceControl.Transaction.class));
         assertFalse(mStageCoordinator.isSplitScreenVisible());
     }
 
@@ -322,7 +322,7 @@
         mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class));
         mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class));
         mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
-                mock(SurfaceControl.Transaction.class), mock(TransitionInfo.class));
+                mock(SurfaceControl.Transaction.class));
         assertTrue(mStageCoordinator.isSplitScreenVisible());
     }
 
@@ -412,7 +412,7 @@
         IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_OPEN, new WindowContainerTransaction(),
                 new RemoteTransition(new TestRemoteTransition(), "Test"),
-                mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+                mStageCoordinator, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 7af6efb..06aed63 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -17,7 +17,6 @@
 #include "Properties.h"
 
 #include "Debug.h"
-#include "log/log_main.h"
 #ifdef __ANDROID__
 #include "HWUIProperties.sysprop.h"
 #endif
@@ -220,12 +219,15 @@
     return sRenderPipelineType;
 }
 
-void Properties::overrideRenderPipelineType(RenderPipelineType type, bool inUnitTest) {
+void Properties::overrideRenderPipelineType(RenderPipelineType type) {
     // If we're doing actual rendering then we can't change the renderer after it's been set.
-    // Unit tests can freely change this as often as it wants.
-    LOG_ALWAYS_FATAL_IF(sRenderPipelineType != RenderPipelineType::NotInitialized &&
-                                sRenderPipelineType != type && !inUnitTest,
-                        "Trying to change pipeline but it's already set.");
+    // Unit tests can freely change this as often as it wants, though, as there's no actual
+    // GL rendering happening
+    if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
+        LOG_ALWAYS_FATAL_IF(sRenderPipelineType != type,
+                            "Trying to change pipeline but it's already set");
+        return;
+    }
     sRenderPipelineType = type;
 }
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 24e206b..bb47744 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -303,7 +303,7 @@
     static bool enableRTAnimations;
 
     // Used for testing only to change the render pipeline.
-    static void overrideRenderPipelineType(RenderPipelineType, bool inUnitTest = false);
+    static void overrideRenderPipelineType(RenderPipelineType);
 
     static bool runningInEmulator;
 
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4cffc6c..d4e919f 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -23,13 +23,11 @@
 #include <GrDirectContext.h>
 #include <GrTypes.h>
 #include <android/sync.h>
+#include <gui/TraceUtils.h>
 #include <ui/FatVector.h>
 #include <vk/GrVkExtensions.h>
 #include <vk/GrVkTypes.h>
 
-#include <cstring>
-
-#include <gui/TraceUtils.h>
 #include "Properties.h"
 #include "RenderThread.h"
 #include "pipeline/skia/ShaderCache.h"
@@ -90,19 +88,6 @@
     }
 }
 
-GrVkGetProc VulkanManager::sSkiaGetProp = [](const char* proc_name, VkInstance instance,
-                                             VkDevice device) {
-    if (device != VK_NULL_HANDLE) {
-        if (strcmp("vkQueueSubmit", proc_name) == 0) {
-            return (PFN_vkVoidFunction)VulkanManager::interceptedVkQueueSubmit;
-        } else if (strcmp("vkQueueWaitIdle", proc_name) == 0) {
-            return (PFN_vkVoidFunction)VulkanManager::interceptedVkQueueWaitIdle;
-        }
-        return vkGetDeviceProcAddr(device, proc_name);
-    }
-    return vkGetInstanceProcAddr(instance, proc_name);
-};
-
 #define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F)
 #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
 #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
@@ -138,6 +123,7 @@
     }
 
     mGraphicsQueue = VK_NULL_HANDLE;
+    mAHBUploadQueue = VK_NULL_HANDLE;
     mDevice = VK_NULL_HANDLE;
     mPhysicalDevice = VK_NULL_HANDLE;
     mInstance = VK_NULL_HANDLE;
@@ -231,7 +217,7 @@
     mDriverVersion = physDeviceProperties.driverVersion;
 
     // query to get the initial queue props size
-    uint32_t queueCount;
+    uint32_t queueCount = 0;
     mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
     LOG_ALWAYS_FATAL_IF(!queueCount);
 
@@ -239,11 +225,14 @@
     std::unique_ptr<VkQueueFamilyProperties[]> queueProps(new VkQueueFamilyProperties[queueCount]);
     mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, queueProps.get());
 
+    constexpr auto kRequestedQueueCount = 2;
+
     // iterate to find the graphics queue
     mGraphicsQueueIndex = queueCount;
     for (uint32_t i = 0; i < queueCount; i++) {
         if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
             mGraphicsQueueIndex = i;
+            LOG_ALWAYS_FATAL_IF(queueProps[i].queueCount < kRequestedQueueCount);
             break;
         }
     }
@@ -273,7 +262,14 @@
         LOG_ALWAYS_FATAL_IF(!hasKHRSwapchainExtension);
     }
 
-    grExtensions.init(sSkiaGetProp, mInstance, mPhysicalDevice, mInstanceExtensions.size(),
+    auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) {
+        if (device != VK_NULL_HANDLE) {
+            return vkGetDeviceProcAddr(device, proc_name);
+        }
+        return vkGetInstanceProcAddr(instance, proc_name);
+    };
+
+    grExtensions.init(getProc, mInstance, mPhysicalDevice, mInstanceExtensions.size(),
                       mInstanceExtensions.data(), mDeviceExtensions.size(),
                       mDeviceExtensions.data());
 
@@ -312,7 +308,7 @@
     // and we can't depend on it on all platforms
     features.features.robustBufferAccess = VK_FALSE;
 
-    float queuePriorities[1] = {0.0};
+    float queuePriorities[kRequestedQueueCount] = {0.0};
 
     void* queueNextPtr = nullptr;
 
@@ -345,7 +341,7 @@
             queueNextPtr,                                // pNext
             0,                                           // VkDeviceQueueCreateFlags
             mGraphicsQueueIndex,                         // queueFamilyIndex
-            1,                                           // queueCount
+            kRequestedQueueCount,                        // queueCount
             queuePriorities,                             // pQueuePriorities
     };
 
@@ -403,6 +399,7 @@
     this->setupDevice(mExtensions, mPhysicalDeviceFeatures2);
 
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
+    mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue);
 
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
@@ -416,16 +413,24 @@
 
 sk_sp<GrDirectContext> VulkanManager::createContext(GrContextOptions& options,
                                                     ContextType contextType) {
+    auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) {
+        if (device != VK_NULL_HANDLE) {
+            return vkGetDeviceProcAddr(device, proc_name);
+        }
+        return vkGetInstanceProcAddr(instance, proc_name);
+    };
+
     GrVkBackendContext backendContext;
     backendContext.fInstance = mInstance;
     backendContext.fPhysicalDevice = mPhysicalDevice;
     backendContext.fDevice = mDevice;
-    backendContext.fQueue = mGraphicsQueue;
+    backendContext.fQueue =
+            (contextType == ContextType::kRenderThread) ? mGraphicsQueue : mAHBUploadQueue;
     backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
     backendContext.fMaxAPIVersion = mAPIVersion;
     backendContext.fVkExtensions = &mExtensions;
     backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
-    backendContext.fGetProc = sSkiaGetProp;
+    backendContext.fGetProc = std::move(getProc);
 
     LOG_ALWAYS_FATAL_IF(options.fContextDeleteProc != nullptr, "Conflicting fContextDeleteProcs!");
     this->incStrong((void*)onGrContextReleased);
@@ -636,8 +641,6 @@
         ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
     } else {
         ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed");
-
-        std::lock_guard<std::mutex> lock(mGraphicsQueueMutex);
         mQueueWaitIdle(mGraphicsQueue);
     }
     if (mDestroySemaphoreContext) {
@@ -652,7 +655,6 @@
 void VulkanManager::destroySurface(VulkanSurface* surface) {
     // Make sure all submit commands have finished before starting to destroy objects.
     if (VK_NULL_HANDLE != mGraphicsQueue) {
-        std::lock_guard<std::mutex> lock(mGraphicsQueueMutex);
         mQueueWaitIdle(mGraphicsQueue);
     }
     mDeviceWaitIdle(mDevice);
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 00a40c0..2be1ffd 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -17,10 +17,6 @@
 #ifndef VULKANMANAGER_H
 #define VULKANMANAGER_H
 
-#include <functional>
-#include <mutex>
-
-#include "vulkan/vulkan_core.h"
 #if !defined(VK_USE_PLATFORM_ANDROID_KHR)
 #define VK_USE_PLATFORM_ANDROID_KHR
 #endif
@@ -186,25 +182,8 @@
     VkDevice mDevice = VK_NULL_HANDLE;
 
     uint32_t mGraphicsQueueIndex;
-
-    std::mutex mGraphicsQueueMutex;
     VkQueue mGraphicsQueue = VK_NULL_HANDLE;
-
-    static VKAPI_ATTR VkResult interceptedVkQueueSubmit(VkQueue queue, uint32_t submitCount,
-                                                        const VkSubmitInfo* pSubmits,
-                                                        VkFence fence) {
-        sp<VulkanManager> manager = VulkanManager::getInstance();
-        std::lock_guard<std::mutex> lock(manager->mGraphicsQueueMutex);
-        return manager->mQueueSubmit(queue, submitCount, pSubmits, fence);
-    }
-
-    static VKAPI_ATTR VkResult interceptedVkQueueWaitIdle(VkQueue queue) {
-        sp<VulkanManager> manager = VulkanManager::getInstance();
-        std::lock_guard<std::mutex> lock(manager->mGraphicsQueueMutex);
-        return manager->mQueueWaitIdle(queue);
-    }
-
-    static GrVkGetProc sSkiaGetProp;
+    VkQueue mAHBUploadQueue = VK_NULL_HANDLE;
 
     // Variables saved to populate VkFunctorInitParams.
     static const uint32_t mAPIVersion = VK_MAKE_VERSION(1, 1, 0);
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 9d5c13e..81ecfe5 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -61,12 +61,12 @@
         ADD_FAILURE() << "ClipState not a rect";                                     \
     }
 
-#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall)      \
-    TEST(test_case_name, test_name##_##pipeline) {                                  \
-        RenderPipelineType oldType = Properties::getRenderPipelineType();           \
-        Properties::overrideRenderPipelineType(RenderPipelineType::pipeline, true); \
-        functionCall;                                                               \
-        Properties::overrideRenderPipelineType(oldType, true);                      \
+#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
+    TEST(test_case_name, test_name##_##pipeline) {                             \
+        RenderPipelineType oldType = Properties::getRenderPipelineType();      \
+        Properties::overrideRenderPipelineType(RenderPipelineType::pipeline);  \
+        functionCall;                                                          \
+        Properties::overrideRenderPipelineType(oldType);                       \
     };
 
 #define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
@@ -78,27 +78,29 @@
  * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
  * (for e.g. accessing its RenderState)
  */
-#define RENDERTHREAD_TEST(test_case_name, test_name)                         \
-    class test_case_name##_##test_name##_RenderThreadTest {                  \
-    public:                                                                  \
-        static void doTheThing(renderthread::RenderThread& renderThread);    \
-    };                                                                       \
-    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL);     \
-    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \
-    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(        \
+#define RENDERTHREAD_TEST(test_case_name, test_name)                                        \
+    class test_case_name##_##test_name##_RenderThreadTest {                                 \
+    public:                                                                                 \
+        static void doTheThing(renderthread::RenderThread& renderThread);                   \
+    };                                                                                      \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL);                    \
+    /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
+    /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */          \
+    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(                       \
             renderthread::RenderThread& renderThread)
 
 /**
  * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
  */
-#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name)           \
-    class test_case_name##_##test_name##_RenderThreadTest {                  \
-    public:                                                                  \
-        static void doTheThing(renderthread::RenderThread& renderThread);    \
-    };                                                                       \
-    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL);     \
-    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \
-    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(        \
+#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name)                          \
+    class test_case_name##_##test_name##_RenderThreadTest {                                 \
+    public:                                                                                 \
+        static void doTheThing(renderthread::RenderThread& renderThread);                   \
+    };                                                                                      \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL);                    \
+    /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
+    /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */          \
+    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(                       \
             renderthread::RenderThread& renderThread)
 
 /**
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 11cb2be..1e3c154 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -81,6 +81,18 @@
     public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
 
     /**
+     * A category indicating that the associated provider is only intended for use within the app
+     * that hosts the provider.
+     *
+     * <p>Declaring this category helps the system save resources by avoiding the launch of services
+     * whose routes are known to be private to the app that provides them.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_SELF_SCAN_ONLY =
+            "android.media.MediaRoute2ProviderService.SELF_SCAN_ONLY";
+
+    /**
      * The request ID to pass {@link #notifySessionCreated(long, RoutingSessionInfo)}
      * when {@link MediaRoute2ProviderService} created a session although there was no creation
      * request.
diff --git a/media/java/android/media/RouteListingPreference.java b/media/java/android/media/RouteListingPreference.java
index ee1f203..3935de8 100644
--- a/media/java/android/media/RouteListingPreference.java
+++ b/media/java/android/media/RouteListingPreference.java
@@ -440,14 +440,14 @@
          * <p>If this method returns {@link #SUBTEXT_CUSTOM}, then the subtext is obtained form
          * {@link #getCustomSubtextMessage()}.
          *
-         * @see #SUBTEXT_NONE,
-         * @see #SUBTEXT_ERROR_UNKNOWN,
-         * @see #SUBTEXT_SUBSCRIPTION_REQUIRED,
-         * @see #SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED,
-         * @see #SUBTEXT_AD_ROUTING_DISALLOWED,
-         * @see #SUBTEXT_DEVICE_LOW_POWER,
-         * @see #SUBTEXT_UNAUTHORIZED ,
-         * @see #SUBTEXT_TRACK_UNSUPPORTED,
+         * @see #SUBTEXT_NONE
+         * @see #SUBTEXT_ERROR_UNKNOWN
+         * @see #SUBTEXT_SUBSCRIPTION_REQUIRED
+         * @see #SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED
+         * @see #SUBTEXT_AD_ROUTING_DISALLOWED
+         * @see #SUBTEXT_DEVICE_LOW_POWER
+         * @see #SUBTEXT_UNAUTHORIZED
+         * @see #SUBTEXT_TRACK_UNSUPPORTED
          * @see #SUBTEXT_CUSTOM
          */
         @SubText
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 85b02ad..720d9a6 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -63,6 +63,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -1009,6 +1010,13 @@
          * @param buffer the {@link AdBuffer} that was consumed.
          */
         public void notifyAdBufferConsumed(@NonNull AdBuffer buffer) {
+            AdBuffer dupBuffer;
+            try {
+                dupBuffer = AdBuffer.dupAdBuffer(buffer);
+            } catch (IOException e) {
+                Log.w(TAG, "dup AdBuffer error in notifyAdBufferConsumed:", e);
+                return;
+            }
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -1016,10 +1024,14 @@
                     try {
                         if (DEBUG) Log.d(TAG, "notifyAdBufferConsumed");
                         if (mSessionCallback != null) {
-                            mSessionCallback.onAdBufferConsumed(buffer);
+                            mSessionCallback.onAdBufferConsumed(dupBuffer);
                         }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyAdBufferConsumed", e);
+                    } finally {
+                        if (dupBuffer != null) {
+                            dupBuffer.getSharedMemory().close();
+                        }
                     }
                 }
             });
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index ec85cc7..2419404 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -1964,6 +1964,13 @@
          */
         @CallSuper
         public void notifyAdBufferReady(@NonNull AdBuffer buffer) {
+            AdBuffer dupBuffer;
+            try {
+                dupBuffer = AdBuffer.dupAdBuffer(buffer);
+            } catch (IOException e) {
+                Log.w(TAG, "dup AdBuffer error in notifyAdBufferReady:", e);
+                return;
+            }
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -1974,10 +1981,14 @@
                                     "notifyAdBufferReady(buffer=" + buffer + ")");
                         }
                         if (mSessionCallback != null) {
-                            mSessionCallback.onAdBufferReady(AdBuffer.dupAdBuffer(buffer));
+                            mSessionCallback.onAdBufferReady(dupBuffer);
                         }
-                    } catch (RemoteException | IOException e) {
+                    } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyAdBuffer", e);
+                    } finally {
+                        if (dupBuffer != null) {
+                            dupBuffer.getSharedMemory().close();
+                        }
                     }
                 }
             });
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index ca1bb3e..da2e56f 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -768,6 +768,7 @@
             android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, buffer);
     if (graphicBuffer.get() == NULL) {
         jniThrowRuntimeException(env, "Invalid graphic buffer!");
+        return;
     }
 
     status_t res = graphicBuffer->unlock();
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
index 5eaa495..460bf99 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
@@ -21,6 +21,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.Hyphens
 import androidx.compose.ui.unit.em
 import androidx.compose.ui.unit.sp
 
@@ -34,42 +35,48 @@
             fontWeight = FontWeight.Normal,
             fontSize = 57.sp,
             lineHeight = 64.sp,
-            letterSpacing = (-0.2).sp
+            letterSpacing = (-0.2).sp,
+            hyphens = Hyphens.Auto,
         ),
         displayMedium = TextStyle(
             fontFamily = brand,
             fontWeight = FontWeight.Normal,
             fontSize = 45.sp,
             lineHeight = 52.sp,
-            letterSpacing = 0.0.sp
+            letterSpacing = 0.0.sp,
+            hyphens = Hyphens.Auto,
         ),
         displaySmall = TextStyle(
             fontFamily = brand,
             fontWeight = FontWeight.Normal,
             fontSize = 36.sp,
             lineHeight = 44.sp,
-            letterSpacing = 0.0.sp
+            letterSpacing = 0.0.sp,
+            hyphens = Hyphens.Auto,
         ),
         headlineLarge = TextStyle(
             fontFamily = brand,
             fontWeight = FontWeight.Normal,
             fontSize = 32.sp,
             lineHeight = 40.sp,
-            letterSpacing = 0.0.sp
+            letterSpacing = 0.0.sp,
+            hyphens = Hyphens.Auto,
         ),
         headlineMedium = TextStyle(
             fontFamily = brand,
             fontWeight = FontWeight.Normal,
             fontSize = 28.sp,
             lineHeight = 36.sp,
-            letterSpacing = 0.0.sp
+            letterSpacing = 0.0.sp,
+            hyphens = Hyphens.Auto,
         ),
         headlineSmall = TextStyle(
             fontFamily = brand,
             fontWeight = FontWeight.Normal,
             fontSize = 24.sp,
             lineHeight = 32.sp,
-            letterSpacing = 0.0.sp
+            letterSpacing = 0.0.sp,
+            hyphens = Hyphens.Auto,
         ),
         titleLarge = TextStyle(
             fontFamily = brand,
@@ -77,6 +84,7 @@
             fontSize = 22.sp,
             lineHeight = 28.sp,
             letterSpacing = 0.02.em,
+            hyphens = Hyphens.Auto,
         ),
         titleMedium = TextStyle(
             fontFamily = brand,
@@ -84,6 +92,7 @@
             fontSize = 20.sp,
             lineHeight = 24.sp,
             letterSpacing = 0.02.em,
+            hyphens = Hyphens.Auto,
         ),
         titleSmall = TextStyle(
             fontFamily = brand,
@@ -91,6 +100,7 @@
             fontSize = 18.sp,
             lineHeight = 20.sp,
             letterSpacing = 0.02.em,
+            hyphens = Hyphens.Auto,
         ),
         bodyLarge = TextStyle(
             fontFamily = plain,
@@ -98,6 +108,7 @@
             fontSize = 16.sp,
             lineHeight = 24.sp,
             letterSpacing = 0.01.em,
+            hyphens = Hyphens.Auto,
         ),
         bodyMedium = TextStyle(
             fontFamily = plain,
@@ -105,6 +116,7 @@
             fontSize = 14.sp,
             lineHeight = 20.sp,
             letterSpacing = 0.01.em,
+            hyphens = Hyphens.Auto,
         ),
         bodySmall = TextStyle(
             fontFamily = plain,
@@ -112,6 +124,7 @@
             fontSize = 12.sp,
             lineHeight = 16.sp,
             letterSpacing = 0.01.em,
+            hyphens = Hyphens.Auto,
         ),
         labelLarge = TextStyle(
             fontFamily = plain,
@@ -119,6 +132,7 @@
             fontSize = 16.sp,
             lineHeight = 24.sp,
             letterSpacing = 0.01.em,
+            hyphens = Hyphens.Auto,
         ),
         labelMedium = TextStyle(
             fontFamily = plain,
@@ -126,6 +140,7 @@
             fontSize = 14.sp,
             lineHeight = 20.sp,
             letterSpacing = 0.01.em,
+            hyphens = Hyphens.Auto,
         ),
         labelSmall = TextStyle(
             fontFamily = plain,
@@ -133,6 +148,7 @@
             fontSize = 12.sp,
             lineHeight = 16.sp,
             letterSpacing = 0.01.em,
+            hyphens = Hyphens.Auto,
         ),
     )
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index 5f2344e..01ba8f8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
@@ -29,6 +30,7 @@
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.theme.toMediumWeight
 
@@ -78,7 +80,7 @@
 @Composable
 fun PlaceholderTitle(title: String) {
     Box(
-        modifier = Modifier.fillMaxSize(),
+        modifier = Modifier.fillMaxSize().padding(SettingsDimension.itemPadding),
         contentAlignment = Alignment.Center,
     ) {
         Text(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index e3ea2e7..a0ff216 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -76,7 +76,7 @@
     private fun AppVersion() {
         if (packageInfo.versionName == null) return
         Spacer(modifier = Modifier.height(4.dp))
-        SettingsBody(packageInfo.versionName)
+        SettingsBody(packageInfo.versionNameBidiWrapped)
     }
 
     @Composable
@@ -84,10 +84,15 @@
         if (packageInfo.versionName == null) return
         Divider()
         Box(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
-            val versionName = BidiFormatter.getInstance().unicodeWrap(packageInfo.versionName)
-            SettingsBody(stringResource(R.string.version_text, versionName))
+            SettingsBody(stringResource(R.string.version_text, packageInfo.versionNameBidiWrapped))
         }
     }
+
+    private companion object {
+        /** Wrapped the version name, so its directionality still keep same when RTL. */
+        val PackageInfo.versionNameBidiWrapped: String
+            get() = BidiFormatter.getInstance().unicodeWrap(versionName)
+    }
 }
 
 @Composable
diff --git a/packages/SettingsLib/TEST_MAPPING b/packages/SettingsLib/TEST_MAPPING
new file mode 100644
index 0000000..f6ada4c1a
--- /dev/null
+++ b/packages/SettingsLib/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+  "presubmit": [
+    {
+      "name": "SettingsLibUnitTests"
+    },
+    {
+      "name": "SpaPrivilegedLibTests"
+    },
+    {
+      "name": "SettingsSpaUnitTests"
+    }
+  ]
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f581091..dac7f8d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1327,6 +1327,12 @@
     <string name="media_transfer_this_device_name" product="default">This phone</string>
     <!-- Name of the tablet device. [CHAR LIMIT=30] -->
     <string name="media_transfer_this_device_name" product="tablet">This tablet</string>
+    <!-- Name of the dock device. [CHAR LIMIT=30] -->
+    <string name="media_transfer_dock_speaker_device_name">Dock speaker</string>
+    <!-- Default name of the external device. [CHAR LIMIT=30] -->
+    <string name="media_transfer_external_device_name">External Device</string>
+    <!-- Default name of the connected device. [CHAR LIMIT=30] -->
+    <string name="media_transfer_default_device_name">Connected device</string>
     <!-- Name of the phone device with an active remote session. [CHAR LIMIT=30] -->
     <string name="media_transfer_this_phone">This phone</string>
     <!-- Sub status indicates device is not available due to an unknown error. [CHAR LIMIT=NONE] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
index a80061e..2bca7cf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
@@ -43,39 +43,5 @@
     /**
      * Bluetooth scheme.
      */
-    public static final String SCHEME_BT_BROADCAST_METADATA = "BT:";
-
-    // BluetoothLeBroadcastMetadata
-    static final String PREFIX_BT_ADDRESS_TYPE = "T:";
-    static final String PREFIX_BT_DEVICE = "D:";
-    static final String PREFIX_BT_ADVERTISING_SID = "AS:";
-    static final String PREFIX_BT_BROADCAST_ID = "B:";
-    static final String PREFIX_BT_SYNC_INTERVAL = "SI:";
-    static final String PREFIX_BT_IS_ENCRYPTED = "E:";
-    static final String PREFIX_BT_BROADCAST_CODE = "C:";
-    static final String PREFIX_BT_PRESENTATION_DELAY = "PD:";
-    static final String PREFIX_BT_SUBGROUPS = "SG:";
-    static final String PREFIX_BT_ANDROID_VERSION = "V:";
-
-    // BluetoothLeBroadcastSubgroup
-    static final String PREFIX_BTSG_CODEC_ID = "CID:";
-    static final String PREFIX_BTSG_CODEC_CONFIG = "CC:";
-    static final String PREFIX_BTSG_AUDIO_CONTENT = "AC:";
-    static final String PREFIX_BTSG_CHANNEL_PREF = "CP:";
-    static final String PREFIX_BTSG_BROADCAST_CHANNEL = "BC:";
-
-    // BluetoothLeAudioCodecConfigMetadata
-    static final String PREFIX_BTCC_AUDIO_LOCATION = "AL:";
-    static final String PREFIX_BTCC_RAW_METADATA = "CCRM:";
-
-    // BluetoothLeAudioContentMetadata
-    static final String PREFIX_BTAC_PROGRAM_INFO = "PI:";
-    static final String PREFIX_BTAC_LANGUAGE = "L:";
-    static final String PREFIX_BTAC_RAW_METADATA = "ACRM:";
-
-    // BluetoothLeBroadcastChannel
-    static final String PREFIX_BTBC_CHANNEL_INDEX = "CI:";
-    static final String PREFIX_BTBC_CODEC_CONFIG = "BCCM:";
-
-    static final String DELIMITER_QR_CODE = ";";
+    public static final String SCHEME_BT_BROADCAST_METADATA = "BT:BluetoothLeBroadcastMetadata:";
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
new file mode 100644
index 0000000..b54b115
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 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.settingslib.bluetooth
+
+import android.bluetooth.BluetoothLeBroadcastMetadata
+import android.os.Parcel
+import android.os.Parcelable
+import android.util.Base64
+import android.util.Log
+import com.android.settingslib.bluetooth.BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA
+
+object BluetoothLeBroadcastMetadataExt {
+    private const val TAG = "BluetoothLeBroadcastMetadataExt"
+
+    /**
+     * Converts [BluetoothLeBroadcastMetadata] to QR code string.
+     *
+     * QR code string will prefix with "BT:BluetoothLeBroadcastMetadata:".
+     */
+    fun BluetoothLeBroadcastMetadata.toQrCodeString(): String =
+        SCHEME_BT_BROADCAST_METADATA + Base64.encodeToString(toBytes(this), Base64.NO_WRAP)
+
+    /**
+     * Converts QR code string to [BluetoothLeBroadcastMetadata].
+     *
+     * QR code string should prefix with "BT:BluetoothLeBroadcastMetadata:".
+     */
+    fun convertToBroadcastMetadata(qrCodeString: String): BluetoothLeBroadcastMetadata? {
+        if (!qrCodeString.startsWith(SCHEME_BT_BROADCAST_METADATA)) return null
+        return try {
+            val encodedString = qrCodeString.removePrefix(SCHEME_BT_BROADCAST_METADATA)
+            val bytes = Base64.decode(encodedString, Base64.NO_WRAP)
+            createFromBytes(BluetoothLeBroadcastMetadata.CREATOR, bytes)
+        } catch (e: Exception) {
+            Log.w(TAG, "Cannot convert QR code string to BluetoothLeBroadcastMetadata", e)
+            null
+        }
+    }
+
+    private fun toBytes(parcelable: Parcelable): ByteArray =
+        Parcel.obtain().run {
+            parcelable.writeToParcel(this, 0)
+            setDataPosition(0)
+            val bytes = marshall()
+            recycle()
+            bytes
+        }
+
+    private fun <T> createFromBytes(creator: Parcelable.Creator<T>, bytes: ByteArray): T =
+        Parcel.obtain().run {
+            unmarshall(bytes, 0, bytes.size)
+            setDataPosition(0)
+            val created = creator.createFromParcel(this)
+            recycle()
+            created
+        }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.java
deleted file mode 100644
index 0630a2e..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.java
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.bluetooth;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
-import android.bluetooth.BluetoothLeAudioContentMetadata;
-import android.bluetooth.BluetoothLeBroadcastChannel;
-import android.bluetooth.BluetoothLeBroadcastMetadata;
-import android.bluetooth.BluetoothLeBroadcastSubgroup;
-import android.util.Log;
-
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class LocalBluetoothLeBroadcastMetadata {
-    private static final boolean DEBUG = BluetoothUtils.D;
-    private static final String TAG = "LocalBluetoothLeBroadcastMetadata";
-    private static final String METADATA_START = "<";
-    private static final String METADATA_END = ">";
-    private static final String PATTERN_REGEX = "<(.*?)>";
-    private static final String PATTERN_BT_BROADCAST_METADATA =
-            "T:<(.*?)>;+D:<(.*?)>;+AS:<(.*?)>;+B:<(.*?)>;+SI:<(.*?)>;+E:<(.*?)>;+C:<(.*?)>;"
-                + "+PD:<(.*?)>;+SG:(.*)";
-    private static final String PATTERN_BT_SUBGROUP =
-            "CID:<(.*?)>;+CC:<(.*?);>;+AC:<(.*?);>;+CP:<(.*?)>;+BC:<(.*)>;>;";
-    private static final String PATTERN_BT_CHANNEL = "CI:<(.*?)>;+BCCM:<(.*?);>;";
-
-    /* Index for BluetoothLeBroadcastMetadata */
-    private static int MATCH_INDEX_ADDRESS_TYPE = 1;
-    private static int MATCH_INDEX_DEVICE = 2;
-    private static int MATCH_INDEX_ADVERTISING_SID = 3;
-    private static int MATCH_INDEX_BROADCAST_ID = 4;
-    private static int MATCH_INDEX_SYNC_INTERVAL = 5;
-    private static int MATCH_INDEX_IS_ENCRYPTED = 6;
-    private static int MATCH_INDEX_BROADCAST_CODE = 7;
-    private static int MATCH_INDEX_PRESENTATION_DELAY = 8;
-    private static int MATCH_INDEX_SUBGROUPS = 9;
-
-    /* Index for BluetoothLeBroadcastSubgroup */
-    private static int MATCH_INDEX_CODEC_ID = 1;
-    private static int MATCH_INDEX_CODEC_CONFIG = 2;
-    private static int MATCH_INDEX_AUDIO_CONTENT = 3;
-    private static int MATCH_INDEX_CHANNEL_PREF = 4;
-    private static int MATCH_INDEX_BROADCAST_CHANNEL = 5;
-
-    /* Index for BluetoothLeAudioCodecConfigMetadata */
-    private static int LIST_INDEX_AUDIO_LOCATION = 0;
-    private static int LIST_INDEX_CODEC_CONFIG_RAW_METADATA = 1;
-
-    /* Index for BluetoothLeAudioContentMetadata */
-    private static int LIST_INDEX_PROGRAM_INFO = 0;
-    private static int LIST_INDEX_LANGUAGE = 1;
-    private static int LIST_INDEX_AUDIO_CONTENT_RAW_METADATA = 2;
-
-    /* Index for BluetoothLeBroadcastChannel */
-    private static int MATCH_INDEX_CHANNEL_INDEX = 1;
-    private static int MATCH_INDEX_CHANNEL_CODEC_CONFIG = 2;
-
-    private BluetoothLeBroadcastSubgroup mSubgroup;
-    private List<BluetoothLeBroadcastSubgroup> mSubgroupList;
-
-    // BluetoothLeBroadcastMetadata
-    // Optional: Identity address type
-    private int mSourceAddressType;
-    // Optional: Must use identity address
-    private BluetoothDevice mSourceDevice;
-    private int mSourceAdvertisingSid;
-    private int mBroadcastId;
-    private int mPaSyncInterval;
-    private int mPresentationDelayMicros;
-    private boolean mIsEncrypted;
-    private byte[] mBroadcastCode;
-
-    // BluetoothLeBroadcastSubgroup
-    private int mCodecId;
-    private BluetoothLeAudioContentMetadata mContentMetadata;
-    private BluetoothLeAudioCodecConfigMetadata mConfigMetadata;
-    private Boolean mNoChannelPreference;
-    private List<BluetoothLeBroadcastChannel> mChannel;
-
-    // BluetoothLeAudioCodecConfigMetadata
-    private long mAudioLocation;
-    private byte[] mCodecConfigMetadata;
-
-    // BluetoothLeAudioContentMetadata
-    private String mLanguage;
-    private String mProgramInfo;
-    private byte[] mAudioContentMetadata;
-
-    // BluetoothLeBroadcastChannel
-    private boolean mIsSelected;
-    private int mChannelIndex;
-
-
-    LocalBluetoothLeBroadcastMetadata(BluetoothLeBroadcastMetadata metadata) {
-        mSourceAddressType = metadata.getSourceAddressType();
-        mSourceDevice = metadata.getSourceDevice();
-        mSourceAdvertisingSid = metadata.getSourceAdvertisingSid();
-        mBroadcastId = metadata.getBroadcastId();
-        mPaSyncInterval = metadata.getPaSyncInterval();
-        mIsEncrypted = metadata.isEncrypted();
-        mBroadcastCode = metadata.getBroadcastCode();
-        mPresentationDelayMicros = metadata.getPresentationDelayMicros();
-        mSubgroupList = metadata.getSubgroups();
-    }
-
-    public LocalBluetoothLeBroadcastMetadata() {
-    }
-
-    public void setBroadcastCode(byte[] code) {
-        mBroadcastCode = code;
-    }
-
-    public int getBroadcastId() {
-        return mBroadcastId;
-    }
-
-    public String convertToQrCodeString() {
-        String subgroupString = convertSubgroupToString(mSubgroupList);
-        return new StringBuilder()
-                .append(BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_ADDRESS_TYPE)
-                .append(METADATA_START).append(mSourceAddressType).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_DEVICE)
-                .append(METADATA_START).append(mSourceDevice).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_ADVERTISING_SID)
-                .append(METADATA_START).append(mSourceAdvertisingSid).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_BROADCAST_ID)
-                .append(METADATA_START).append(mBroadcastId).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_SYNC_INTERVAL)
-                .append(METADATA_START).append(mPaSyncInterval).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_IS_ENCRYPTED)
-                .append(METADATA_START).append(mIsEncrypted).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_BROADCAST_CODE)
-                .append(METADATA_START).append(Arrays.toString(mBroadcastCode)).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_PRESENTATION_DELAY)
-                .append(METADATA_START).append(mPresentationDelayMicros).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BT_SUBGROUPS)
-                .append(METADATA_START).append(subgroupString).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .toString();
-    }
-
-    private String convertSubgroupToString(List<BluetoothLeBroadcastSubgroup> subgroupList) {
-        StringBuilder subgroupListBuilder = new StringBuilder();
-        String subgroupString = "";
-        for (BluetoothLeBroadcastSubgroup subgroup: subgroupList) {
-            String audioCodec = convertAudioCodecConfigToString(subgroup.getCodecSpecificConfig());
-            String audioContent = convertAudioContentToString(subgroup.getContentMetadata());
-            boolean hasChannelPreference = subgroup.hasChannelPreference();
-            String channels = convertChannelToString(subgroup.getChannels());
-            subgroupString = new StringBuilder()
-                    .append(BluetoothBroadcastUtils.PREFIX_BTSG_CODEC_ID)
-                    .append(METADATA_START).append(subgroup.getCodecId()).append(METADATA_END)
-                    .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                    .append(BluetoothBroadcastUtils.PREFIX_BTSG_CODEC_CONFIG)
-                    .append(METADATA_START).append(audioCodec).append(METADATA_END)
-                    .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                    .append(BluetoothBroadcastUtils.PREFIX_BTSG_AUDIO_CONTENT)
-                    .append(METADATA_START).append(audioContent).append(METADATA_END)
-                    .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                    .append(BluetoothBroadcastUtils.PREFIX_BTSG_CHANNEL_PREF)
-                    .append(METADATA_START).append(hasChannelPreference).append(METADATA_END)
-                    .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                    .append(BluetoothBroadcastUtils.PREFIX_BTSG_BROADCAST_CHANNEL)
-                    .append(METADATA_START).append(channels).append(METADATA_END)
-                    .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                    .toString();
-            subgroupListBuilder.append(subgroupString);
-        }
-        return subgroupListBuilder.toString();
-    }
-
-    private String convertAudioCodecConfigToString(BluetoothLeAudioCodecConfigMetadata config) {
-        String audioLocation = String.valueOf(config.getAudioLocation());
-        String rawMetadata = new String(config.getRawMetadata(), StandardCharsets.UTF_8);
-        return new StringBuilder()
-            .append(BluetoothBroadcastUtils.PREFIX_BTCC_AUDIO_LOCATION)
-            .append(METADATA_START).append(audioLocation).append(METADATA_END)
-            .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-            .append(BluetoothBroadcastUtils.PREFIX_BTCC_RAW_METADATA)
-            .append(METADATA_START).append(rawMetadata).append(METADATA_END)
-            .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-            .toString();
-    }
-
-    private String convertAudioContentToString(BluetoothLeAudioContentMetadata audioContent) {
-        String rawMetadata = new String(audioContent.getRawMetadata(), StandardCharsets.UTF_8);
-        return new StringBuilder()
-            .append(BluetoothBroadcastUtils.PREFIX_BTAC_PROGRAM_INFO)
-            .append(METADATA_START).append(audioContent.getProgramInfo()).append(METADATA_END)
-            .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-            .append(BluetoothBroadcastUtils.PREFIX_BTAC_LANGUAGE)
-            .append(METADATA_START).append(audioContent.getLanguage()).append(METADATA_END)
-            .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-            .append(BluetoothBroadcastUtils.PREFIX_BTAC_RAW_METADATA)
-            .append(METADATA_START).append(rawMetadata).append(METADATA_END)
-            .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-            .toString();
-    }
-
-    private String convertChannelToString(List<BluetoothLeBroadcastChannel> channelList) {
-        StringBuilder channelListBuilder = new StringBuilder();
-        String channelString = "";
-        for (BluetoothLeBroadcastChannel channel: channelList) {
-            String channelAudioCodec = convertAudioCodecConfigToString(channel.getCodecMetadata());
-            channelString = new StringBuilder()
-                .append(BluetoothBroadcastUtils.PREFIX_BTBC_CHANNEL_INDEX)
-                .append(METADATA_START).append(channel.getChannelIndex()).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .append(BluetoothBroadcastUtils.PREFIX_BTBC_CODEC_CONFIG)
-                .append(METADATA_START).append(channelAudioCodec).append(METADATA_END)
-                .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
-                .toString();
-            channelListBuilder.append(channelString);
-        }
-        return channelListBuilder.toString();
-    }
-
-    /**
-     * Example : prefix is with the “BT:”, and end by the Android Version.
-     * BT:T:<1>;D:<00:11:22:AA:BB:CC>;AS:<1>;B:…;V:T;;
-     *
-     * @return BluetoothLeBroadcastMetadata
-     */
-    public BluetoothLeBroadcastMetadata convertToBroadcastMetadata(String qrCodeString) {
-        if (DEBUG) {
-            Log.d(TAG, "Convert " + qrCodeString + "to BluetoothLeBroadcastMetadata");
-        }
-
-        Pattern pattern = Pattern.compile(PATTERN_BT_BROADCAST_METADATA);
-        Matcher match = pattern.matcher(qrCodeString);
-        if (match.find()) {
-            try {
-                mSourceAddressType = Integer.parseInt(match.group(MATCH_INDEX_ADDRESS_TYPE));
-                mSourceDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
-                    match.group(MATCH_INDEX_DEVICE));
-                mSourceAdvertisingSid = Integer.parseInt(
-                    match.group(MATCH_INDEX_ADVERTISING_SID));
-                mBroadcastId = Integer.parseInt(match.group(MATCH_INDEX_BROADCAST_ID));
-                mPaSyncInterval = Integer.parseInt(match.group(MATCH_INDEX_SYNC_INTERVAL));
-                mIsEncrypted = Boolean.valueOf(match.group(MATCH_INDEX_IS_ENCRYPTED));
-                mBroadcastCode = match.group(MATCH_INDEX_BROADCAST_CODE).getBytes();
-                mPresentationDelayMicros =
-                    Integer.parseInt(match.group(MATCH_INDEX_PRESENTATION_DELAY));
-
-                if (DEBUG) {
-                    Log.d(TAG, "Converted qrCodeString result: "
-                        + " ,Type = " + mSourceAddressType
-                        + " ,Device = " + mSourceDevice
-                        + " ,AdSid = " + mSourceAdvertisingSid
-                        + " ,BroadcastId = " + mBroadcastId
-                        + " ,paSync = " + mPaSyncInterval
-                        + " ,encrypted = " + mIsEncrypted
-                        + " ,BroadcastCode = " + Arrays.toString(mBroadcastCode)
-                        + " ,delay = " + mPresentationDelayMicros);
-                }
-
-                mSubgroup = convertToSubgroup(match.group(MATCH_INDEX_SUBGROUPS));
-
-                return new BluetoothLeBroadcastMetadata.Builder()
-                    .setSourceDevice(mSourceDevice, mSourceAddressType)
-                    .setSourceAdvertisingSid(mSourceAdvertisingSid)
-                    .setBroadcastId(mBroadcastId)
-                    .setPaSyncInterval(mPaSyncInterval)
-                    .setEncrypted(mIsEncrypted)
-                    .setBroadcastCode(mBroadcastCode)
-                    .setPresentationDelayMicros(mPresentationDelayMicros)
-                    .addSubgroup(mSubgroup)
-                    .build();
-            } catch (IllegalArgumentException e) {
-                Log.d(TAG, "IllegalArgumentException when convert : " + e);
-                return null;
-            }
-        } else {
-            if (DEBUG) {
-                Log.d(TAG, "The match fail, can not convert it to BluetoothLeBroadcastMetadata.");
-            }
-            return null;
-        }
-    }
-
-    private BluetoothLeBroadcastSubgroup convertToSubgroup(String subgroupString) {
-        if (DEBUG) {
-            Log.d(TAG, "Convert " + subgroupString + "to BluetoothLeBroadcastSubgroup");
-        }
-        Pattern pattern = Pattern.compile(PATTERN_BT_SUBGROUP);
-        Matcher match = pattern.matcher(subgroupString);
-        if (match.find()) {
-            mCodecId = Integer.parseInt(match.group(MATCH_INDEX_CODEC_ID));
-            mConfigMetadata = convertToConfigMetadata(match.group(MATCH_INDEX_CODEC_CONFIG));
-            mContentMetadata = convertToContentMetadata(match.group(MATCH_INDEX_AUDIO_CONTENT));
-            mNoChannelPreference = Boolean.valueOf(match.group(MATCH_INDEX_CHANNEL_PREF));
-            mChannel =
-                  convertToChannel(match.group(MATCH_INDEX_BROADCAST_CHANNEL), mConfigMetadata);
-
-            BluetoothLeBroadcastSubgroup.Builder subgroupBuilder =
-                    new BluetoothLeBroadcastSubgroup.Builder();
-            subgroupBuilder.setCodecId(mCodecId);
-            subgroupBuilder.setCodecSpecificConfig(mConfigMetadata);
-            subgroupBuilder.setContentMetadata(mContentMetadata);
-
-            for (BluetoothLeBroadcastChannel channel : mChannel) {
-                subgroupBuilder.addChannel(channel);
-            }
-            return subgroupBuilder.build();
-        } else {
-            if (DEBUG) {
-                Log.d(TAG,
-                        "The match fail, can not convert it to BluetoothLeBroadcastSubgroup.");
-            }
-            return null;
-        }
-    }
-
-    private BluetoothLeAudioCodecConfigMetadata convertToConfigMetadata(
-            String configMetadataString) {
-        if (DEBUG) {
-            Log.d(TAG,
-                    "Convert " + configMetadataString + "to BluetoothLeAudioCodecConfigMetadata");
-        }
-        Pattern pattern = Pattern.compile(PATTERN_REGEX);
-        Matcher match = pattern.matcher(configMetadataString);
-        ArrayList<String> resultList = new ArrayList<>();
-        while (match.find()) {
-            resultList.add(match.group(1));
-            Log.d(TAG, "Codec Config match : " + match.group(1));
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Converted configMetadataString result: " + resultList.size());
-        }
-        if (resultList.size() > 0) {
-            mAudioLocation = Long.parseLong(resultList.get(LIST_INDEX_AUDIO_LOCATION));
-            mCodecConfigMetadata = resultList.get(LIST_INDEX_CODEC_CONFIG_RAW_METADATA).getBytes();
-            return new BluetoothLeAudioCodecConfigMetadata.Builder()
-                    .setAudioLocation(mAudioLocation)
-                    .build();
-        } else {
-            if (DEBUG) {
-                Log.d(TAG,
-                        "The match fail, can not convert it to "
-                                + "BluetoothLeAudioCodecConfigMetadata.");
-            }
-            return null;
-        }
-    }
-
-    private BluetoothLeAudioContentMetadata convertToContentMetadata(String contentMetadataString) {
-        if (DEBUG) {
-            Log.d(TAG, "Convert " + contentMetadataString + "to BluetoothLeAudioContentMetadata");
-        }
-        Pattern pattern = Pattern.compile(PATTERN_REGEX);
-        Matcher match = pattern.matcher(contentMetadataString);
-        ArrayList<String> resultList = new ArrayList<>();
-        while (match.find()) {
-            Log.d(TAG, "Audio Content match : " + match.group(1));
-            resultList.add(match.group(1));
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Converted contentMetadataString result: " + resultList.size());
-        }
-        if (resultList.size() > 0) {
-            mProgramInfo = resultList.get(LIST_INDEX_PROGRAM_INFO);
-            mLanguage = resultList.get(LIST_INDEX_LANGUAGE);
-            mAudioContentMetadata =
-                  resultList.get(LIST_INDEX_AUDIO_CONTENT_RAW_METADATA).getBytes();
-
-            /* TODO(b/265253566) : Need to set the default value for language when the user starts
-            *  the broadcast.
-            */
-            if (mLanguage.equals("null")) {
-                mLanguage = "eng";
-            }
-
-            return new BluetoothLeAudioContentMetadata.Builder()
-                    .setProgramInfo(mProgramInfo)
-                    .setLanguage(mLanguage)
-                    .build();
-        } else {
-            if (DEBUG) {
-                Log.d(TAG,
-                        "The match fail, can not convert it to BluetoothLeAudioContentMetadata.");
-            }
-            return null;
-        }
-    }
-
-    private List<BluetoothLeBroadcastChannel> convertToChannel(String channelString,
-            BluetoothLeAudioCodecConfigMetadata configMetadata) {
-        if (DEBUG) {
-            Log.d(TAG, "Convert " + channelString + "to BluetoothLeBroadcastChannel");
-        }
-        Pattern pattern = Pattern.compile(PATTERN_BT_CHANNEL);
-        Matcher match = pattern.matcher(channelString);
-        Map<Integer, BluetoothLeAudioCodecConfigMetadata> channel =
-                new HashMap<Integer, BluetoothLeAudioCodecConfigMetadata>();
-        while (match.find()) {
-            channel.put(Integer.parseInt(match.group(MATCH_INDEX_CHANNEL_INDEX)),
-                    convertToConfigMetadata(match.group(MATCH_INDEX_CHANNEL_CODEC_CONFIG)));
-        }
-
-        if (channel.size() > 0) {
-            mIsSelected = false;
-            ArrayList<BluetoothLeBroadcastChannel> broadcastChannelList = new ArrayList<>();
-            for (Map.Entry<Integer, BluetoothLeAudioCodecConfigMetadata> entry :
-                    channel.entrySet()) {
-
-                broadcastChannelList.add(
-                        new BluetoothLeBroadcastChannel.Builder()
-                            .setSelected(mIsSelected)
-                            .setChannelIndex(entry.getKey())
-                            .setCodecMetadata(entry.getValue())
-                            .build());
-            }
-            return broadcastChannelList;
-        } else {
-            if (DEBUG) {
-                Log.d(TAG,
-                        "The match fail, can not convert it to BluetoothLeBroadcastChannel.");
-            }
-            return null;
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.kt
new file mode 100644
index 0000000..870ea8d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.bluetooth
+
+import android.bluetooth.BluetoothLeBroadcastMetadata
+import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt.toQrCodeString
+
+@Deprecated("Replace with BluetoothLeBroadcastMetadataExt")
+class LocalBluetoothLeBroadcastMetadata(private val metadata: BluetoothLeBroadcastMetadata?) {
+
+    constructor() : this(null)
+
+    fun convertToQrCodeString(): String = metadata?.toQrCodeString() ?: ""
+
+    fun convertToBroadcastMetadata(qrCodeString: String) =
+        BluetoothLeBroadcastMetadataExt.convertToBroadcastMetadata(qrCodeString)
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index 76556639..1251b0d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -196,6 +196,9 @@
 
     /** Gets the battery level from the intent. */
     public static int getBatteryLevel(Intent batteryChangedIntent) {
+        if (batteryChangedIntent == null) {
+            return -1; /*invalid battery level*/
+        }
         final int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
         final int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
         return scale == 0
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 1c82be9..34519c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -70,13 +70,17 @@
                 name = mContext.getString(R.string.media_transfer_wired_usb_device_name);
                 break;
             case TYPE_DOCK:
-            case TYPE_HDMI:
-                name = mRouteInfo.getName();
+                name = mContext.getString(R.string.media_transfer_dock_speaker_device_name);
                 break;
             case TYPE_BUILTIN_SPEAKER:
-            default:
                 name = mContext.getString(R.string.media_transfer_this_device_name);
                 break;
+            case TYPE_HDMI:
+                name = mContext.getString(R.string.media_transfer_external_device_name);
+                break;
+            default:
+                name = mContext.getString(R.string.media_transfer_default_device_name);
+                break;
         }
         return name.toString();
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
index e835125..e6b1cfb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/SubscriptionInfoDao.java
@@ -16,14 +16,13 @@
 
 package com.android.settingslib.mobile.dataservice;
 
-import java.util.List;
-
 import androidx.lifecycle.LiveData;
 import androidx.room.Dao;
 import androidx.room.Insert;
 import androidx.room.OnConflictStrategy;
 import androidx.room.Query;
-import androidx.room.Update;
+
+import java.util.List;
 
 @Dao
 public interface SubscriptionInfoDao {
@@ -32,7 +31,9 @@
     void insertSubsInfo(SubscriptionInfoEntity... subscriptionInfo);
 
     @Query("SELECT * FROM " + DataServiceUtils.SubscriptionInfoData.TABLE_NAME + " ORDER BY "
-            + DataServiceUtils.SubscriptionInfoData.COLUMN_ID)
+            + " CASE WHEN " +  DataServiceUtils.SubscriptionInfoData.COLUMN_SIM_SLOT_INDEX
+            + " >= 0 THEN 1 ELSE 2 END , "
+            + DataServiceUtils.SubscriptionInfoData.COLUMN_SIM_SLOT_INDEX)
     LiveData<List<SubscriptionInfoEntity>> queryAvailableSubInfos();
 
     @Query("SELECT * FROM " + DataServiceUtils.SubscriptionInfoData.TABLE_NAME + " WHERE "
@@ -43,7 +44,8 @@
             + DataServiceUtils.SubscriptionInfoData.COLUMN_IS_ACTIVE_SUBSCRIPTION_ID
             + " = :isActiveSubscription" + " AND "
             + DataServiceUtils.SubscriptionInfoData.COLUMN_IS_SUBSCRIPTION_VISIBLE
-            + " = :isSubscriptionVisible")
+            + " = :isSubscriptionVisible" + " ORDER BY "
+            + DataServiceUtils.SubscriptionInfoData.COLUMN_SIM_SLOT_INDEX)
     LiveData<List<SubscriptionInfoEntity>> queryActiveSubInfos(
             boolean isActiveSubscription, boolean isSubscriptionVisible);
 
diff --git a/packages/SettingsLib/tests/unit/Android.bp b/packages/SettingsLib/tests/unit/Android.bp
new file mode 100644
index 0000000..a4558f1
--- /dev/null
+++ b/packages/SettingsLib/tests/unit/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2023 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "SettingsLibUnitTests",
+    test_suites: ["device-tests"],
+
+    srcs: [
+        "src/**/*.kt",
+    ],
+
+    static_libs: [
+        "SettingsLib",
+        "androidx.test.ext.junit",
+        "androidx.test.runner",
+        "truth-prebuilt",
+    ],
+}
diff --git a/packages/SettingsLib/tests/unit/AndroidManifest.xml b/packages/SettingsLib/tests/unit/AndroidManifest.xml
new file mode 100644
index 0000000..568f9cb
--- /dev/null
+++ b/packages/SettingsLib/tests/unit/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.test">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Tests for SettingsLib"
+        android:targetPackage="com.android.settingslib.test">
+    </instrumentation>
+</manifest>
diff --git a/packages/SettingsLib/tests/unit/OWNERS b/packages/SettingsLib/tests/unit/OWNERS
new file mode 100644
index 0000000..66559252
--- /dev/null
+++ b/packages/SettingsLib/tests/unit/OWNERS
@@ -0,0 +1,2 @@
+# We do not guard tests - everyone is welcomed to contribute to tests.
+per-file *.kt=*
diff --git a/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt b/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
new file mode 100644
index 0000000..0e3590d
--- /dev/null
+++ b/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 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.settingslib.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothLeAudioCodecConfigMetadata
+import android.bluetooth.BluetoothLeAudioContentMetadata
+import android.bluetooth.BluetoothLeBroadcastChannel
+import android.bluetooth.BluetoothLeBroadcastMetadata
+import android.bluetooth.BluetoothLeBroadcastSubgroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt.toQrCodeString
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class BluetoothLeBroadcastMetadataExtTest {
+
+    @Test
+    fun toQrCodeString() {
+        val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
+            setCodecId(100)
+            val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
+            setCodecSpecificConfig(audioCodecConfigMetadata)
+            setContentMetadata(BluetoothLeAudioContentMetadata.Builder().build())
+            addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+                setChannelIndex(1000)
+                setCodecMetadata(audioCodecConfigMetadata)
+            }.build())
+        }.build()
+
+        val metadata = BluetoothLeBroadcastMetadata.Builder().apply {
+            setSourceDevice(Device, 0)
+            setSourceAdvertisingSid(1)
+            setBroadcastId(2)
+            setPaSyncInterval(3)
+            setEncrypted(true)
+            setBroadcastCode(byteArrayOf(10, 11, 12, 13))
+            setPresentationDelayMicros(4)
+            addSubgroup(subgroup)
+        }.build()
+
+        val qrCodeString = metadata.toQrCodeString()
+
+        assertThat(qrCodeString).isEqualTo(QR_CODE_STRING)
+    }
+
+    @Test
+    fun decodeAndEncodeAgain_sameString() {
+        val metadata = BluetoothLeBroadcastMetadataExt.convertToBroadcastMetadata(QR_CODE_STRING)!!
+
+        val qrCodeString = metadata.toQrCodeString()
+
+        assertThat(qrCodeString).isEqualTo(QR_CODE_STRING)
+    }
+
+    private companion object {
+        const val TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"
+
+        val Device: BluetoothDevice =
+            BluetoothAdapter.getDefaultAdapter().getRemoteDevice(TEST_DEVICE_ADDRESS)
+
+        const val QR_CODE_STRING =
+            "BT:BluetoothLeBroadcastMetadata:AAAAAAEAAAABAAAAEQAAADAAMAA6AEEAMQA6AEEAMQA6AEEAMQA6" +
+                "AEEAMQA6AEEAMQAAAAAAAAABAAAAAgAAAAMAAAABAAAABAAAAAQAAAAKCwwNBAAAAAEAAAABAAAAZAAA" +
+                "AAAAAAABAAAAAAAAAAAAAAAGAAAABgAAAAUDAAAAAAAAAAAAAAAAAAAAAAAAAQAAAP//////////AAAA" +
+                "AAAAAAABAAAAAQAAAAAAAADoAwAAAQAAAAAAAAAAAAAABgAAAAYAAAAFAwAAAAAAAAAAAAAAAAAAAAAA" +
+                "AAAAAAD/////AAAAAAAAAAA="
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
new file mode 100644
index 0000000..8fe1f48
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 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.compose.animation
+
+import androidx.compose.animation.core.Easing
+import androidx.core.animation.Interpolator
+import com.android.app.animation.InterpolatorsAndroidX
+
+/**
+ * Compose-compatible definition of Android motion eases, see
+ * https://carbon.googleplex.com/android-motion/pages/easing
+ */
+object Easings {
+
+    /** The standard interpolator that should be used on every normal animation */
+    val StandardEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD)
+
+    /**
+     * The standard accelerating interpolator that should be used on every regular movement of
+     * content that is disappearing e.g. when moving off screen.
+     */
+    val StandardAccelerateEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD_ACCELERATE)
+
+    /**
+     * The standard decelerating interpolator that should be used on every regular movement of
+     * content that is appearing e.g. when coming from off screen.
+     */
+    val StandardDecelerateEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD_DECELERATE)
+
+    /** The default emphasized interpolator. Used for hero / emphasized movement of content. */
+    val EmphasizedEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED)
+
+    /**
+     * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+     * is disappearing e.g. when moving off screen.
+     */
+    val EmphasizedAccelerateEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_ACCELERATE)
+
+    /**
+     * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+     * is appearing e.g. when coming from off screen
+     */
+    val EmphasizedDecelerateEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_DECELERATE)
+
+    /** The linear interpolator. */
+    val LinearEasing = fromInterpolator(InterpolatorsAndroidX.LINEAR)
+
+    private fun fromInterpolator(source: Interpolator) = Easing { x -> source.getInterpolation(x) }
+}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
index 954bad56..d364374 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.scene.ui.composable
 
+import android.content.Context
 import com.android.systemui.bouncer.ui.composable.BouncerScene
 import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
 import com.android.systemui.dagger.SysUISingleton
@@ -28,6 +29,7 @@
 import com.android.systemui.scene.shared.model.SceneContainerNames
 import com.android.systemui.shade.ui.composable.ShadeScene
 import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
+import com.android.systemui.statusbar.phone.SystemUIDialog
 import dagger.Module
 import dagger.Provides
 import javax.inject.Named
@@ -57,6 +59,7 @@
     @SysUISingleton
     @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
     fun bouncerScene(
+        @Application context: Context,
         viewModelFactory: BouncerViewModel.Factory,
     ): BouncerScene {
         return BouncerScene(
@@ -64,6 +67,7 @@
                 viewModelFactory.create(
                     containerName = SceneContainerNames.SYSTEM_UI_DEFAULT,
                 ),
+            dialogFactory = { SystemUIDialog(context) },
         )
     }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 3c74ef5..240bace 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -14,9 +14,16 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalMaterial3Api::class)
+
 package com.android.systemui.bouncer.ui.composable
 
+import android.app.AlertDialog
+import android.app.Dialog
+import android.content.DialogInterface
 import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -26,15 +33,20 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.Button
 import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import com.android.systemui.R
 import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -51,6 +63,7 @@
 /** The bouncer scene displays authentication challenges like PIN, password, or pattern. */
 class BouncerScene(
     private val viewModel: BouncerViewModel,
+    private val dialogFactory: () -> AlertDialog,
 ) : ComposableScene {
     override val key = SceneKey.Bouncer
 
@@ -68,16 +81,19 @@
     override fun Content(
         containerName: String,
         modifier: Modifier,
-    ) = BouncerScene(viewModel, modifier)
+    ) = BouncerScene(viewModel, dialogFactory, modifier)
 }
 
 @Composable
 private fun BouncerScene(
     viewModel: BouncerViewModel,
+    dialogFactory: () -> AlertDialog,
     modifier: Modifier = Modifier,
 ) {
-    val message: String by viewModel.message.collectAsState()
+    val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
     val authMethodViewModel: AuthMethodBouncerViewModel? by viewModel.authMethod.collectAsState()
+    val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
+    var dialog: Dialog? by remember { mutableStateOf(null) }
 
     Column(
         horizontalAlignment = Alignment.CenterHorizontally,
@@ -88,9 +104,10 @@
         Crossfade(
             targetState = message,
             label = "Bouncer message",
-        ) {
+            animationSpec = if (message.isUpdateAnimated) tween() else snap(),
+        ) { message ->
             Text(
-                text = it,
+                text = message.text,
                 color = MaterialTheme.colorScheme.onSurface,
                 style = MaterialTheme.typography.bodyLarge,
             )
@@ -132,5 +149,26 @@
                 style = MaterialTheme.typography.bodyMedium,
             )
         }
+
+        if (dialogMessage != null) {
+            if (dialog == null) {
+                dialog =
+                    dialogFactory().apply {
+                        setMessage(dialogMessage)
+                        setButton(
+                            DialogInterface.BUTTON_NEUTRAL,
+                            context.getString(R.string.ok),
+                        ) { _, _ ->
+                            viewModel.onThrottlingDialogDismissed()
+                        }
+                        setCancelable(false)
+                        setCanceledOnTouchOutside(false)
+                        show()
+                    }
+            }
+        } else {
+            dialog?.dismiss()
+            dialog = null
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 4e85621..7545ff4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -53,6 +53,8 @@
 ) {
     val focusRequester = remember { FocusRequester() }
     val password: String by viewModel.password.collectAsState()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
 
     LaunchedEffect(Unit) {
         // When the UI comes up, request focus on the TextField to bring up the software keyboard.
@@ -61,6 +63,13 @@
         viewModel.onShown()
     }
 
+    LaunchedEffect(animateFailure) {
+        if (animateFailure) {
+            // We don't currently have a failure animation for password, just consume it:
+            viewModel.onFailureAnimationShown()
+        }
+    }
+
     Column(
         horizontalAlignment = Alignment.CenterHorizontally,
         modifier = modifier,
@@ -71,6 +80,7 @@
         TextField(
             value = password,
             onValueChange = viewModel::onPasswordInputChanged,
+            enabled = isInputEnabled,
             visualTransformation = PasswordVisualTransformation(),
             singleLine = true,
             textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 3afd33f..8844114 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -18,6 +18,7 @@
 
 import android.view.HapticFeedbackConstants
 import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.gestures.detectDragGestures
@@ -41,12 +42,15 @@
 import androidx.compose.ui.res.integerResource
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Easings
 import com.android.internal.R
 import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PatternDotViewModel
+import com.android.systemui.compose.modifiers.thenIf
 import kotlin.math.min
 import kotlin.math.pow
 import kotlin.math.sqrt
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
 /**
@@ -66,9 +70,9 @@
     val rowCount = viewModel.rowCount
 
     val dotColor = MaterialTheme.colorScheme.secondary
-    val dotRadius = with(LocalDensity.current) { 8.dp.toPx() }
+    val dotRadius = with(LocalDensity.current) { (DOT_DIAMETER_DP / 2).dp.toPx() }
     val lineColor = MaterialTheme.colorScheme.primary
-    val lineStrokeWidth = dotRadius * 2 + with(LocalDensity.current) { 4.dp.toPx() }
+    val lineStrokeWidth = with(LocalDensity.current) { LINE_STROKE_WIDTH_DP.dp.toPx() }
 
     var containerSize: IntSize by remember { mutableStateOf(IntSize(0, 0)) }
     val horizontalSpacing = containerSize.width / colCount
@@ -82,6 +86,9 @@
     val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsState()
     // The dots selected so far, if the user is currently dragging.
     val selectedDots: List<PatternDotViewModel> by viewModel.selectedDots.collectAsState()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
+    val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsState()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
 
     // Map of animatables for the scale of each dot, keyed by dot.
     val dotScalingAnimatables = remember(dots) { dots.associateWith { Animatable(1f) } }
@@ -96,19 +103,46 @@
     val view = LocalView.current
 
     // When the current dot is changed, we need to update our animations.
-    LaunchedEffect(currentDot) {
-        view.performHapticFeedback(
-            HapticFeedbackConstants.VIRTUAL_KEY,
-            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
-        )
+    LaunchedEffect(currentDot, isAnimationEnabled) {
+        // Perform haptic feedback, but only if the current dot is not null, so we don't perform it
+        // when the UI first shows up or when the user lifts their pointer/finger.
+        if (currentDot != null) {
+            view.performHapticFeedback(
+                HapticFeedbackConstants.VIRTUAL_KEY,
+                HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
+            )
+        }
 
-        // Make sure that the current dot is scaled up while the other dots are scaled back down.
+        if (!isAnimationEnabled) {
+            return@LaunchedEffect
+        }
+
+        // Make sure that the current dot is scaled up while the other dots are scaled back
+        // down.
         dotScalingAnimatables.entries.forEach { (dot, animatable) ->
             val isSelected = dot == currentDot
-            launch {
-                animatable.animateTo(if (isSelected) 2f else 1f)
+            // Launch using the longer-lived scope because we want these animations to proceed to
+            // completion even if the LaunchedEffect is canceled because its key objects have
+            // changed.
+            scope.launch {
                 if (isSelected) {
-                    animatable.animateTo(1f)
+                    animatable.animateTo(
+                        targetValue = (SELECTED_DOT_DIAMETER_DP / DOT_DIAMETER_DP.toFloat()),
+                        animationSpec =
+                            tween(
+                                durationMillis = SELECTED_DOT_REACTION_ANIMATION_DURATION_MS,
+                                easing = Easings.StandardAccelerateEasing,
+                            ),
+                    )
+                } else {
+                    animatable.animateTo(
+                        targetValue = 1f,
+                        animationSpec =
+                            tween(
+                                durationMillis = SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS,
+                                easing = Easings.StandardDecelerateEasing,
+                            ),
+                    )
                 }
             }
         }
@@ -116,14 +150,18 @@
         selectedDots.forEach { dot ->
             lineFadeOutAnimatables[dot]?.let { line ->
                 if (!line.isRunning) {
+                    // Launch using the longer-lived scope because we want these animations to
+                    // proceed to completion even if the LaunchedEffect is canceled because its key
+                    // objects have changed.
                     scope.launch {
                         if (dot == currentDot) {
-                            // Reset the fade-out animation for the current dot. When the current
-                            // dot is switched, this entire code block runs again for the newly
-                            // selected dot.
+                            // Reset the fade-out animation for the current dot. When the
+                            // current dot is switched, this entire code block runs again for
+                            // the newly selected dot.
                             line.snapTo(1f)
                         } else {
-                            // For all non-current dots, make sure that the lines are fading out.
+                            // For all non-current dots, make sure that the lines are fading
+                            // out.
                             line.animateTo(
                                 targetValue = 0f,
                                 animationSpec =
@@ -139,6 +177,17 @@
         }
     }
 
+    // Show the failure animation if the user entered the wrong input.
+    LaunchedEffect(animateFailure) {
+        if (animateFailure) {
+            showFailureAnimation(
+                dots = dots,
+                scalingAnimatables = dotScalingAnimatables,
+            )
+            viewModel.onFailureAnimationShown()
+        }
+    }
+
     // This is the position of the input pointer.
     var inputPosition: Offset? by remember { mutableStateOf(null) }
 
@@ -148,27 +197,34 @@
             // when it leaves the bounds of the dot grid.
             .clipToBounds()
             .onSizeChanged { containerSize = it }
-            .pointerInput(Unit) {
-                detectDragGestures(
-                    onDragStart = { start ->
-                        inputPosition = start
-                        viewModel.onDragStart()
-                    },
-                    onDragEnd = {
-                        inputPosition = null
-                        lineFadeOutAnimatables.values.forEach { animatable ->
-                            scope.launch { animatable.animateTo(1f) }
-                        }
-                        viewModel.onDragEnd()
-                    },
-                ) { change, _ ->
-                    inputPosition = change.position
-                    viewModel.onDrag(
-                        xPx = change.position.x,
-                        yPx = change.position.y,
-                        containerSizePx = containerSize.width,
-                        verticalOffsetPx = verticalOffset,
-                    )
+            .thenIf(isInputEnabled) {
+                Modifier.pointerInput(Unit) {
+                    detectDragGestures(
+                        onDragStart = { start ->
+                            inputPosition = start
+                            viewModel.onDragStart()
+                        },
+                        onDragEnd = {
+                            inputPosition = null
+                            if (isAnimationEnabled) {
+                                lineFadeOutAnimatables.values.forEach { animatable ->
+                                    // Launch using the longer-lived scope because we want these
+                                    // animations to proceed to completion even if the surrounding
+                                    // scope is canceled.
+                                    scope.launch { animatable.animateTo(1f) }
+                                }
+                            }
+                            viewModel.onDragEnd()
+                        },
+                    ) { change, _ ->
+                        inputPosition = change.position
+                        viewModel.onDrag(
+                            xPx = change.position.x,
+                            yPx = change.position.y,
+                            containerSizePx = containerSize.width,
+                            verticalOffsetPx = verticalOffset,
+                        )
+                    }
                 }
             }
     ) {
@@ -247,3 +303,62 @@
     // farther the user input pointer goes from the line, the more opaque the line gets.
     return ((lineLength / gridSpacing - 0.3f) * 4f).coerceIn(0f, 1f)
 }
+
+private suspend fun showFailureAnimation(
+    dots: List<PatternDotViewModel>,
+    scalingAnimatables: Map<PatternDotViewModel, Animatable<Float, AnimationVector1D>>,
+) {
+    val dotsByRow =
+        buildList<MutableList<PatternDotViewModel>> {
+            dots.forEach { dot ->
+                val rowIndex = dot.y
+                while (size <= rowIndex) {
+                    add(mutableListOf())
+                }
+                get(rowIndex).add(dot)
+            }
+        }
+
+    coroutineScope {
+        dotsByRow.forEachIndexed { rowIndex, rowDots ->
+            rowDots.forEach { dot ->
+                scalingAnimatables[dot]?.let { dotScaleAnimatable ->
+                    launch {
+                        dotScaleAnimatable.animateTo(
+                            targetValue =
+                                FAILURE_ANIMATION_DOT_DIAMETER_DP / DOT_DIAMETER_DP.toFloat(),
+                            animationSpec =
+                                tween(
+                                    durationMillis =
+                                        FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS,
+                                    delayMillis =
+                                        rowIndex * FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS,
+                                    easing = Easings.LinearEasing,
+                                ),
+                        )
+
+                        dotScaleAnimatable.animateTo(
+                            targetValue = 1f,
+                            animationSpec =
+                                tween(
+                                    durationMillis =
+                                        FAILURE_ANIMATION_DOT_REVERT_ANIMATION_DURATION,
+                                    easing = Easings.StandardEasing,
+                                ),
+                        )
+                    }
+                }
+            }
+        }
+    }
+}
+
+private const val DOT_DIAMETER_DP = 16
+private const val SELECTED_DOT_DIAMETER_DP = 24
+private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83
+private const val SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS = 750
+private const val LINE_STROKE_WIDTH_DP = 16
+private const val FAILURE_ANIMATION_DOT_DIAMETER_DP = 13
+private const val FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS = 50
+private const val FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS = 33
+private const val FAILURE_ANIMATION_DOT_REVERT_ANIMATION_DURATION = 617
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 9c210c2..968e5ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -18,11 +18,15 @@
 
 package com.android.systemui.bouncer.ui.composable
 
+import android.view.HapticFeedbackConstants
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.tween
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.scaleIn
@@ -48,6 +52,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -55,15 +60,23 @@
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Easings
 import com.android.compose.grid.VerticalGrid
 import com.android.systemui.R
 import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.compose.modifiers.thenIf
 import kotlin.math.max
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.DurationUnit
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
 
 @Composable
 internal fun PinBouncer(
@@ -75,6 +88,16 @@
 
     // The length of the PIN input received so far, so we know how many dots to render.
     val pinLength: Pair<Int, Int> by viewModel.pinLengths.collectAsState()
+    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
+    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+
+    // Show the failure animation if the user entered the wrong input.
+    LaunchedEffect(animateFailure) {
+        if (animateFailure) {
+            showFailureAnimation()
+            viewModel.onFailureAnimationShown()
+        }
+    }
 
     Column(
         horizontalAlignment = Alignment.CenterHorizontally,
@@ -116,6 +139,7 @@
                 val digit = index + 1
                 PinButton(
                     onClicked = { viewModel.onPinButtonClicked(digit) },
+                    isEnabled = isInputEnabled,
                 ) { contentColor ->
                     PinDigit(digit, contentColor)
                 }
@@ -124,7 +148,8 @@
             PinButton(
                 onClicked = { viewModel.onBackspaceButtonClicked() },
                 onLongPressed = { viewModel.onBackspaceButtonLongPressed() },
-                isHighlighted = true,
+                isEnabled = isInputEnabled,
+                isIconButton = true,
             ) { contentColor ->
                 PinIcon(
                     Icon.Resource(
@@ -138,13 +163,15 @@
 
             PinButton(
                 onClicked = { viewModel.onPinButtonClicked(0) },
+                isEnabled = isInputEnabled,
             ) { contentColor ->
                 PinDigit(0, contentColor)
             }
 
             PinButton(
                 onClicked = { viewModel.onAuthenticateButtonClicked() },
-                isHighlighted = true,
+                isEnabled = isInputEnabled,
+                isIconButton = true,
             ) { contentColor ->
                 PinIcon(
                     Icon.Resource(
@@ -187,61 +214,107 @@
 @Composable
 private fun PinButton(
     onClicked: () -> Unit,
+    isEnabled: Boolean,
     modifier: Modifier = Modifier,
     onLongPressed: (() -> Unit)? = null,
-    isHighlighted: Boolean = false,
+    isIconButton: Boolean = false,
     content: @Composable (contentColor: Color) -> Unit,
 ) {
     var isPressed: Boolean by remember { mutableStateOf(false) }
+
+    val view = LocalView.current
+    LaunchedEffect(isPressed) {
+        if (isPressed) {
+            view.performHapticFeedback(
+                HapticFeedbackConstants.VIRTUAL_KEY,
+                HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
+            )
+        }
+    }
+
+    // Pin button animation specification is asymmetric: fast animation to the pressed state, and a
+    // slow animation upon release. Note that isPressed is guaranteed to be true for at least the
+    // press animation duration (see below in detectTapGestures).
+    val animEasing = if (isPressed) pinButtonPressedEasing else pinButtonReleasedEasing
+    val animDurationMillis =
+        (if (isPressed) pinButtonPressedDuration else pinButtonReleasedDuration).toInt(
+            DurationUnit.MILLISECONDS
+        )
+
     val cornerRadius: Dp by
         animateDpAsState(
-            if (isPressed) 24.dp else PinButtonSize / 2,
+            if (isPressed) 24.dp else pinButtonSize / 2,
             label = "PinButton round corners",
+            animationSpec = tween(animDurationMillis, easing = animEasing)
         )
+    val colorAnimationSpec: AnimationSpec<Color> = tween(animDurationMillis, easing = animEasing)
     val containerColor: Color by
         animateColorAsState(
             when {
-                isPressed -> MaterialTheme.colorScheme.primaryContainer
-                isHighlighted -> MaterialTheme.colorScheme.secondaryContainer
-                else -> MaterialTheme.colorScheme.surface
+                isPressed -> MaterialTheme.colorScheme.primary
+                isIconButton -> MaterialTheme.colorScheme.secondaryContainer
+                else -> MaterialTheme.colorScheme.surfaceVariant
             },
             label = "Pin button container color",
+            animationSpec = colorAnimationSpec
         )
     val contentColor: Color by
         animateColorAsState(
             when {
-                isPressed -> MaterialTheme.colorScheme.onPrimaryContainer
-                isHighlighted -> MaterialTheme.colorScheme.onSecondaryContainer
-                else -> MaterialTheme.colorScheme.onSurface
+                isPressed -> MaterialTheme.colorScheme.onPrimary
+                isIconButton -> MaterialTheme.colorScheme.onSecondaryContainer
+                else -> MaterialTheme.colorScheme.onSurfaceVariant
             },
             label = "Pin button container color",
+            animationSpec = colorAnimationSpec
         )
 
+    val scope = rememberCoroutineScope()
+
     Box(
         contentAlignment = Alignment.Center,
         modifier =
             modifier
-                .size(PinButtonSize)
+                .size(pinButtonSize)
                 .drawBehind {
                     drawRoundRect(
                         color = containerColor,
                         cornerRadius = CornerRadius(cornerRadius.toPx()),
                     )
                 }
-                .pointerInput(Unit) {
-                    detectTapGestures(
-                        onPress = {
-                            isPressed = true
-                            tryAwaitRelease()
-                            isPressed = false
-                        },
-                        onTap = { onClicked() },
-                        onLongPress = onLongPressed?.let { { onLongPressed() } },
-                    )
+                .thenIf(isEnabled) {
+                    Modifier.pointerInput(Unit) {
+                        detectTapGestures(
+                            onPress = {
+                                scope.launch {
+                                    isPressed = true
+                                    val minDuration = async {
+                                        delay(pinButtonPressedDuration + pinButtonHoldTime)
+                                    }
+                                    tryAwaitRelease()
+                                    minDuration.await()
+                                    isPressed = false
+                                }
+                            },
+                            onTap = { onClicked() },
+                            onLongPress = onLongPressed?.let { { onLongPressed() } },
+                        )
+                    }
                 },
     ) {
         content(contentColor)
     }
 }
 
-private val PinButtonSize = 84.dp
+private fun showFailureAnimation() {
+    // TODO(b/282730134): implement.
+}
+
+private val pinButtonSize = 84.dp
+
+// Pin button motion spec: http://shortn/_9TTIG6SoEa
+private val pinButtonPressedDuration = 100.milliseconds
+private val pinButtonPressedEasing = LinearEasing
+private val pinButtonHoldTime = 33.milliseconds
+private val pinButtonReleasedDuration = 420.milliseconds
+private val pinButtonReleasedEasing = Easings.StandardEasing
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/ConditionalModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/ConditionalModifiers.kt
new file mode 100644
index 0000000..83071d7
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/ConditionalModifiers.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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.compose.modifiers
+
+import androidx.compose.ui.Modifier
+
+/**
+ * Concatenates this modifier with another if `condition` is true.
+ *
+ * @param condition Whether or not to apply the modifiers.
+ * @param factory Creates the modifier to concatenate with the current one.
+ * @return a Modifier representing this modifier followed by other in sequence.
+ * @see Modifier.then
+ *
+ * This method allows inline conditional addition of modifiers to a modifier chain. Instead of
+ * writing
+ *
+ * ```
+ * val aModifier = Modifier.a()
+ * val bModifier = if(condition) aModifier.b() else aModifier
+ * Composable(modifier = bModifier)
+ * ```
+ *
+ * You can instead write
+ *
+ * ```
+ * Composable(modifier = Modifier.a().thenIf(condition){
+ *  Modifier.b()
+ * }
+ * ```
+ *
+ * This makes the modifier chain easier to read.
+ *
+ * Note that unlike the non-factory version, the conditional modifier is recreated each time, and
+ * may never be created at all.
+ */
+inline fun Modifier.thenIf(condition: Boolean, crossinline factory: () -> Modifier): Modifier =
+    if (condition) this.then(factory()) else this
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 252c8e3..e557c8e 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.plugins.ClockFaceController
 import com.android.systemui.plugins.ClockFaceEvents
 import com.android.systemui.plugins.ClockSettings
+import com.android.systemui.plugins.WeatherData
 import java.io.PrintWriter
 import java.util.Locale
 import java.util.TimeZone
@@ -50,6 +51,7 @@
     private val layoutInflater: LayoutInflater,
     private val resources: Resources,
     private val settings: ClockSettings?,
+    private val hasStepClockAnimation: Boolean = false,
 ) : ClockController {
     override val smallClock: DefaultClockFaceController
     override val largeClock: LargeClockFaceController
@@ -170,7 +172,8 @@
         view: AnimatableClockView,
         seedColor: Int?,
     ) : DefaultClockFaceController(view, seedColor) {
-        override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
+        override val config =
+            ClockFaceConfig(hasCustomPositionUpdatedAnimation = hasStepClockAnimation)
 
         init {
             animations = LargeClockAnimations(view, 0f, 0f)
@@ -225,6 +228,8 @@
 
             clocks.forEach { it.refreshFormat() }
         }
+
+        override fun onWeatherDataChanged(data: WeatherData) {}
     }
 
     open inner class DefaultClockAnimations(
@@ -271,6 +276,8 @@
             // the top margin change in recomputePadding to make clock be centered
             view.translationY = 0.5f * view.bottom * (1 - swipingFraction)
         }
+
+        override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}
     }
 
     inner class LargeClockAnimations(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 0fd1b49..949641a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -29,10 +29,11 @@
 const val DEFAULT_CLOCK_ID = "DEFAULT"
 
 /** Provides the default system clock */
-class DefaultClockProvider constructor(
+class DefaultClockProvider(
     val ctx: Context,
     val layoutInflater: LayoutInflater,
-    val resources: Resources
+    val resources: Resources,
+    val hasStepClockAnimation: Boolean = false
 ) : ClockProvider {
     override fun getClocks(): List<ClockMetadata> =
         listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))
@@ -42,7 +43,13 @@
             throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
         }
 
-        return DefaultClockController(ctx, layoutInflater, resources, settings)
+        return DefaultClockController(
+            ctx,
+            layoutInflater,
+            resources,
+            settings,
+            hasStepClockAnimation,
+        )
     }
 
     override fun getClockThumbnail(id: ClockId): Drawable? {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 5d0a3af..3ae328e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -74,18 +74,10 @@
         resources: Resources,
         dozeFraction: Float,
         foldFraction: Float,
-    ) {
-        events.onColorPaletteChanged(resources)
-        smallClock.animations.doze(dozeFraction)
-        largeClock.animations.doze(dozeFraction)
-        smallClock.animations.fold(foldFraction)
-        largeClock.animations.fold(foldFraction)
-        smallClock.events.onTimeTick()
-        largeClock.events.onTimeTick()
-    }
+    )
 
     /** Optional method for dumping debug information */
-    fun dump(pw: PrintWriter) {}
+    fun dump(pw: PrintWriter)
 }
 
 /** Interface for a specific clock face version rendered by the clock */
@@ -109,37 +101,37 @@
 /** Events that should call when various rendering parameters change */
 interface ClockEvents {
     /** Call whenever timezone changes */
-    fun onTimeZoneChanged(timeZone: TimeZone) {}
+    fun onTimeZoneChanged(timeZone: TimeZone)
 
     /** Call whenever the text time format changes (12hr vs 24hr) */
-    fun onTimeFormatChanged(is24Hr: Boolean) {}
+    fun onTimeFormatChanged(is24Hr: Boolean)
 
     /** Call whenever the locale changes */
-    fun onLocaleChanged(locale: Locale) {}
+    fun onLocaleChanged(locale: Locale)
 
     /** Call whenever the color palette should update */
-    fun onColorPaletteChanged(resources: Resources) {}
+    fun onColorPaletteChanged(resources: Resources)
 
     /** Call if the seed color has changed and should be updated */
-    fun onSeedColorChanged(seedColor: Int?) {}
+    fun onSeedColorChanged(seedColor: Int?)
 
     /** Call whenever the weather data should update */
-    fun onWeatherDataChanged(data: WeatherData) {}
+    fun onWeatherDataChanged(data: WeatherData)
 }
 
 /** Methods which trigger various clock animations */
 interface ClockAnimations {
     /** Runs an enter animation (if any) */
-    fun enter() {}
+    fun enter()
 
     /** Sets how far into AOD the device currently is. */
-    fun doze(fraction: Float) {}
+    fun doze(fraction: Float)
 
     /** Sets how far into the folding animation the device is. */
-    fun fold(fraction: Float) {}
+    fun fold(fraction: Float)
 
     /** Runs the battery animation (if any). */
-    fun charge() {}
+    fun charge()
 
     /**
      * Runs when the clock's position changed during the move animation.
@@ -150,32 +142,32 @@
      * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means
      *   it finished moving.
      */
-    fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}
+    fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float)
 
     /**
      * Runs when swiping clock picker, swipingFraction: 1.0 -> clock is scaled up in the preview,
      * 0.0 -> clock is scaled down in the shade; previewRatio is previewSize / screenSize
      */
-    fun onPickerCarouselSwiping(swipingFraction: Float) {}
+    fun onPickerCarouselSwiping(swipingFraction: Float)
 }
 
 /** Events that have specific data about the related face */
 interface ClockFaceEvents {
     /** Call every time tick */
-    fun onTimeTick() {}
+    fun onTimeTick()
 
     /**
      * Region Darkness specific to the clock face.
      * - isRegionDark = dark theme -> clock should be light
      * - !isRegionDark = light theme -> clock should be dark
      */
-    fun onRegionDarknessChanged(isRegionDark: Boolean) {}
+    fun onRegionDarknessChanged(isRegionDark: Boolean)
 
     /**
      * Call whenever font settings change. Pass in a target font size in pixels. The specific clock
      * design is allowed to ignore this target size on a case-by-case basis.
      */
-    fun onFontSettingChanged(fontSizePx: Float) {}
+    fun onFontSettingChanged(fontSizePx: Float)
 
     /**
      * Target region information for the clock face. For small clock, this will match the bounds of
@@ -184,7 +176,7 @@
      * render within the centered targetRect to avoid obstructing other elements. The specified
      * targetRegion is relative to the parent view.
      */
-    fun onTargetRegionChanged(targetRegion: Rect?) {}
+    fun onTargetRegionChanged(targetRegion: Rect?)
 }
 
 /** Tick rates for clocks */
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml
new file mode 100644
index 0000000..2a80920
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+<resources>
+     <!-- Invisibility to use for the date & weather view when it is disabled by a clock -->
+    <integer name="keyguard_date_weather_view_invisibility">8</integer>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values/integers.xml b/packages/SystemUI/res-keyguard/values/integers.xml
index c6e90c0..b08fde3 100644
--- a/packages/SystemUI/res-keyguard/values/integers.xml
+++ b/packages/SystemUI/res-keyguard/values/integers.xml
@@ -27,4 +27,7 @@
 
          0x50 = bottom, 0x01 = center_horizontal -->
     <integer name="keyguard_host_view_one_handed_gravity">0x51</integer>
+
+     <!-- Invisibility to use for the date & weather view when it is disabled by a clock -->
+    <integer name="keyguard_date_weather_view_invisibility">4</integer>
 </resources>
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index 0d86e0a..ab522a3 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -36,7 +36,7 @@
             android:layout_width="@dimen/screenrecord_logo_size"
             android:layout_height="@dimen/screenrecord_logo_size"
             android:src="@drawable/ic_media_projection_permission"
-            android:tint="?androidprv:attr/colorAccentPrimary"
+            android:tint="?androidprv:attr/colorAccentPrimaryVariant"
             android:importantForAccessibility="no"/>
         <TextView
             android:id="@+id/screen_share_dialog_title"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index d8bf570..99e2574 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -86,6 +86,7 @@
 
     private int mKeyguardSmallClockTopMargin = 0;
     private int mKeyguardLargeClockTopMargin = 0;
+    private int mKeyguardDateWeatherViewInvisibility = View.INVISIBLE;
     private final ClockRegistry.ClockChangeListener mClockChangedListener;
 
     private ViewGroup mStatusArea;
@@ -201,6 +202,8 @@
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
         mKeyguardLargeClockTopMargin =
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin);
+        mKeyguardDateWeatherViewInvisibility =
+                mView.getResources().getInteger(R.integer.keyguard_date_weather_view_invisibility);
 
         if (mOnlyClock) {
             View ksv = mView.findViewById(R.id.keyguard_slice_view);
@@ -335,7 +338,10 @@
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
         mKeyguardLargeClockTopMargin =
                 mView.getResources().getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin);
+        mKeyguardDateWeatherViewInvisibility =
+                mView.getResources().getInteger(R.integer.keyguard_date_weather_view_invisibility);
         mView.updateClockTargetRegions();
+        setDateWeatherVisibility();
     }
 
 
@@ -497,8 +503,9 @@
     private void setDateWeatherVisibility() {
         if (mDateWeatherView != null) {
             mUiExecutor.execute(() -> {
-                mDateWeatherView.setVisibility(
-                        clockHasCustomWeatherDataDisplay() ? View.INVISIBLE : View.VISIBLE);
+                mDateWeatherView.setVisibility(clockHasCustomWeatherDataDisplay()
+                        ? mKeyguardDateWeatherViewInvisibility
+                        : View.VISIBLE);
             });
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index afc2590..99b5d52 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -158,15 +158,18 @@
 
     public void startAppearAnimation() {
         enableClipping(false);
-        setAlpha(1f);
+        setAlpha(0f);
         setTranslationY(mAppearAnimationUtils.getStartTranslation());
         AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
                 0, mAppearAnimationUtils.getInterpolator(),
                 getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PATTERN_APPEAR));
-        mAppearAnimationUtils.startAnimation2d(
-                mLockPatternView.getCellStates(),
-                () -> enableClipping(true),
-                this);
+        mLockPatternView.post(() -> {
+            setAlpha(1f);
+            mAppearAnimationUtils.startAnimation2d(
+                    mLockPatternView.getCellStates(),
+                    () -> enableClipping(true),
+                    KeyguardPatternView.this);
+        });
         if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
             mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
                     AppearAnimationUtils.DEFAULT_APPEAR_DURATION,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 1db0ab6..835cc13 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -46,7 +46,6 @@
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.systemui.R;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.ClockController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -363,7 +362,7 @@
         boolean customClockAnimation = clock != null
                 && clock.getLargeClock().getConfig().getHasCustomPositionUpdatedAnimation();
 
-        if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
+        if (customClockAnimation) {
             // Find the clock, so we can exclude it from this transition.
             FrameLayout clockContainerView = mView.findViewById(R.id.lockscreen_clock_view_large);
 
@@ -400,8 +399,10 @@
     @VisibleForTesting
     static class SplitShadeTransitionAdapter extends Transition {
         private static final String PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft";
+        private static final String PROP_BOUNDS_RIGHT = "splitShadeTransitionAdapter:boundsRight";
         private static final String PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow";
-        private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW};
+        private static final String[] TRANSITION_PROPERTIES = {
+                PROP_BOUNDS_LEFT, PROP_BOUNDS_RIGHT, PROP_X_IN_WINDOW};
 
         private final KeyguardClockSwitchController mController;
 
@@ -412,6 +413,7 @@
 
         private void captureValues(TransitionValues transitionValues) {
             transitionValues.values.put(PROP_BOUNDS_LEFT, transitionValues.view.getLeft());
+            transitionValues.values.put(PROP_BOUNDS_RIGHT, transitionValues.view.getRight());
             int[] locationInWindowTmp = new int[2];
             transitionValues.view.getLocationInWindow(locationInWindowTmp);
             transitionValues.values.put(PROP_X_IN_WINDOW, locationInWindowTmp[0]);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e8046dc0..1721891 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -246,6 +246,7 @@
     private static final int MSG_TIME_FORMAT_UPDATE = 344;
     private static final int MSG_REQUIRE_NFC_UNLOCK = 345;
     private static final int MSG_KEYGUARD_DISMISS_ANIMATION_FINISHED = 346;
+    private static final int MSG_SERVICE_PROVIDERS_UPDATED = 347;
 
     /** Biometric authentication state: Not listening. */
     private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -1826,6 +1827,8 @@
             } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
                 String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
+            } else if (TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED.equals(action)) {
+                mHandler.obtainMessage(MSG_SERVICE_PROVIDERS_UPDATED, intent).sendToTarget();
             } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                 mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
             } else if (Intent.ACTION_SERVICE_STATE.equals(action)) {
@@ -2402,6 +2405,9 @@
                     case MSG_SERVICE_STATE_CHANGE:
                         handleServiceStateChange(msg.arg1, (ServiceState) msg.obj);
                         break;
+                    case MSG_SERVICE_PROVIDERS_UPDATED:
+                        handleServiceProvidersUpdated((Intent) msg.obj);
+                        break;
                     case MSG_SCREEN_TURNED_OFF:
                         Trace.beginSection("KeyguardUpdateMonitor#handler MSG_SCREEN_TURNED_OFF");
                         handleScreenTurnedOff();
@@ -2472,6 +2478,7 @@
         filter.addAction(Intent.ACTION_SERVICE_STATE);
         filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         filter.addAction(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED);
         mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler);
@@ -3727,6 +3734,14 @@
     }
 
     /**
+     * Handle {@link #MSG_SERVICE_PROVIDERS_UPDATED}
+     */
+    private void handleServiceProvidersUpdated(Intent intent) {
+        mLogger.logServiceProvidersUpdated(intent);
+        callbacksRefreshCarrierInfo();
+    }
+
+    /**
      * Whether the keyguard is showing and not occluded.
      */
     public boolean isKeyguardVisible() {
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 2d0bf9c..d2b10d6 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -63,7 +63,11 @@
                 bgDispatcher,
                 featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
                 /* handleAllUsers= */ true,
-                new DefaultClockProvider(context, layoutInflater, resources),
+                new DefaultClockProvider(
+                        context,
+                        layoutInflater,
+                        resources,
+                        featureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION)),
                 context.getString(R.string.lockscreen_clock_id_fallback),
                 logBuffer,
                 /* keepAllLoaded = */ false,
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 17cc236..4923ab0 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -16,11 +16,15 @@
 
 package com.android.keyguard.logging
 
+import android.content.Intent
 import android.hardware.biometrics.BiometricConstants.LockoutMode
 import android.os.PowerManager
 import android.os.PowerManager.WakeReason
 import android.telephony.ServiceState
 import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import android.telephony.TelephonyManager
 import com.android.keyguard.ActiveUnlockConfig
 import com.android.keyguard.FaceAuthUiEvent
 import com.android.keyguard.KeyguardListenModel
@@ -363,6 +367,21 @@
         )
     }
 
+    fun logServiceProvidersUpdated(intent: Intent) {
+        logBuffer.log(
+                TAG,
+                VERBOSE,
+                {
+                    int1 = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID)
+                    str1 = intent.getStringExtra(TelephonyManager.EXTRA_SPN)
+                    str2 = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
+                },
+                {
+                    "action SERVICE_PROVIDERS_UPDATED subId=$int1 spn=$str1 plmn=$str2"
+                }
+        )
+    }
+
     fun logSimState(subId: Int, slotId: Int, state: Int) {
         logBuffer.log(
             TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 0782537..630cfff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -152,7 +152,7 @@
         WindowManager.LayoutParams(
                 WindowManager.LayoutParams.WRAP_CONTENT,
                 WindowManager.LayoutParams.WRAP_CONTENT,
-                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                 Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
                 PixelFormat.TRANSLUCENT
             )
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index e462e2f..1d2fce7 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -148,12 +148,17 @@
      *
      * If the input is correct, the device will be unlocked and the lock screen and bouncer will be
      * dismissed and hidden.
+     *
+     * @param input The input from the user to try to authenticate with. This can be a list of
+     *   different things, based on the current authentication method.
+     * @return `true` if the authentication succeeded and the device is now unlocked; `false`
+     *   otherwise.
      */
     fun authenticate(
         input: List<Any>,
-    ) {
+    ): Boolean {
         if (repository.throttling.value != null) {
-            return
+            return false
         }
 
         val isAuthenticated = authenticationInteractor.authenticate(input)
@@ -186,6 +191,8 @@
                 }
             else -> repository.setMessage(errorMessage(authenticationMethod.value))
         }
+
+        return isAuthenticated
     }
 
     private fun promptMessage(authMethod: AuthenticationMethodModel): String {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 774a559..d95b70c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -16,14 +16,37 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 
-sealed interface AuthMethodBouncerViewModel {
+sealed class AuthMethodBouncerViewModel(
     /**
      * Whether user input is enabled.
      *
      * If `false`, user input should be completely ignored in the UI as the user is "locked out" of
      * being able to attempt to unlock the device.
      */
-    val isInputEnabled: StateFlow<Boolean>
+    val isInputEnabled: StateFlow<Boolean>,
+) {
+
+    private val _animateFailure = MutableStateFlow(false)
+    /**
+     * Whether a failure animation should be shown. Once consumed, the UI must call
+     * [onFailureAnimationShown] to consume this state.
+     */
+    val animateFailure: StateFlow<Boolean> = _animateFailure.asStateFlow()
+
+    /**
+     * Notifies that the failure animation has been shown. This should be called to consume a `true`
+     * value in [animateFailure].
+     */
+    fun onFailureAnimationShown() {
+        _animateFailure.value = false
+    }
+
+    /** Ask the UI to show the failure animation. */
+    protected fun showFailureAnimation() {
+        _animateFailure.value = true
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 02991bd..984d9ab 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.R
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.shared.model.AuthenticationThrottledModel
 import com.android.systemui.dagger.qualifiers.Application
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
@@ -29,6 +30,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
@@ -45,21 +47,6 @@
 ) {
     private val interactor: BouncerInteractor = interactorFactory.create(containerName)
 
-    /**
-     * Whether updates to the message should be cross-animated from one message to another.
-     *
-     * If `false`, no animation should be applied, the message text should just be replaced
-     * instantly.
-     */
-    val isMessageUpdateAnimationsEnabled: StateFlow<Boolean> =
-        interactor.throttling
-            .map { it == null }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = interactor.throttling.value == null,
-            )
-
     private val isInputEnabled: StateFlow<Boolean> =
         interactor.throttling
             .map { it == null }
@@ -104,13 +91,21 @@
             )
 
     /** The user-facing message to show in the bouncer. */
-    val message: StateFlow<String> =
-        interactor.message
-            .map { it ?: "" }
+    val message: StateFlow<MessageViewModel> =
+        combine(
+                interactor.message,
+                interactor.throttling,
+            ) { message, throttling ->
+                toMessageViewModel(message, throttling)
+            }
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.WhileSubscribed(),
-                initialValue = interactor.message.value ?: "",
+                initialValue =
+                    toMessageViewModel(
+                        message = interactor.message.value,
+                        throttling = interactor.throttling.value,
+                    ),
             )
 
     private val _throttlingDialogMessage = MutableStateFlow<String?>(null)
@@ -177,6 +172,28 @@
         }
     }
 
+    private fun toMessageViewModel(
+        message: String?,
+        throttling: AuthenticationThrottledModel?,
+    ): MessageViewModel {
+        return MessageViewModel(
+            text = message ?: "",
+            isUpdateAnimated = throttling == null,
+        )
+    }
+
+    data class MessageViewModel(
+        val text: String,
+
+        /**
+         * Whether updates to the message should be cross-animated from one message to another.
+         *
+         * If `false`, no animation should be applied, the message text should just be replaced
+         * instantly.
+         */
+        val isUpdateAnimated: Boolean,
+    )
+
     @AssistedFactory
     interface Factory {
         fun create(
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index c38fcaa..55929b5 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -24,8 +24,11 @@
 /** Holds UI state and handles user input for the password bouncer UI. */
 class PasswordBouncerViewModel(
     private val interactor: BouncerInteractor,
-    override val isInputEnabled: StateFlow<Boolean>,
-) : AuthMethodBouncerViewModel {
+    isInputEnabled: StateFlow<Boolean>,
+) :
+    AuthMethodBouncerViewModel(
+        isInputEnabled = isInputEnabled,
+    ) {
 
     private val _password = MutableStateFlow("")
     /** The password entered so far. */
@@ -47,7 +50,10 @@
 
     /** Notifies that the user has pressed the key for attempting to authenticate the password. */
     fun onAuthenticateKeyPressed() {
-        interactor.authenticate(password.value.toCharArray().toList())
+        if (!interactor.authenticate(password.value.toCharArray().toList())) {
+            showFailureAnimation()
+        }
+
         _password.value = ""
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 1b0b38e..d9ef75d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -37,8 +37,11 @@
     private val applicationContext: Context,
     applicationScope: CoroutineScope,
     private val interactor: BouncerInteractor,
-    override val isInputEnabled: StateFlow<Boolean>,
-) : AuthMethodBouncerViewModel {
+    isInputEnabled: StateFlow<Boolean>,
+) :
+    AuthMethodBouncerViewModel(
+        isInputEnabled = isInputEnabled,
+    ) {
 
     /** The number of columns in the dot grid. */
     val columnCount = 3
@@ -150,7 +153,11 @@
 
     /** Notifies that the user has ended the drag gesture across the dot grid. */
     fun onDragEnd() {
-        interactor.authenticate(_selectedDots.value.map { it.toCoordinate() })
+        val isSuccessfullyAuthenticated =
+            interactor.authenticate(_selectedDots.value.map { it.toCoordinate() })
+        if (!isSuccessfullyAuthenticated) {
+            showFailureAnimation()
+        }
 
         _dots.value = defaultDots()
         _currentDot.value = null
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 2a733d9..5c0fd92 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -33,8 +33,11 @@
 class PinBouncerViewModel(
     private val applicationScope: CoroutineScope,
     private val interactor: BouncerInteractor,
-    override val isInputEnabled: StateFlow<Boolean>,
-) : AuthMethodBouncerViewModel {
+    isInputEnabled: StateFlow<Boolean>,
+) :
+    AuthMethodBouncerViewModel(
+        isInputEnabled = isInputEnabled,
+    ) {
 
     private val entered = MutableStateFlow<List<Int>>(emptyList())
     /**
@@ -92,7 +95,10 @@
 
     /** Notifies that the user clicked the "enter" button. */
     fun onAuthenticateButtonClicked() {
-        interactor.authenticate(entered.value)
+        if (!interactor.authenticate(entered.value)) {
+            showFailureAnimation()
+        }
+
         entered.value = emptyList()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 8ba060e..1eba667 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.ActivityTaskManagerProxy
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.indentIfPossible
 import java.io.PrintWriter
@@ -67,6 +68,7 @@
     @Background private val backgroundExecutor: Executor,
     private val serviceListingBuilder: (Context) -> ServiceListing,
     private val userTracker: UserTracker,
+    private val activityTaskManagerProxy: ActivityTaskManagerProxy,
     dumpManager: DumpManager,
     private val featureFlags: FeatureFlags
 ) : ControlsListingController, Dumpable {
@@ -76,9 +78,18 @@
             context: Context,
             @Background executor: Executor,
             userTracker: UserTracker,
+            activityTaskManagerProxy: ActivityTaskManagerProxy,
             dumpManager: DumpManager,
             featureFlags: FeatureFlags
-    ) : this(context, executor, ::createServiceListing, userTracker, dumpManager, featureFlags)
+    ) : this(
+        context,
+        executor,
+        ::createServiceListing,
+        userTracker,
+        activityTaskManagerProxy,
+        dumpManager,
+        featureFlags
+    )
 
     private var serviceListing = serviceListingBuilder(context)
     // All operations in background thread
@@ -113,7 +124,8 @@
     }
 
     private fun updateServices(newServices: List<ControlsServiceInfo>) {
-        if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
+        if (featureFlags.isEnabled(Flags.USE_APP_PANELS) &&
+                activityTaskManagerProxy.supportsMultiWindow(context)) {
             val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
             newServices.forEach {
                 it.resolvePanelActivity(allowAllApps) }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index a2b159c..d6c0829 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -731,4 +731,9 @@
     // TODO(b/283084712): Tracking Bug
     @JvmField
     val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations")
+
+    // TODO(b/283447257): Tracking bug
+    @JvmField
+    val BIGPICTURE_NOTIFICATION_LAZY_LOADING =
+            unreleasedFlag(283447257, "bigpicture_notification_lazy_loading")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index b8d3121..a8d22c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -122,6 +122,10 @@
                 }
             }
 
+            // Avoid wrapping non-task and non-wallpaper changes as they don't need to animate
+            // for keyguard unlock animation.
+            if (taskId < 0 && !wallpapers) continue;
+
             final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
                     // wallpapers go into the "below" layer space
                     info.getChanges().size() - i,
@@ -141,8 +145,8 @@
             return apps.length == 0 ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
                     : TRANSIT_OLD_KEYGUARD_GOING_AWAY;
         } else if (type == TRANSIT_KEYGUARD_OCCLUDE) {
-            boolean isOccludeByDream = apps.length > 0 && apps[0].taskInfo.topActivityType
-                    == WindowConfiguration.ACTIVITY_TYPE_DREAM;
+            boolean isOccludeByDream = apps.length > 0 && apps[0].taskInfo != null
+                    && apps[0].taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_DREAM;
             if (isOccludeByDream) return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
             return TRANSIT_OLD_KEYGUARD_OCCLUDE;
         } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
@@ -303,46 +307,6 @@
         }
     }
 
-    final IRemoteTransition mOccludeAnimation = new IRemoteTransition.Stub() {
-        @Override
-        public void startAnimation(IBinder transition, TransitionInfo info,
-                SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
-                    throws RemoteException {
-            t.apply();
-            mBinder.setOccluded(true /* isOccluded */, true /* animate */);
-            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
-            info.releaseAllSurfaces();
-        }
-
-        @Override
-        public void mergeAnimation(IBinder transition, TransitionInfo info,
-                SurfaceControl.Transaction t, IBinder mergeTarget,
-                IRemoteTransitionFinishedCallback finishCallback) {
-            t.close();
-            info.releaseAllSurfaces();
-        }
-    };
-
-    final IRemoteTransition mUnoccludeAnimation = new IRemoteTransition.Stub() {
-        @Override
-        public void startAnimation(IBinder transition, TransitionInfo info,
-                SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
-                throws RemoteException {
-            t.apply();
-            mBinder.setOccluded(false /* isOccluded */, true /* animate */);
-            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
-            info.releaseAllSurfaces();
-        }
-
-        @Override
-        public void mergeAnimation(IBinder transition, TransitionInfo info,
-                SurfaceControl.Transaction t, IBinder mergeTarget,
-                IRemoteTransitionFinishedCallback finishCallback) {
-            t.close();
-            info.releaseAllSurfaces();
-        }
-    };
-
     private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
         private static final String TRACK_NAME = "IKeyguardService";
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 122e259..29a7fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -637,7 +637,9 @@
      * Unlock to the launcher, using in-window animations, and the smartspace shared element
      * transition if possible.
      */
-    private fun unlockToLauncherWithInWindowAnimations() {
+
+    @VisibleForTesting
+    fun unlockToLauncherWithInWindowAnimations() {
         setSurfaceBehindAppearAmount(1f, wallpapers = false)
 
         try {
@@ -662,10 +664,28 @@
 
         // Now that the Launcher surface (with its smartspace positioned identically to ours) is
         // visible, hide our smartspace.
-        lockscreenSmartspace?.visibility = View.INVISIBLE
+        if (lockscreenSmartspace?.visibility == View.VISIBLE) {
+            lockscreenSmartspace?.visibility = View.INVISIBLE
+        }
 
-        // Start an animation for the wallpaper, which will finish keyguard exit when it completes.
-        fadeInWallpaper()
+        // As soon as the shade has animated out of the way, start the canned unlock animation,
+        // which will finish keyguard exit when it completes. The in-window animations in the
+        // Launcher window will end on their own.
+        handler.postDelayed({
+            if (keyguardViewMediator.get().isShowingAndNotOccluded &&
+                !keyguardStateController.isKeyguardGoingAway) {
+                    Log.e(TAG, "Finish keyguard exit animation delayed Runnable ran, but we are " +
+                            "showing and not going away.")
+                return@postDelayed
+            }
+
+            if (wallpaperTargets != null) {
+              fadeInWallpaper()
+            } else {
+                keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
+                    false /* cancelled */)
+            }
+        }, CANNED_UNLOCK_START_DELAY)
     }
 
     /**
@@ -914,7 +934,9 @@
         willUnlockWithSmartspaceTransition = false
 
         // The lockscreen surface is gone, so it is now safe to re-show the smartspace.
-        lockscreenSmartspace?.visibility = View.VISIBLE
+        if (lockscreenSmartspace?.visibility == View.INVISIBLE) {
+            lockscreenSmartspace?.visibility = View.VISIBLE
+        }
 
         listeners.forEach { it.onUnlockAnimationFinished() }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
index 356a8fb..4dad179 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -47,7 +47,7 @@
 class KeyguardQuickAffordanceLocalUserSelectionManager
 @Inject
 constructor(
-    @Application context: Context,
+    @Application private val context: Context,
     private val userFileManager: UserFileManager,
     private val userTracker: UserTracker,
     broadcastDispatcher: BroadcastDispatcher,
@@ -126,6 +126,11 @@
             }
 
     override fun getSelections(): Map<String, List<String>> {
+        // If the custom shortcuts feature is not enabled, ignore prior selections and use defaults
+        if (!context.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)) {
+            return defaults
+        }
+
         val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) }
         val result =
             slotKeys
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 4ba2eb9..1227078 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -18,7 +18,6 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.content.Context
-import android.content.Intent
 import android.graphics.drawable.Drawable
 import android.service.quickaccesswallet.GetWalletCardsError
 import android.service.quickaccesswallet.GetWalletCardsResponse
@@ -33,7 +32,6 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.Companion.componentName
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.wallet.controller.QuickAccessWalletController
 import javax.inject.Inject
@@ -103,17 +101,6 @@
             !walletController.walletClient.isWalletServiceAvailable ->
                 KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
             !walletController.isWalletEnabled || queryCards().isEmpty() -> {
-                val componentName =
-                    walletController.walletClient.createWalletSettingsIntent().toComponentName()
-                val actionText =
-                    if (componentName != null) {
-                        context.getString(
-                            R.string.keyguard_affordance_enablement_dialog_action_template,
-                            pickerName,
-                        )
-                    } else {
-                        null
-                    }
                 KeyguardQuickAffordanceConfig.PickerScreenState.Disabled(
                     instructions =
                         listOf(
@@ -124,8 +111,6 @@
                                 R.string.keyguard_affordance_enablement_dialog_wallet_instruction_2
                             ),
                         ),
-                    actionText = actionText,
-                    actionComponentName = componentName,
                 )
             }
             else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default()
@@ -182,14 +167,6 @@
         }
     }
 
-    private fun Intent?.toComponentName(): String? {
-        if (this == null) {
-            return null
-        }
-
-        return componentName(packageName = `package`, action = action)
-    }
-
     companion object {
         private const val TAG = "QuickAccessWalletKeyguardQuickAffordanceConfig"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index 641e20b..16ad29a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -21,12 +21,17 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import com.android.systemui.plugins.ClockId
+import com.android.systemui.shared.clocks.ClockRegistry
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.withContext
 
@@ -35,6 +40,7 @@
 @Inject
 constructor(
     private val secureSettings: SecureSettings,
+    private val clockRegistry: ClockRegistry,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
 
@@ -47,6 +53,24 @@
             .onStart { emit(Unit) } // Forces an initial update.
             .map { getClockSize() }
 
+    val currentClockId: Flow<ClockId> =
+        callbackFlow {
+                fun send() {
+                    trySend(clockRegistry.currentClockId)
+                }
+
+                val listener =
+                    object : ClockRegistry.ClockChangeListener {
+                        override fun onCurrentClockChanged() {
+                            send()
+                        }
+                    }
+                clockRegistry.registerClockChangeListener(listener)
+                send()
+                awaitClose { clockRegistry.unregisterClockChangeListener(listener) }
+            }
+            .mapNotNull { it }
+
     private suspend fun getClockSize(): SettingsClockSize {
         return withContext(backgroundDispatcher) {
             if (
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index 98f445c..dad5831 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
 import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import com.android.systemui.plugins.ClockId
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 
@@ -31,4 +32,6 @@
     repository: KeyguardClockRepository,
 ) {
     val selectedClockSize: Flow<SettingsClockSize> = repository.selectedClockSize
+
+    val currentClockId: Flow<ClockId> = repository.currentClockId
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index c8d37a1..a8d662c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -406,19 +406,21 @@
         view.isClickable = viewModel.isClickable
         if (viewModel.isClickable) {
             if (viewModel.useLongPress) {
-                view.setOnTouchListener(
-                    KeyguardQuickAffordanceOnTouchListener(
-                        view,
-                        viewModel,
-                        messageDisplayer,
-                        vibratorHelper,
-                        falsingManager,
-                    )
+                val onTouchListener = KeyguardQuickAffordanceOnTouchListener(
+                    view,
+                    viewModel,
+                    messageDisplayer,
+                    vibratorHelper,
+                    falsingManager,
                 )
+                view.setOnTouchListener(onTouchListener)
+                view.onLongClickListener =
+                    OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
             } else {
                 view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager)))
             }
         } else {
+            view.onLongClickListener = null
             view.setOnClickListener(null)
             view.setOnTouchListener(null)
         }
@@ -454,6 +456,42 @@
             .start()
     }
 
+    private class OnLongClickListener(
+        private val falsingManager: FalsingManager?,
+        private val viewModel: KeyguardQuickAffordanceViewModel,
+        private val vibratorHelper: VibratorHelper?,
+        private val onTouchListener: KeyguardQuickAffordanceOnTouchListener
+    ) : View.OnLongClickListener {
+        override fun onLongClick(view: View): Boolean {
+            if (falsingManager?.isFalseLongTap(FalsingManager.MODERATE_PENALTY) == true) {
+                return true
+            }
+
+            if (viewModel.configKey != null) {
+                viewModel.onClicked(
+                    KeyguardQuickAffordanceViewModel.OnClickedParameters(
+                        configKey = viewModel.configKey,
+                        expandable = Expandable.fromView(view),
+                        slotId = viewModel.slotId,
+                    )
+                )
+                vibratorHelper?.vibrate(
+                    if (viewModel.isActivated) {
+                        KeyguardBottomAreaVibrations.Activated
+                    } else {
+                        KeyguardBottomAreaVibrations.Deactivated
+                    }
+                )
+            }
+
+            onTouchListener.cancel()
+            return true
+        }
+
+        override fun onLongClickUseDefaultHapticFeedback(view: View?) = false
+
+    }
+
     private class OnClickListener(
         private val viewModel: KeyguardQuickAffordanceViewModel,
         private val falsingManager: FalsingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockSmartspaceViewBinder.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 57c32b3..1b5b329 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -21,19 +21,17 @@
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockSmartspaceViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.flow.collect
 
-/** Binder for the small clock view, large clock view and smartspace. */
-object KeyguardPreviewClockSmartspaceViewBinder {
+/** Binder for the small clock view, large clock view. */
+object KeyguardPreviewClockViewBinder {
 
     @JvmStatic
     fun bind(
         largeClockHostView: View,
         smallClockHostView: View,
-        smartspace: View?,
-        viewModel: KeyguardPreviewClockSmartspaceViewModel,
+        viewModel: KeyguardPreviewClockViewModel,
     ) {
         largeClockHostView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -46,15 +44,5 @@
                 viewModel.isSmallClockVisible.collect { smallClockHostView.isVisible = it }
             }
         }
-
-        smartspace?.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.STARTED) {
-                viewModel.smartSpaceTopPadding.collect { smartspace.setTopPadding(it) }
-            }
-        }
-    }
-
-    private fun View.setTopPadding(padding: Int) {
-        setPaddingRelative(paddingStart, padding, paddingEnd, paddingBottom)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
new file mode 100644
index 0000000..f5e4c6a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.view.View
+import androidx.core.view.isInvisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.launch
+
+/** Binder for the small clock view, large clock view and smartspace. */
+object KeyguardPreviewSmartspaceViewBinder {
+
+    @JvmStatic
+    fun bind(
+        smartspace: View,
+        viewModel: KeyguardPreviewSmartspaceViewModel,
+    ) {
+        smartspace.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch { viewModel.smartspaceTopPadding.collect { smartspace.setTopPadding(it) } }
+
+                launch { viewModel.shouldHideSmartspace.collect { smartspace.isInvisible = it } }
+            }
+        }
+    }
+
+    private fun View.setTopPadding(padding: Int) {
+        setPaddingRelative(paddingStart, padding, paddingEnd, paddingBottom)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index 5745d6a..7685345 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -46,7 +46,7 @@
     @SuppressLint("ClickableViewAccessibility")
     override fun onTouch(v: View, event: MotionEvent): Boolean {
         return when (event.actionMasked) {
-            MotionEvent.ACTION_DOWN ->
+            MotionEvent.ACTION_DOWN -> {
                 if (viewModel.configKey != null) {
                     downDisplayCoords.set(event.rawX, event.rawY)
                     if (isUsingAccurateTool(event)) {
@@ -62,21 +62,10 @@
                                 .scaleX(PRESSED_SCALE)
                                 .scaleY(PRESSED_SCALE)
                                 .setDuration(longPressDurationMs)
-                                .withEndAction {
-                                    if (
-                                        falsingManager?.isFalseLongTap(
-                                            FalsingManager.MODERATE_PENALTY
-                                        ) == false
-                                    ) {
-                                        dispatchClick(viewModel.configKey)
-                                    }
-                                    cancel()
-                                }
                     }
-                    true
-                } else {
-                    false
                 }
+                false
+            }
             MotionEvent.ACTION_MOVE -> {
                 if (!isUsingAccurateTool(event)) {
                     // Moving too far while performing a long-press gesture cancels that
@@ -91,7 +80,7 @@
                         cancel()
                     }
                 }
-                true
+                false
             }
             MotionEvent.ACTION_UP -> {
                 if (isUsingAccurateTool(event)) {
@@ -146,7 +135,7 @@
                             }
                     )
                 }
-                true
+                false
             }
             MotionEvent.ACTION_CANCEL -> {
                 cancel()
@@ -179,7 +168,7 @@
         view.setOnClickListener(null)
     }
 
-    private fun cancel(onAnimationEnd: Runnable? = null) {
+    fun cancel(onAnimationEnd: Runnable? = null) {
         longPressAnimator?.cancel()
         longPressAnimator = null
         view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 4308d84..fe62bf4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -42,9 +42,11 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockSmartspaceViewBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockSmartspaceViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.shared.clocks.ClockRegistry
 import com.android.systemui.shared.clocks.DefaultClockController
@@ -65,7 +67,8 @@
     @Application private val context: Context,
     @Main private val mainDispatcher: CoroutineDispatcher,
     @Main private val mainHandler: Handler,
-    private val clockSmartspaceViewModel: KeyguardPreviewClockSmartspaceViewModel,
+    private val clockViewModel: KeyguardPreviewClockViewModel,
+    private val smartspaceViewModel: KeyguardPreviewSmartspaceViewModel,
     private val bottomAreaViewModel: KeyguardBottomAreaViewModel,
     displayManager: DisplayManager,
     private val windowManager: WindowManager,
@@ -129,16 +132,18 @@
             setUpBottomArea(rootView)
 
             setUpSmartspace(rootView)
+            smartSpaceView?.let {
+                KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel)
+            }
 
             setUpUdfps(rootView)
 
             if (!shouldHideClock) {
                 setUpClock(rootView)
-                KeyguardPreviewClockSmartspaceViewBinder.bind(
+                KeyguardPreviewClockViewBinder.bind(
                     largeClockHostView,
                     smallClockHostView,
-                    smartSpaceView,
-                    clockSmartspaceViewModel,
+                    clockViewModel,
                 )
             }
 
@@ -219,8 +224,8 @@
         smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
 
         val topPadding: Int =
-            KeyguardPreviewClockSmartspaceViewModel.getLargeClockSmartspaceTopPadding(
-                context.resources
+            KeyguardPreviewSmartspaceViewModel.getLargeClockSmartspaceTopPadding(
+                context.resources,
             )
 
         val startPadding: Int =
@@ -235,7 +240,7 @@
         smartSpaceView?.let {
             it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
             it.isClickable = false
-
+            it.isInvisible = true
             parentView.addView(
                 it,
                 FrameLayout.LayoutParams(
@@ -372,7 +377,7 @@
                 resources.getDimensionPixelSize(R.dimen.small_clock_height)
             )
         layoutParams.topMargin =
-            KeyguardPreviewClockSmartspaceViewModel.getStatusBarHeight(resources) +
+            KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) +
                 resources.getDimensionPixelSize(R.dimen.small_clock_padding_top)
         hostView.layoutParams = layoutParams
 
@@ -394,9 +399,6 @@
 
         updateLargeClock(clock)
         updateSmallClock(clock)
-
-        // Hide smart space if the clock has weather display; otherwise show it
-        hideSmartspace(clock.largeClock.config.hasCustomWeatherDataDisplay)
     }
 
     private fun updateLargeClock(clock: ClockController) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt
new file mode 100644
index 0000000..5301302
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.content.Context
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.shared.model.SettingsClockSize
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** View model for the small clock view, large clock view. */
+class KeyguardPreviewClockViewModel
+@Inject
+constructor(
+    @Application private val context: Context,
+    interactor: KeyguardClockInteractor,
+) {
+
+    val isLargeClockVisible: Flow<Boolean> =
+        interactor.selectedClockSize.map { it == SettingsClockSize.DYNAMIC }
+
+    val isSmallClockVisible: Flow<Boolean> =
+        interactor.selectedClockSize.map { it == SettingsClockSize.SMALL }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockSmartspaceViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index 00c603b..bf51976 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewClockSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -24,23 +24,18 @@
 import com.android.systemui.keyguard.shared.model.SettingsClockSize
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 
-/** View model for the small clock view, large clock view and smartspace. */
-class KeyguardPreviewClockSmartspaceViewModel
+/** View model for the smartspace. */
+class KeyguardPreviewSmartspaceViewModel
 @Inject
 constructor(
     @Application private val context: Context,
     interactor: KeyguardClockInteractor,
 ) {
 
-    val isLargeClockVisible: Flow<Boolean> =
-        interactor.selectedClockSize.map { it == SettingsClockSize.DYNAMIC }
-
-    val isSmallClockVisible: Flow<Boolean> =
-        interactor.selectedClockSize.map { it == SettingsClockSize.SMALL }
-
-    val smartSpaceTopPadding: Flow<Int> =
+    val smartspaceTopPadding: Flow<Int> =
         interactor.selectedClockSize.map {
             when (it) {
                 SettingsClockSize.DYNAMIC -> getLargeClockSmartspaceTopPadding(context.resources)
@@ -48,6 +43,22 @@
             }
         }
 
+    val shouldHideSmartspace: Flow<Boolean> =
+        combine(
+                interactor.selectedClockSize,
+                interactor.currentClockId,
+                ::Pair,
+            )
+            .map { (size, currentClockId) ->
+                when (size) {
+                    // TODO (b/284122375) This is temporary. We should use clockController
+                    //      .largeClock.config.hasCustomWeatherDataDisplay instead, but
+                    //      ClockRegistry.createCurrentClock is not reliable.
+                    SettingsClockSize.DYNAMIC -> currentClockId == "DIGITAL_CLOCK_WEATHER"
+                    SettingsClockSize.SMALL -> false
+                }
+            }
+
     companion object {
         fun getLargeClockSmartspaceTopPadding(resources: Resources): Int {
             return with(resources) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index c9c2ea2..f6a2f37 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -29,6 +29,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -203,14 +204,17 @@
             dialogTitle = getString(R.string.media_projection_dialog_title, appName);
         }
 
+        // Using application context for the dialog, instead of the activity context, so we get
+        // the correct screen width when in split screen.
+        Context dialogContext = getApplicationContext();
         if (isPartialScreenSharingEnabled()) {
-            mDialog = new MediaProjectionPermissionDialog(this, () -> {
+            mDialog = new MediaProjectionPermissionDialog(dialogContext, () -> {
                 ScreenShareOption selectedOption =
                         ((MediaProjectionPermissionDialog) mDialog).getSelectedScreenShareOption();
                 grantMediaProjectionPermission(selectedOption.getMode());
             }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName);
         } else {
-            AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this,
+            AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(dialogContext,
                     R.style.Theme_SystemUI_Dialog)
                     .setTitle(dialogTitle)
                     .setIcon(R.drawable.ic_media_projection_permission)
@@ -263,7 +267,10 @@
         final UserHandle hostUserHandle = getHostUserHandle();
         if (mScreenCaptureDevicePolicyResolver.get()
                 .isScreenCaptureCompletelyDisabled(hostUserHandle)) {
-            AlertDialog dialog = new ScreenCaptureDisabledDialog(this);
+            // Using application context for the dialog, instead of the activity context, so we get
+            // the correct screen width when in split screen.
+            Context dialogContext = getApplicationContext();
+            AlertDialog dialog = new ScreenCaptureDisabledDialog(dialogContext);
             setUpDialog(dialog);
             dialog.show();
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index bce3346..6b993ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -53,6 +53,7 @@
 import android.util.Log
 import android.util.Pair as APair
 import androidx.media.utils.MediaConstants
+import com.android.internal.annotations.Keep
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.Dumpable
@@ -219,7 +220,7 @@
     private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
     // There should ONLY be at most one Smartspace media recommendation.
     var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
-    private var smartspaceSession: SmartspaceSession? = null
+    @Keep private var smartspaceSession: SmartspaceSession? = null
     private var allowMediaRecommendations = allowMediaRecommendations(context)
 
     private val artworkWidth =
@@ -381,6 +382,8 @@
 
     fun destroy() {
         smartspaceMediaDataProvider.unregisterListener(this)
+        smartspaceSession?.close()
+        smartspaceSession = null
         context.unregisterReceiver(appChangeReceiver)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 1fd11bd..77e2847 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -607,7 +607,7 @@
         )
     }
 
-    private var previousPreThresholdWidthInterpolator = params.entryWidthTowardsEdgeInterpolator
+    private var previousPreThresholdWidthInterpolator = params.entryWidthInterpolator
     private fun preThresholdWidthStretchAmount(progress: Float): Float {
         val interpolator = run {
             val isPastSlop = totalTouchDeltaInactive > viewConfiguration.scaledTouchSlop
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index 6d881d5..9ddb78a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -147,8 +147,21 @@
         val flungCommittedWidthSpring = createSpring(10000f, 1f)
         val flungCommittedHeightSpring = createSpring(10000f, 1f)
 
-        val entryIndicatorAlphaThreshold = .23f
-        val entryIndicatorAlphaFactor = 1.05f
+        val commonArrowDimensAlphaThreshold = .165f
+        val commonArrowDimensAlphaFactor = 1.05f
+        val commonArrowDimensAlphaSpring = Step(
+            threshold = commonArrowDimensAlphaThreshold,
+            factor = commonArrowDimensAlphaFactor,
+            postThreshold = createSpring(180f, 0.9f),
+            preThreshold = createSpring(2000f, 0.6f)
+        )
+        val commonArrowDimensAlphaSpringInterpolator = Step(
+            threshold = commonArrowDimensAlphaThreshold,
+            factor = commonArrowDimensAlphaFactor,
+            postThreshold = 1f,
+            preThreshold = 0f
+        )
+
         entryIndicator = BackIndicatorDimens(
                 horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
                 scale = getDimenFloat(R.dimen.navigation_edge_entry_scale),
@@ -162,18 +175,8 @@
                         alpha = 0f,
                         lengthSpring = createSpring(600f, 0.4f),
                         heightSpring = createSpring(600f, 0.4f),
-                        alphaSpring = Step(
-                                threshold = entryIndicatorAlphaThreshold,
-                                factor = entryIndicatorAlphaFactor,
-                                postThreshold = createSpring(200f, 1f),
-                                preThreshold = createSpring(2000f, 0.6f)
-                        ),
-                        alphaInterpolator = Step(
-                                threshold = entryIndicatorAlphaThreshold,
-                                factor = entryIndicatorAlphaFactor,
-                                postThreshold = 1f,
-                                preThreshold = 0f
-                        )
+                        alphaSpring = commonArrowDimensAlphaSpring,
+                        alphaInterpolator = commonArrowDimensAlphaSpringInterpolator
                 ),
                 backgroundDimens = BackgroundDimens(
                         alpha = 1f,
@@ -188,20 +191,6 @@
                 )
         )
 
-        val preThresholdAndActiveIndicatorAlphaThreshold = .355f
-        val preThresholdAndActiveIndicatorAlphaFactor = 1.05f
-        val preThresholdAndActiveAlphaSpring = Step(
-                threshold = preThresholdAndActiveIndicatorAlphaThreshold,
-                factor = preThresholdAndActiveIndicatorAlphaFactor,
-                postThreshold = createSpring(180f, 0.9f),
-                preThreshold = createSpring(2000f, 0.6f)
-        )
-        val preThresholdAndActiveAlphaSpringInterpolator = Step(
-                threshold = preThresholdAndActiveIndicatorAlphaThreshold,
-                factor = preThresholdAndActiveIndicatorAlphaFactor,
-                postThreshold = 1f,
-                preThreshold = 0f
-        )
         activeIndicator = BackIndicatorDimens(
                 horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
                 scale = getDimenFloat(R.dimen.navigation_edge_active_scale),
@@ -214,8 +203,8 @@
                         alpha = 1f,
                         lengthSpring = activeCommittedArrowLengthSpring,
                         heightSpring = activeCommittedArrowHeightSpring,
-                        alphaSpring = preThresholdAndActiveAlphaSpring,
-                        alphaInterpolator = preThresholdAndActiveAlphaSpringInterpolator
+                        alphaSpring = commonArrowDimensAlphaSpring,
+                        alphaInterpolator = commonArrowDimensAlphaSpringInterpolator
                 ),
                 backgroundDimens = BackgroundDimens(
                         alpha = 1f,
@@ -242,8 +231,8 @@
                         alpha = 1f,
                         lengthSpring = createSpring(100f, 0.6f),
                         heightSpring = createSpring(100f, 0.6f),
-                        alphaSpring = preThresholdAndActiveAlphaSpring,
-                        alphaInterpolator = preThresholdAndActiveAlphaSpringInterpolator
+                        alphaSpring = commonArrowDimensAlphaSpring,
+                        alphaInterpolator = commonArrowDimensAlphaSpringInterpolator
                 ),
                 backgroundDimens = BackgroundDimens(
                         alpha = 1f,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index bfaf3d0..604d449 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -93,6 +93,7 @@
             }
             dismiss()
         }
+        setCancelButtonOnClickListener { dismiss() }
         initRecordOptionsView()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 10e2afe..9a1ffcb 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -203,7 +203,10 @@
     }
 
     public void setBottomEdgeRadius(float radius) {
-        mBottomEdgeRadius = radius;
+        if (mBottomEdgeRadius != radius) {
+            mBottomEdgeRadius = radius;
+            invalidateSelf();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 784a360..047fea1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2206,6 +2206,7 @@
         // TODO: non-linearly transform progress fraction into squish amount (ease-in, linear out)
         mCurrentBackProgress = progressFraction;
         applyBackScaling(progressFraction);
+        mQsController.setClippingBounds();
     }
 
     /** Resets back progress. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 8672260..c42c2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -114,6 +114,8 @@
 public class QuickSettingsController implements Dumpable {
     public static final String TAG = "QuickSettingsController";
 
+    public static final int SHADE_BACK_ANIM_SCALE_MULTIPLIER = 100;
+
     private QS mQs;
     private final Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
 
@@ -1128,7 +1130,7 @@
      * Updates scrim bounds, QS clipping, notifications clipping and keyguard status view clipping
      * as well based on the bounds of the shade and QS state.
      */
-    private void setClippingBounds() {
+    void setClippingBounds() {
         float qsExpansionFraction = computeExpansionFraction();
         final int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction);
         // Split shade has no QQS
@@ -1216,7 +1218,10 @@
                             ? 0 : mScreenCornerRadius;
             radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
                     Math.min(top / (float) mScrimCornerRadius, 1f));
-            mScrimController.setNotificationBottomRadius(radius);
+
+            float bottomRadius = mExpanded ? screenCornerRadius :
+                    calculateBottomCornerRadius(screenCornerRadius);
+            mScrimController.setNotificationBottomRadius(bottomRadius);
         }
         if (isQsFragmentCreated()) {
             float qsTranslation = 0;
@@ -1279,6 +1284,28 @@
                 nsslLeft, nsslTop, nsslRight, nsslBottom, topRadius, bottomRadius);
     }
 
+    /**
+     * Bottom corner radius should follow screen corner radius unless
+     * predictive back is running. We want a smooth transition from screen
+     * corner radius to scrim corner radius as the notification scrim is scaled down,
+     * but the transition should be brief enough to accommodate very short back gestures.
+     */
+    @VisibleForTesting
+    int calculateBottomCornerRadius(float screenCornerRadius) {
+        return (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
+                Math.min(calculateBottomRadiusProgress(), 1f));
+    }
+
+    @VisibleForTesting
+    float calculateBottomRadiusProgress() {
+        return (1 - mScrimController.getBackScaling()) * SHADE_BACK_ANIM_SCALE_MULTIPLIER;
+    }
+
+    @VisibleForTesting
+    int getScrimCornerRadius() {
+        return mScrimCornerRadius;
+    }
+
     void setDisplayInsets(int leftInset, int rightInset) {
         mDisplayLeftInset = leftInset;
         mDisplayRightInset = rightInset;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fdb772b..25ecf1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -602,6 +602,10 @@
         mNotificationsScrim.setScaleY(scale);
     }
 
+    public float getBackScaling() {
+        return mNotificationsScrim.getScaleY();
+    }
+
     public void onTrackingStarted() {
         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
         if (!mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0414a14..1bf63be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -495,6 +495,7 @@
 
         return mKeyguardStateController.isShowing()
                 && !primaryBouncerIsOrWillBeShowing()
+                && !mKeyguardStateController.isKeyguardGoingAway()
                 && isUserTrackingStarted
                 && !hideBouncerOverDream
                 && !mKeyguardStateController.isOccluded()
diff --git a/packages/SystemUI/src/com/android/systemui/util/ActivityTaskManagerProxy.kt b/packages/SystemUI/src/com/android/systemui/util/ActivityTaskManagerProxy.kt
new file mode 100644
index 0000000..6e82cf6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/ActivityTaskManagerProxy.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.app.ActivityTaskManager
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Proxy for static calls to [ActivityTaskManager]. */
+@SysUISingleton
+class ActivityTaskManagerProxy @Inject constructor() {
+
+    /** Calls [ActivityTaskManager.supportsMultiWindow] */
+    fun supportsMultiWindow(context: Context) = ActivityTaskManager.supportsMultiWindow(context)
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index fb73845..b21cc6d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -150,6 +150,8 @@
                 .thenReturn(100);
         when(mResources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin))
                 .thenReturn(-200);
+        when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility))
+                .thenReturn(View.INVISIBLE);
 
         when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame);
         when(mView.findViewById(R.id.lockscreen_clock_view)).thenReturn(mSmallClockFrame);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index 68dc6c0..4d3243a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -15,11 +15,13 @@
  */
 package com.android.keyguard;
 
+import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.pm.PackageManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -76,12 +78,20 @@
 
     @Test
     public void refresh_replacesSliceContentAndNotifiesListener() {
+        // Skips the test if running on a watch because watches don't have a SliceManager system
+        // service.
+        assumeFalse(isWatch());
+
         mController.refresh();
         verify(mView).hideSlice();
     }
 
     @Test
     public void onAttachedToWindow_registersListeners() {
+        // Skips the test if running on a watch because watches don't have a SliceManager system
+        // service.
+        assumeFalse(isWatch());
+
         mController.init();
         verify(mTunerService).addTunable(any(TunerService.Tunable.class), anyString());
         verify(mConfigurationController).addCallback(
@@ -90,6 +100,10 @@
 
     @Test
     public void onDetachedFromWindow_unregistersListeners() {
+        // Skips the test if running on a watch because watches don't have a SliceManager system
+        // service.
+        assumeFalse(isWatch());
+
         ArgumentCaptor<View.OnAttachStateChangeListener> attachListenerArgumentCaptor =
                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
 
@@ -102,4 +116,9 @@
         verify(mConfigurationController).removeCallback(
                 any(ConfigurationController.ConfigurationListener.class));
     }
+
+    private boolean isWatch() {
+        final PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2f72cb9..419d045 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -654,6 +654,24 @@
     }
 
     @Test
+    public void serviceProvidersUpdated_broadcastTriggersInfoRefresh() {
+        // The callback is invoked once on init
+        verify(mTestCallback, times(1)).onRefreshCarrierInfo();
+
+        // WHEN the SERVICE_PROVIDERS_UPDATED broadcast is sent
+        Intent intent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+        intent.putExtra(TelephonyManager.EXTRA_SPN, "spn");
+        intent.putExtra(TelephonyManager.EXTRA_PLMN, "plmn");
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
+                putPhoneInfo(intent, null, true));
+        mTestableLooper.processAllMessages();
+
+        // THEN verify keyguardUpdateMonitorCallback receives a refresh callback
+        // Note that we have times(2) here because it's been called once already
+        verify(mTestCallback, times(2)).onRefreshCarrierInfo();
+    }
+
+    @Test
     public void testTriesToAuthenticateFingerprint_whenKeyguard() {
         mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 9f5c181..374c28d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -82,7 +82,7 @@
             assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
 
             // Wrong input.
-            underTest.authenticate(listOf(9, 8, 7))
+            assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse()
             assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
 
@@ -90,7 +90,7 @@
             assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
 
             // Correct input.
-            underTest.authenticate(listOf(1, 2, 3, 4))
+            assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
         }
 
@@ -114,7 +114,7 @@
             assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
 
             // Wrong input.
-            underTest.authenticate("alohamora".toList())
+            assertThat(underTest.authenticate("alohamora".toList())).isFalse()
             assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
 
@@ -122,7 +122,7 @@
             assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
 
             // Correct input.
-            underTest.authenticate("password".toList())
+            assertThat(underTest.authenticate("password".toList())).isTrue()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
         }
 
@@ -146,9 +146,12 @@
             assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
 
             // Wrong input.
-            underTest.authenticate(
-                listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(3, 4))
-            )
+            assertThat(
+                    underTest.authenticate(
+                        listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(3, 4))
+                    )
+                )
+                .isFalse()
             assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
 
@@ -156,7 +159,7 @@
             assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
 
             // Correct input.
-            underTest.authenticate(emptyList())
+            assertThat(underTest.authenticate(emptyList())).isTrue()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
         }
 
@@ -214,7 +217,7 @@
             assertThat(isUnlocked).isFalse()
             repeat(BouncerInteractor.THROTTLE_EVERY) { times ->
                 // Wrong PIN.
-                underTest.authenticate(listOf(6, 7, 8, 9))
+                assertThat(underTest.authenticate(listOf(6, 7, 8, 9))).isFalse()
                 if (times < BouncerInteractor.THROTTLE_EVERY - 1) {
                     assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
                 }
@@ -223,7 +226,7 @@
             assertTryAgainMessage(message, BouncerInteractor.THROTTLE_DURATION_SEC)
 
             // Correct PIN, but throttled, so doesn't unlock:
-            underTest.authenticate(listOf(1, 2, 3, 4))
+            assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isFalse()
             assertThat(isUnlocked).isFalse()
             assertTryAgainMessage(message, BouncerInteractor.THROTTLE_DURATION_SEC)
 
@@ -241,7 +244,7 @@
             assertThat(isUnlocked).isFalse()
 
             // Correct PIN and no longer throttled so unlocks:
-            underTest.authenticate(listOf(1, 2, 3, 4))
+            assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
             assertThat(isUnlocked).isTrue()
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
new file mode 100644
index 0000000..1642410
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.bouncer.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.scene.SceneTestUtils
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class AuthMethodBouncerViewModelTest : SysuiTestCase() {
+
+    private val testScope = TestScope()
+    private val utils = SceneTestUtils(this, testScope)
+    private val authenticationInteractor =
+        utils.authenticationInteractor(
+            utils.authenticationRepository(),
+        )
+    private val underTest =
+        PinBouncerViewModel(
+            applicationScope = testScope.backgroundScope,
+            interactor =
+                utils.bouncerInteractor(
+                    authenticationInteractor = authenticationInteractor,
+                    sceneInteractor = utils.sceneInteractor(),
+                ),
+            isInputEnabled = MutableStateFlow(true),
+        )
+
+    @Test
+    fun animateFailure() =
+        testScope.runTest {
+            authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+            val animateFailure by collectLastValue(underTest.animateFailure)
+            assertThat(animateFailure).isFalse()
+
+            // Wrong PIN:
+            underTest.onPinButtonClicked(3)
+            underTest.onPinButtonClicked(4)
+            underTest.onPinButtonClicked(5)
+            underTest.onPinButtonClicked(6)
+            underTest.onAuthenticateButtonClicked()
+            assertThat(animateFailure).isTrue()
+
+            underTest.onFailureAnimationShown()
+            assertThat(animateFailure).isFalse()
+
+            // Correct PIN:
+            underTest.onPinButtonClicked(1)
+            underTest.onPinButtonClicked(2)
+            underTest.onPinButtonClicked(3)
+            underTest.onPinButtonClicked(4)
+            underTest.onAuthenticateButtonClicked()
+            assertThat(animateFailure).isFalse()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index b942ccb..e8c946c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -93,22 +93,21 @@
     }
 
     @Test
-    fun isMessageUpdateAnimationsEnabled() =
+    fun message() =
         testScope.runTest {
-            val isMessageUpdateAnimationsEnabled by
-                collectLastValue(underTest.isMessageUpdateAnimationsEnabled)
+            val message by collectLastValue(underTest.message)
             val throttling by collectLastValue(bouncerInteractor.throttling)
             authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
-            assertThat(isMessageUpdateAnimationsEnabled).isTrue()
+            assertThat(message?.isUpdateAnimated).isTrue()
 
             repeat(BouncerInteractor.THROTTLE_EVERY) {
                 // Wrong PIN.
                 bouncerInteractor.authenticate(listOf(3, 4, 5, 6))
             }
-            assertThat(isMessageUpdateAnimationsEnabled).isFalse()
+            assertThat(message?.isUpdateAnimated).isFalse()
 
             throttling?.totalDurationSec?.let { seconds -> advanceTimeBy(seconds * 1000L) }
-            assertThat(isMessageUpdateAnimationsEnabled).isTrue()
+            assertThat(message?.isUpdateAnimated).isTrue()
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index b7b90de..f436aa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -85,7 +85,7 @@
 
             underTest.onShown()
 
-            assertThat(message).isEqualTo(ENTER_YOUR_PASSWORD)
+            assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
             assertThat(password).isEqualTo("")
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -109,7 +109,7 @@
 
             underTest.onPasswordInputChanged("password")
 
-            assertThat(message).isEmpty()
+            assertThat(message?.text).isEmpty()
             assertThat(password).isEqualTo("password")
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -156,7 +156,7 @@
             underTest.onAuthenticateKeyPressed()
 
             assertThat(password).isEqualTo("")
-            assertThat(message).isEqualTo(WRONG_PASSWORD)
+            assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
         }
@@ -179,13 +179,13 @@
             underTest.onPasswordInputChanged("wrong")
             underTest.onAuthenticateKeyPressed()
             assertThat(password).isEqualTo("")
-            assertThat(message).isEqualTo(WRONG_PASSWORD)
+            assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
 
             // Enter the correct password:
             underTest.onPasswordInputChanged("password")
-            assertThat(message).isEmpty()
+            assertThat(message?.text).isEmpty()
 
             underTest.onAuthenticateKeyPressed()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index b588ba2..d7d7154 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -89,7 +89,7 @@
 
             underTest.onShown()
 
-            assertThat(message).isEqualTo(ENTER_YOUR_PATTERN)
+            assertThat(message?.text).isEqualTo(ENTER_YOUR_PATTERN)
             assertThat(selectedDots).isEmpty()
             assertThat(currentDot).isNull()
             assertThat(isUnlocked).isFalse()
@@ -115,7 +115,7 @@
 
             underTest.onDragStart()
 
-            assertThat(message).isEmpty()
+            assertThat(message?.text).isEmpty()
             assertThat(selectedDots).isEmpty()
             assertThat(currentDot).isNull()
             assertThat(isUnlocked).isFalse()
@@ -202,7 +202,7 @@
 
             assertThat(selectedDots).isEmpty()
             assertThat(currentDot).isNull()
-            assertThat(message).isEqualTo(WRONG_PATTERN)
+            assertThat(message?.text).isEqualTo(WRONG_PATTERN)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
         }
@@ -235,7 +235,7 @@
             underTest.onDragEnd()
             assertThat(selectedDots).isEmpty()
             assertThat(currentDot).isNull()
-            assertThat(message).isEqualTo(WRONG_PATTERN)
+            assertThat(message?.text).isEqualTo(WRONG_PATTERN)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 83f9687..3bdaf05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -94,7 +94,7 @@
 
             underTest.onShown()
 
-            assertThat(message).isEqualTo(ENTER_YOUR_PIN)
+            assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN)
             assertThat(pinLengths).isEqualTo(0 to 0)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -116,7 +116,7 @@
 
             underTest.onPinButtonClicked(1)
 
-            assertThat(message).isEmpty()
+            assertThat(message?.text).isEmpty()
             assertThat(pinLengths).isEqualTo(0 to 1)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -140,7 +140,7 @@
 
             underTest.onBackspaceButtonClicked()
 
-            assertThat(message).isEmpty()
+            assertThat(message?.text).isEmpty()
             assertThat(pinLengths).isEqualTo(1 to 0)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -170,7 +170,7 @@
                 advanceTimeBy(PinBouncerViewModel.BACKSPACE_LONG_PRESS_DELAY_MS)
             }
 
-            assertThat(message).isEmpty()
+            assertThat(message?.text).isEmpty()
             assertThat(pinLengths).isEqualTo(1 to 0)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -220,7 +220,7 @@
             underTest.onAuthenticateButtonClicked()
 
             assertThat(pinLengths).isEqualTo(0 to 0)
-            assertThat(message).isEqualTo(WRONG_PIN)
+            assertThat(message?.text).isEqualTo(WRONG_PIN)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
         }
@@ -244,7 +244,7 @@
             underTest.onPinButtonClicked(4)
             underTest.onPinButtonClicked(5) // PIN is now wrong!
             underTest.onAuthenticateButtonClicked()
-            assertThat(message).isEqualTo(WRONG_PIN)
+            assertThat(message?.text).isEqualTo(WRONG_PIN)
             assertThat(pinLengths).isEqualTo(0 to 0)
             assertThat(isUnlocked).isFalse()
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -254,7 +254,7 @@
             underTest.onPinButtonClicked(2)
             underTest.onPinButtonClicked(3)
             underTest.onPinButtonClicked(4)
-            assertThat(message).isEmpty()
+            assertThat(message?.text).isEmpty()
 
             underTest.onAuthenticateButtonClicked()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 10bfc1b..ee213f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.flags.Flags.APP_PANELS_ALL_APPS_ALLOWED
 import com.android.systemui.flags.Flags.USE_APP_PANELS
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.ActivityTaskManagerProxy
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argThat
@@ -88,6 +89,8 @@
     private lateinit var packageManager: PackageManager
     @Mock
     private lateinit var featureFlags: FeatureFlags
+    @Mock
+    private lateinit var activityTaskManagerProxy: ActivityTaskManagerProxy
 
     private var componentName = ComponentName("pkg", "class1")
     private var activityName = ComponentName("pkg", "activity")
@@ -112,6 +115,7 @@
         // Return disabled by default
         `when`(packageManager.getComponentEnabledSetting(any()))
                 .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
+        `when`(activityTaskManagerProxy.supportsMultiWindow(any())).thenReturn(true)
         mContext.setMockPackageManager(packageManager)
 
         mContext.orCreateTestableResources
@@ -136,6 +140,7 @@
                 executor,
                 { mockSL },
                 userTracker,
+                activityTaskManagerProxy,
                 dumpManager,
                 featureFlags
         )
@@ -171,6 +176,7 @@
                 exec,
                 { mockServiceListing },
                 userTracker,
+                activityTaskManagerProxy,
                 dumpManager,
                 featureFlags
         )
@@ -637,7 +643,34 @@
         assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
     }
 
+    @Test
+    fun testNoPanelIfMultiWindowNotSupported() {
+        `when`(activityTaskManagerProxy.supportsMultiWindow(any())).thenReturn(false)
 
+        val serviceInfo = ServiceInfo(
+            componentName,
+            activityName
+        )
+
+        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+        setUpQueryResult(listOf(
+            ActivityInfo(
+                activityName,
+                enabled = true,
+                exported = true,
+                permission = Manifest.permission.BIND_CONTROLS
+            )
+        ))
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
 
     private fun ServiceInfo(
             componentName: ComponentName,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 688c2db..477e076 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -10,6 +10,7 @@
 import android.view.RemoteAnimationTarget
 import android.view.SurfaceControl
 import android.view.SyncRtSurfaceTransactionApplier
+import android.view.View
 import android.view.ViewRootImpl
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardViewController
@@ -32,6 +33,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito.atLeastOnce
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
@@ -374,6 +376,83 @@
         verifyNoMoreInteractions(surfaceTransactionApplier)
     }
 
+    @Test
+    fun unlockToLauncherWithInWindowAnimations_ssViewIsVisible() {
+        val mockLockscreenSmartspaceView = mock(View::class.java)
+        whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE)
+        keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
+
+        keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
+
+        verify(mockLockscreenSmartspaceView).visibility = View.INVISIBLE
+    }
+
+    @Test
+    fun unlockToLauncherWithInWindowAnimations_ssViewIsInvisible() {
+        val mockLockscreenSmartspaceView = mock(View::class.java)
+        whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
+        keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
+
+        keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
+
+        verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
+    }
+
+    @Test
+    fun unlockToLauncherWithInWindowAnimations_ssViewIsGone() {
+        val mockLockscreenSmartspaceView = mock(View::class.java)
+        whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
+        keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
+
+        keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
+
+        verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
+    }
+
+    @Test
+    fun notifyFinishedKeyguardExitAnimation_ssViewIsInvisibleAndCancelledIsTrue() {
+        val mockLockscreenSmartspaceView = mock(View::class.java)
+        whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
+        keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
+
+        keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
+
+        verify(mockLockscreenSmartspaceView).visibility = View.VISIBLE
+    }
+
+    @Test
+    fun notifyFinishedKeyguardExitAnimation_ssViewIsGoneAndCancelledIsTrue() {
+        val mockLockscreenSmartspaceView = mock(View::class.java)
+        whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
+        keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
+
+        keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
+
+        verify(mockLockscreenSmartspaceView, never()).visibility = View.VISIBLE
+    }
+
+    @Test
+    fun notifyFinishedKeyguardExitAnimation_ssViewIsInvisibleAndCancelledIsFalse() {
+        val mockLockscreenSmartspaceView = mock(View::class.java)
+        whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
+        keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
+
+        keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(false)
+
+        verify(mockLockscreenSmartspaceView).visibility = View.VISIBLE
+    }
+
+    @Test
+    fun notifyFinishedKeyguardExitAnimation_ssViewIsGoneAndCancelledIsFalse() {
+        val mockLockscreenSmartspaceView = mock(View::class.java)
+        whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
+        keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
+
+        keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(false)
+
+        verify(mockLockscreenSmartspaceView, never()).visibility = View.VISIBLE
+    }
+
     private class ArgThatCaptor<T> {
         private var allArgs: MutableList<T> = mutableListOf()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 9200d72..de3bb6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -72,6 +72,8 @@
         val resources: Resources = mock()
         whenever(resources.getStringArray(R.array.config_keyguardQuickAffordanceDefaults))
             .thenReturn(emptyArray())
+        whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled))
+            .thenReturn(true)
         whenever(context.resources).thenReturn(resources)
 
         testDispatcher = UnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index bad4b36..b2528c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -66,6 +66,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true)
         sharedPrefs = mutableMapOf()
         whenever(userFileManager.getSharedPreferences(anyString(), anyInt(), anyInt())).thenAnswer {
             val userId = it.arguments[2] as Int
@@ -86,6 +87,13 @@
 
     @After
     fun tearDown() {
+        mContext
+            .getOrCreateTestableResources()
+            .removeOverride(R.bool.custom_lockscreen_shortcuts_enabled)
+        mContext
+            .getOrCreateTestableResources()
+            .removeOverride(R.array.config_keyguardQuickAffordanceDefaults)
+
         Dispatchers.resetMain()
     }
 
@@ -358,6 +366,22 @@
         job.cancel()
     }
 
+    @Test
+    fun getSelections_alwaysReturnsDefaultsIfCustomShortcutsFeatureDisabled() {
+        overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, false)
+        overrideResource(
+            R.array.config_keyguardQuickAffordanceDefaults,
+            arrayOf("leftTest:testShortcut1", "rightTest:testShortcut2")
+        )
+
+        assertThat(underTest.getSelections()).isEqualTo(
+            mapOf(
+                "leftTest" to listOf("testShortcut1"),
+                "rightTest" to listOf("testShortcut2"),
+            )
+        )
+    }
+
     private fun assertSelections(
         observed: Map<String, List<String>>?,
         expected: Map<String, List<String>>,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 8dc04bd..ca7c5db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -47,6 +47,7 @@
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,6 +71,7 @@
 
     @Before
     fun setUp() {
+        overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true)
         context.resources.configuration.setLayoutDirection(Locale.US)
         config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
         config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
@@ -137,6 +139,13 @@
             )
     }
 
+    @After
+    fun tearDown() {
+        mContext
+            .getOrCreateTestableResources()
+            .removeOverride(R.bool.custom_lockscreen_shortcuts_enabled)
+    }
+
     @Test
     fun setSelections() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 5d2c3ed..895c1cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -102,6 +102,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true)
+
         repository = FakeKeyguardRepository()
         repository.setKeyguardShowing(true)
 
@@ -200,7 +202,7 @@
                 devicePolicyManager = devicePolicyManager,
                 dockManager = dockManager,
                 backgroundDispatcher = testDispatcher,
-                appContext = mContext,
+                appContext = context,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index 5b094c9..07feedf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -104,6 +104,26 @@
         assertThat(visibility).isEqualTo(View.VISIBLE)
     }
 
+    @Test
+    fun showDialog_dialogIsShowing() {
+        dialog.show()
+
+        assertThat(dialog.isShowing).isTrue()
+    }
+
+    @Test
+    fun showDialog_cancelClicked_dialogIsDismissed() {
+        dialog.show()
+
+        clickOnCancel()
+
+        assertThat(dialog.isShowing).isFalse()
+    }
+
+    private fun clickOnCancel() {
+        dialog.requireViewById<View>(android.R.id.button2).performClick()
+    }
+
     private fun onSpinnerItemSelected(position: Int) {
         val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
         spinner.onItemSelectedListener.onItemSelected(spinner, mock(), position, /* id= */ 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index 34d09a9..ff047aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -580,6 +580,30 @@
         verify(mQs).setQsVisible(true);
     }
 
+    @Test
+    public void calculateBottomCornerRadius_scrimScaleMax() {
+        when(mScrimController.getBackScaling()).thenReturn(1.0f);
+        assertThat(mQsController.calculateBottomCornerRadius(0.0f)).isEqualTo(0);
+    }
+
+    @Test
+    public void calculateBottomCornerRadius_scrimScaleMin() {
+        when(mScrimController.getBackScaling())
+                .thenReturn(mNotificationPanelViewController.SHADE_BACK_ANIM_MIN_SCALE);
+        assertThat(mQsController.calculateBottomCornerRadius(0.0f))
+                .isEqualTo(mQsController.getScrimCornerRadius());
+    }
+
+    @Test
+    public void calculateBottomCornerRadius_scrimScaleCutoff() {
+        float ratio = 1 / mQsController.calculateBottomRadiusProgress();
+        float cutoffScale = 1 - mNotificationPanelViewController.SHADE_BACK_ANIM_MIN_SCALE / ratio;
+        when(mScrimController.getBackScaling())
+                .thenReturn(cutoffScale);
+        assertThat(mQsController.calculateBottomCornerRadius(0.0f))
+                .isEqualTo(mQsController.getScrimCornerRadius());
+    }
+
     private void lockScreen() {
         mQsController.setBarState(KEYGUARD);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index e56f0d6..3eea93c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -352,6 +352,17 @@
     }
 
     @Test
+    public void onPanelExpansionChanged_neverTranslatesBouncerWhenGoingAway() {
+        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
+        mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+                expansionEvent(
+                        /* fraction= */ EXPANSION_VISIBLE,
+                        /* expanded= */ true,
+                        /* tracking= */ false));
+        verify(mPrimaryBouncerInteractor, never()).setPanelExpansion(anyFloat());
+    }
+
+    @Test
     public void onPanelExpansionChanged_neverTranslatesBouncerWhenShowBouncer() {
         // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
         // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index f31ca81..c2ebddf 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -2057,7 +2057,11 @@
             mIsImageValid = false;
 
             if (mGraphicBuffer != null) {
-                ImageReader.unlockGraphicBuffer(mGraphicBuffer);
+                try {
+                    ImageReader.unlockGraphicBuffer(mGraphicBuffer);
+                } catch (RuntimeException e) {
+                    e.printStackTrace();
+                }
                 mGraphicBuffer.destroy();
                 mGraphicBuffer = null;
             }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index fc758cb..1a57bc1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -761,6 +761,18 @@
     }
 
     // Called by Shell command
+    boolean isFieldDetectionServiceEnabledForUser(@UserIdInt int userId) {
+        enforceCallingPermissionForManagement();
+        synchronized (mLock) {
+            final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+            if (service != null) {
+                return service.isPccClassificationEnabled();
+            }
+        }
+        return false;
+    }
+
+    // Called by Shell command
     String getFieldDetectionServiceName(@UserIdInt int userId) {
         enforceCallingPermissionForManagement();
         return mFieldClassificationResolver.readServiceName(userId);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index cd6de87..c66fb81 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -26,7 +26,6 @@
 import android.os.ShellCommand;
 import android.os.UserHandle;
 import android.service.autofill.AutofillFieldClassificationService.Scores;
-import android.text.TextUtils;
 import android.view.autofill.AutofillManager;
 
 import com.android.internal.os.IResultReceiver;
@@ -348,9 +347,7 @@
 
     private int isFieldDetectionServiceEnabled(PrintWriter pw) {
         final int userId = getNextIntArgRequired();
-        String name = mService.getFieldDetectionServiceName(userId);
-        boolean pccFlagEnabled = mService.isPccClassificationFlagEnabled();
-        boolean enabled = (!TextUtils.isEmpty(name)) && pccFlagEnabled;
+        boolean enabled = mService.isFieldDetectionServiceEnabledForUser(userId);
         pw.println(enabled);
         return 0;
     }
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionRendorInfoCallbackOnResultListener.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionRendorInfoCallbackOnResultListener.java
new file mode 100644
index 0000000..7351ef5
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionRendorInfoCallbackOnResultListener.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 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.server.autofill;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+
+final class InlineSuggestionRendorInfoCallbackOnResultListener implements
+        RemoteCallback.OnResultListener{
+    private static final String TAG = "InlineSuggestionRendorInfoCallbackOnResultListener";
+
+    private final int mRequestIdCopy;
+    private final AutofillId mFocusedId;
+    private final WeakReference<Session> mSessionWeakReference;
+    private final Consumer<InlineSuggestionsRequest> mInlineSuggestionsRequestConsumer;
+
+    InlineSuggestionRendorInfoCallbackOnResultListener(WeakReference<Session> sessionWeakReference,
+            int requestIdCopy,
+            Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer,
+            AutofillId focusedId) {
+        this.mRequestIdCopy = requestIdCopy;
+        this.mInlineSuggestionsRequestConsumer = inlineSuggestionsRequestConsumer;
+        this.mSessionWeakReference = sessionWeakReference;
+        this.mFocusedId = focusedId;
+    }
+    public void onResult(@Nullable Bundle result) {
+        Session session = this.mSessionWeakReference.get();
+        if (session == null) {
+            Slog.wtf(TAG, "Session is null before trying to call onResult");
+            return;
+        }
+        synchronized (session.mLock) {
+            if (session.mDestroyed) {
+                Slog.wtf(TAG, "Session is destroyed before trying to call onResult");
+                return;
+            }
+            session.mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
+                    this.mFocusedId,
+                    session.inlineSuggestionsRequestCacheDecorator(
+                        this.mInlineSuggestionsRequestConsumer, this.mRequestIdCopy),
+                    result);
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java
new file mode 100644
index 0000000..a3efb25
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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.server.autofill;
+
+import android.util.Slog;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+
+class InlineSuggestionRequestConsumer implements Consumer<InlineSuggestionsRequest> {
+
+    static final String TAG = "InlineSuggestionRequestConsumer";
+
+    private final WeakReference<Session.AssistDataReceiverImpl> mAssistDataReceiverWeakReference;
+    private final WeakReference<ViewState>  mViewStateWeakReference;
+
+    InlineSuggestionRequestConsumer(WeakReference<Session.AssistDataReceiverImpl>
+            assistDataReceiverWeakReference,
+            WeakReference<ViewState>  viewStateWeakReference) {
+        mAssistDataReceiverWeakReference = assistDataReceiverWeakReference;
+        mViewStateWeakReference = viewStateWeakReference;
+    }
+
+    @Override
+    public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) {
+        Session.AssistDataReceiverImpl assistDataReceiver = mAssistDataReceiverWeakReference.get();
+        ViewState viewState = mViewStateWeakReference.get();
+        if (assistDataReceiver == null) {
+            Slog.wtf(TAG, "assistDataReceiver is null when accepting new inline suggestion"
+                    + "requests");
+            return;
+        }
+
+        if (viewState == null) {
+            Slog.wtf(TAG, "view state is null when accepting new inline suggestion requests");
+            return;
+        }
+        assistDataReceiver.handleInlineSuggestionRequest(inlineSuggestionsRequest, viewState);
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0a8f474..4576abb 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -184,6 +184,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -224,6 +225,7 @@
     private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID";
 
     private static final String PCC_HINTS_DELIMITER = ",";
+    public static final String EXTRA_KEY_DETECTIONS = "detections";
 
     final Object mLock;
 
@@ -321,7 +323,7 @@
      * Id of the View currently being displayed.
      */
     @GuardedBy("mLock")
-    @Nullable private AutofillId mCurrentViewId;
+    private @Nullable AutofillId mCurrentViewId;
 
     @GuardedBy("mLock")
     private IAutoFillManagerClient mClient;
@@ -369,7 +371,7 @@
     private Bundle mClientState;
 
     @GuardedBy("mLock")
-    private boolean mDestroyed;
+    boolean mDestroyed;
 
     /**
      * Helper used to handle state of Save UI when it must be hiding to show a custom description
@@ -448,7 +450,7 @@
     private ArrayList<AutofillId> mAugmentedAutofillableIds;
 
     @NonNull
-    private final AutofillInlineSessionController mInlineSessionController;
+    final AutofillInlineSessionController mInlineSessionController;
 
     /**
      * Receiver of assist data from the app's {@link Activity}.
@@ -612,7 +614,7 @@
      * TODO(b/151867668): improve how asynchronous data dependencies are handled, without using
      * CountDownLatch.
      */
-    private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
+    final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
         @GuardedBy("mLock")
         private boolean mWaitForInlineRequest;
         @GuardedBy("mLock")
@@ -627,17 +629,28 @@
             mPendingFillRequest = null;
             mWaitForInlineRequest = isInlineRequest;
             mPendingInlineSuggestionsRequest = null;
-            return isInlineRequest ? (inlineSuggestionsRequest) -> {
-                synchronized (mLock) {
-                    if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) {
-                        return;
-                    }
-                    mWaitForInlineRequest = inlineSuggestionsRequest != null;
-                    mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
-                    maybeRequestFillLocked();
-                    viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+            if (isInlineRequest) {
+                WeakReference<AssistDataReceiverImpl> assistDataReceiverWeakReference =
+                        new WeakReference<AssistDataReceiverImpl>(this);
+                WeakReference<ViewState> viewStateWeakReference =
+                        new WeakReference<ViewState>(viewState);
+                return new InlineSuggestionRequestConsumer(assistDataReceiverWeakReference,
+                    viewStateWeakReference);
+            }
+            return null;
+        }
+
+        void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest,
+                ViewState viewState) {
+            synchronized (mLock) {
+                if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) {
+                    return;
                 }
-            } : null;
+                mWaitForInlineRequest = inlineSuggestionsRequest != null;
+                mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
+                maybeRequestFillLocked();
+                viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+            }
         }
 
         @GuardedBy("mLock")
@@ -1224,24 +1237,30 @@
         final RemoteInlineSuggestionRenderService remoteRenderService =
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
         if (mSessionFlags.mInlineSupportedByService
-            && remoteRenderService != null
-            && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
+                && remoteRenderService != null
+                && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
+
             Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
                 mAssistReceiver.newAutofillRequestLocked(viewState,
                     /* isInlineRequest= */ true);
+
             if (inlineSuggestionsRequestConsumer != null) {
-                final AutofillId focusedId = mCurrentViewId;
                 final int requestIdCopy = requestId;
+                final AutofillId focusedId = mCurrentViewId;
+
+                WeakReference sessionWeakReference = new WeakReference<Session>(this);
+                InlineSuggestionRendorInfoCallbackOnResultListener
+                        inlineSuggestionRendorInfoCallbackOnResultListener =
+                                new InlineSuggestionRendorInfoCallbackOnResultListener(
+                                        sessionWeakReference,
+                                        requestIdCopy,
+                                        inlineSuggestionsRequestConsumer,
+                                        focusedId);
+                RemoteCallback inlineSuggestionRendorInfoCallback = new RemoteCallback(
+                        inlineSuggestionRendorInfoCallbackOnResultListener, mHandler);
+
                 remoteRenderService.getInlineSuggestionsRendererInfo(
-                        new RemoteCallback((extras) -> {
-                            synchronized (mLock) {
-                                mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
-                                        focusedId, inlineSuggestionsRequestCacheDecorator(
-                                                inlineSuggestionsRequestConsumer, requestIdCopy),
-                                        extras);
-                            }
-                        }, mHandler)
-                );
+                        inlineSuggestionRendorInfoCallback);
                 viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
             }
         } else {
@@ -3646,6 +3665,17 @@
 
         final ArrayList<FillContext> contexts = mergePreviousSessionLocked( /* forSave= */ true);
 
+        FieldClassificationResponse fieldClassificationResponse =
+                mClassificationState.mLastFieldClassificationResponse;
+        if (mService.isPccClassificationEnabled()
+                && fieldClassificationResponse != null
+                && !fieldClassificationResponse.getClassifications().isEmpty()) {
+            if (mClientState == null) {
+                mClientState = new Bundle();
+            }
+            mClientState.putParcelableArrayList(EXTRA_KEY_DETECTIONS, new ArrayList<>(
+                    fieldClassificationResponse.getClassifications()));
+        }
         final SaveRequest saveRequest =
                 new SaveRequest(contexts, mClientState, mSelectedDatasetIds);
         mRemoteFillService.onSaveRequest(saveRequest);
@@ -5151,7 +5181,7 @@
     }
 
     @NonNull
-    private Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestCacheDecorator(
+    Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestCacheDecorator(
             @NonNull Consumer<InlineSuggestionsRequest> consumer, int requestId) {
         return inlineSuggestionsRequest -> {
             consumer.accept(inlineSuggestionsRequest);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 31d60f2..00dbb97 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3797,7 +3797,9 @@
             }
             clientPsr.addConnection(c);
             c.startAssociationIfNeeded();
-            if (c.hasFlag(Context.BIND_ABOVE_CLIENT)) {
+            // Don't set hasAboveClient if binding to self to prevent modifyRawOomAdj() from
+            // dropping the process' adjustment level.
+            if (b.client != s.app && c.hasFlag(Context.BIND_ABOVE_CLIENT)) {
                 clientPsr.setHasAboveClient(true);
             }
             if (c.hasFlag(BIND_ALLOW_WHITELIST_MANAGEMENT)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3d02c96..460ce44 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3393,7 +3393,7 @@
                 mProcessList.noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
                         ApplicationExitInfo.SUBREASON_UNKNOWN, reason);
             }
-            ProcessList.killProcessGroup(app.uid, pid);
+            app.killProcessGroupIfNecessaryLocked(true);
             synchronized (mProcLock) {
                 app.setKilled(true);
             }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 85a0185..6c3f01e 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -3253,7 +3253,11 @@
                 // {@link SCHED_GROUP_TOP_APP}. We don't check render thread because it
                 // is not ready when attaching.
                 app.getWindowProcessController().onTopProcChanged();
-                setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
+                if (mService.mUseFifoUiScheduling) {
+                    mService.scheduleAsFifoPriority(app.getPid(), true);
+                } else {
+                    setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
+                }
                 initialSchedGroup = SCHED_GROUP_TOP_APP;
             } catch (Exception e) {
                 Slog.w(TAG, "Failed to pre-set top priority to " + app + " " + e);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 335d676..d6495c7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1197,26 +1197,7 @@
                 EventLog.writeEvent(EventLogTags.AM_KILL,
                         userId, mPid, processName, mState.getSetAdj(), reason);
                 Process.killProcessQuiet(mPid);
-                final boolean killProcessGroup;
-                if (mHostingRecord != null
-                        && (mHostingRecord.usesWebviewZygote() || mHostingRecord.usesAppZygote())) {
-                    synchronized (ProcessRecord.this) {
-                        killProcessGroup = mProcessGroupCreated;
-                        if (!killProcessGroup) {
-                            // The process group hasn't been created, request to skip it.
-                            mSkipProcessGroupCreation = true;
-                        }
-                    }
-                } else {
-                    killProcessGroup = true;
-                }
-                if (killProcessGroup) {
-                    if (asyncKPG) {
-                        ProcessList.killProcessGroup(uid, mPid);
-                    } else {
-                        Process.sendSignalToProcessGroup(uid, mPid, OsConstants.SIGKILL);
-                    }
-                }
+                killProcessGroupIfNecessaryLocked(asyncKPG);
             } else {
                 mPendingStart = false;
             }
@@ -1231,6 +1212,30 @@
         }
     }
 
+    @GuardedBy("mService")
+    void killProcessGroupIfNecessaryLocked(boolean async) {
+        final boolean killProcessGroup;
+        if (mHostingRecord != null
+                && (mHostingRecord.usesWebviewZygote() || mHostingRecord.usesAppZygote())) {
+            synchronized (ProcessRecord.this) {
+                killProcessGroup = mProcessGroupCreated;
+                if (!killProcessGroup) {
+                    // The process group hasn't been created, request to skip it.
+                    mSkipProcessGroupCreation = true;
+                }
+            }
+        } else {
+            killProcessGroup = true;
+        }
+        if (killProcessGroup) {
+            if (async) {
+                ProcessList.killProcessGroup(uid, mPid);
+            } else {
+                Process.sendSignalToProcessGroup(uid, mPid, OsConstants.SIGKILL);
+            }
+        }
+    }
+
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         dumpDebug(proto, fieldId, -1);
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 81d0b6a..7ff6d11 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -341,7 +341,8 @@
         mHasAboveClient = false;
         for (int i = mConnections.size() - 1; i >= 0; i--) {
             ConnectionRecord cr = mConnections.valueAt(i);
-            if (cr.hasFlag(Context.BIND_ABOVE_CLIENT)) {
+            if (cr.binding.service.app.mServices != this
+                    && cr.hasFlag(Context.BIND_ABOVE_CLIENT)) {
                 mHasAboveClient = true;
                 break;
             }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 12bb5d2..76a994e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -151,6 +151,8 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
@@ -1707,7 +1709,8 @@
                     mInjector.getWindowManager().setSwitchingUser(true);
                     // Only lock if the user has a secure keyguard PIN/Pattern/Pwd
                     if (mInjector.getKeyguardManager().isDeviceSecure(userId)) {
-                        mInjector.getWindowManager().lockNow(null);
+                        // Make sure the device is locked before moving on with the user switch
+                        mInjector.lockDeviceNowAndWaitForKeyguardShown();
                     }
                 }
 
@@ -3444,6 +3447,11 @@
         WindowManagerService getWindowManager() {
             return mService.mWindowManager;
         }
+
+        ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+            return mService.mAtmInternal;
+        }
+
         void activityManagerOnUserStopped(@UserIdInt int userId) {
             LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId);
         }
@@ -3667,5 +3675,43 @@
         void onSystemUserVisibilityChanged(boolean visible) {
             getUserManagerInternal().onSystemUserVisibilityChanged(visible);
         }
+
+        void lockDeviceNowAndWaitForKeyguardShown() {
+            if (getWindowManager().isKeyguardLocked()) {
+                return;
+            }
+
+            final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+            t.traceBegin("lockDeviceNowAndWaitForKeyguardShown");
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            ActivityTaskManagerInternal.ScreenObserver screenObserver =
+                    new ActivityTaskManagerInternal.ScreenObserver() {
+                        @Override
+                        public void onAwakeStateChanged(boolean isAwake) {
+
+                        }
+
+                        @Override
+                        public void onKeyguardStateChanged(boolean isShowing) {
+                            if (isShowing) {
+                                latch.countDown();
+                            }
+                        }
+                    };
+
+            getActivityTaskManagerInternal().registerScreenObserver(screenObserver);
+            getWindowManager().lockDeviceNow();
+            try {
+                if (!latch.await(20, TimeUnit.SECONDS)) {
+                    throw new RuntimeException("Keyguard is not shown in 20 seconds");
+                }
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            } finally {
+                getActivityTaskManagerInternal().unregisterScreenObserver(screenObserver);
+                t.traceEnd();
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index 21ade1b..fc3d7c8 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -156,7 +156,7 @@
     @Override
     public OperationContextExt updateContext(@NonNull OperationContextExt operationContext,
             boolean isCryptoOperation) {
-        return operationContext.update(this);
+        return operationContext.update(this, isCryptoOperation);
     }
 
     @Nullable
@@ -238,7 +238,7 @@
 
     private void notifySubscribers() {
         mSubscribers.forEach((context, consumer) -> {
-            consumer.accept(context.update(this).toAidlContext());
+            consumer.accept(context.update(this, context.isCrypto()).toAidlContext());
         });
     }
 
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
index 4d821e9..4a10e8e 100644
--- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -241,9 +241,10 @@
     }
 
     /** Update this object with the latest values from the given context. */
-    OperationContextExt update(@NonNull BiometricContext biometricContext) {
+    OperationContextExt update(@NonNull BiometricContext biometricContext, boolean isCrypto) {
         mAidlContext.isAod = biometricContext.isAod();
         mAidlContext.displayState = toAidlDisplayState(biometricContext.getDisplayState());
+        mAidlContext.isCrypto = isCrypto;
         setFirstSessionId(biometricContext);
 
         mIsDisplayOn = biometricContext.isDisplayOn();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index b474cad..aa5f9fa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -32,6 +32,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
 import com.android.server.biometrics.BiometricSchedulerProto;
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
@@ -569,9 +570,10 @@
         if (mCurrentOperation == null) {
             return;
         }
-        final BiometricSchedulerOperation mOperation = mCurrentOperation;
+        final BiometricSchedulerOperation operation = mCurrentOperation;
         mHandler.postDelayed(() -> {
-            if (mOperation == mCurrentOperation) {
+            if (operation == mCurrentOperation) {
+                Counter.logIncrement("biometric.scheduler_watchdog_triggered_count");
                 clearScheduler();
             }
         }, 10000);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index c2994a9..6f26e7b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -415,6 +415,11 @@
                         }
                     }
                 }
+
+                @Override
+                public void onError(int error, int vendorCode) throws RemoteException {
+                    receiver.onError(error, vendorCode);
+                }
             };
 
             // This effectively iterates through all sensors, but has to do so by finding all
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 4208a12..d4bb445 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -3828,10 +3828,27 @@
                     }, retryDelayMs, TimeUnit.MILLISECONDS);
         }
 
+        private boolean significantCapsChange(@Nullable final NetworkCapabilities left,
+                @Nullable final NetworkCapabilities right) {
+            if (left == right) return false;
+            return null == left
+                    || null == right
+                    || !Arrays.equals(left.getTransportTypes(), right.getTransportTypes())
+                    || !Arrays.equals(left.getCapabilities(), right.getCapabilities())
+                    || !Arrays.equals(left.getEnterpriseIds(), right.getEnterpriseIds())
+                    || !Objects.equals(left.getTransportInfo(), right.getTransportInfo())
+                    || !Objects.equals(left.getAllowedUids(), right.getAllowedUids())
+                    || !Objects.equals(left.getUnderlyingNetworks(), right.getUnderlyingNetworks())
+                    || !Objects.equals(left.getNetworkSpecifier(), right.getNetworkSpecifier());
+        }
+
         /** Called when the NetworkCapabilities of underlying network is changed */
         public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
-            mEventChanges.log("[UnderlyingNW] Cap changed from "
-                    + mUnderlyingNetworkCapabilities + " to " + nc);
+            if (significantCapsChange(mUnderlyingNetworkCapabilities, nc)) {
+                // TODO : make this log terser
+                mEventChanges.log("[UnderlyingNW] Cap changed from "
+                        + mUnderlyingNetworkCapabilities + " to " + nc);
+            }
             final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities;
             mUnderlyingNetworkCapabilities = nc;
             if (oldNc == null || !nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 774087c..75709fbb 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -333,11 +333,8 @@
         // Initialize to active (normal) screen brightness mode
         switchToInteractiveScreenBrightnessMode();
 
-        if (userLux != BrightnessMappingStrategy.NO_USER_LUX
-                && userBrightness != BrightnessMappingStrategy.NO_USER_BRIGHTNESS) {
-            // Use the given short-term model
-            setScreenBrightnessByUser(userLux, userBrightness);
-        }
+        // Use the given short-term model
+        setScreenBrightnessByUser(userLux, userBrightness);
     }
 
     /**
@@ -520,6 +517,10 @@
     }
 
     private boolean setScreenBrightnessByUser(float lux, float brightness) {
+        if (lux == BrightnessMappingStrategy.NO_USER_LUX
+                || brightness == BrightnessMappingStrategy.NO_USER_BRIGHTNESS) {
+            return false;
+        }
         mCurrentBrightnessMapper.addUserDataPoint(lux, brightness);
         mShortTermModel.setUserBrightness(lux, brightness);
         return true;
@@ -1234,14 +1235,14 @@
         // light.
         // The anchor determines what were the light levels when the user has set their preference,
         // and we use a relative threshold to determine when to revert to the OEM curve.
-        private float mAnchor = -1f;
-        private float mBrightness;
-        private boolean mIsValid = true;
+        private float mAnchor = BrightnessMappingStrategy.NO_USER_LUX;
+        private float mBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS;
+        private boolean mIsValid = false;
 
         private void reset() {
-            mAnchor = -1f;
-            mBrightness = -1f;
-            mIsValid = true;
+            mAnchor = BrightnessMappingStrategy.NO_USER_LUX;
+            mBrightness = BrightnessMappingStrategy.NO_USER_BRIGHTNESS;
+            mIsValid = false;
         }
 
         private void invalidate() {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index be9df4a..2c54e1c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4725,13 +4725,11 @@
             Slog.w(TAG, "Tried to update eARC status on a port that doesn't support eARC.");
             return;
         }
-        // If eARC is disabled, the local device is null. In this case, the HAL shouldn't have
-        // reported connection state changes, but even if it did, it won't take effect.
         if (mEarcLocalDevice != null) {
             mEarcLocalDevice.handleEarcStateChange(status);
         } else if (status == HDMI_EARC_STATUS_ARC_PENDING) {
-            // If the local device is null we notify the Audio Service that eARC connection
-            // is disabled.
+            // If eARC is disabled, the local device is null. This is why we notify
+            // AudioService here that the eARC connection is terminated.
             notifyEarcStatusToAudioService(false, new ArrayList<>());
             startArcAction(true, null);
         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 662591e..4cb22db 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -500,8 +500,6 @@
 
         // Add ourselves to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
-
-        mSettingsObserver.registerAndUpdate();
     }
 
     // TODO(BT) Pass in parameter for bluetooth system
@@ -512,6 +510,8 @@
 
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
 
+        mSettingsObserver.registerAndUpdate();
+
         synchronized (mLidSwitchLock) {
             mSystemReady = true;
 
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index c076c05..c59b733 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -31,6 +31,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 abstract class MediaRoute2Provider {
     final ComponentName mComponentName;
@@ -56,7 +57,9 @@
     public abstract void requestCreateSession(long requestId, String packageName, String routeId,
             @Nullable Bundle sessionHints);
     public abstract void releaseSession(long requestId, String sessionId);
-    public abstract void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference);
+
+    public abstract void updateDiscoveryPreference(
+            Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference);
 
     public abstract void selectRoute(long requestId, String sessionId, String routeId);
     public abstract void deselectRoute(long requestId, String sessionId, String routeId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 72b8436..3cf0786 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -49,6 +49,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Maintains a connection to a particular {@link MediaRoute2ProviderService}.
@@ -61,6 +62,7 @@
     private final Context mContext;
     private final int mUserId;
     private final Handler mHandler;
+    private final boolean mIsSelfScanOnlyProvider;
 
     // Connection state
     private boolean mRunning;
@@ -70,14 +72,19 @@
 
     private boolean mIsManagerScanning;
     private RouteDiscoveryPreference mLastDiscoveryPreference = null;
+    private boolean mLastDiscoveryPreferenceIncludesThisPackage = false;
 
     @GuardedBy("mLock")
     final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>();
 
-    MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName,
+    MediaRoute2ProviderServiceProxy(
+            @NonNull Context context,
+            @NonNull ComponentName componentName,
+            boolean isSelfScanOnlyProvider,
             int userId) {
         super(componentName);
         mContext = Objects.requireNonNull(context, "Context must not be null.");
+        mIsSelfScanOnlyProvider = isSelfScanOnlyProvider;
         mUserId = userId;
         mHandler = new Handler(Looper.myLooper());
     }
@@ -107,8 +114,11 @@
     }
 
     @Override
-    public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+    public void updateDiscoveryPreference(
+            Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference) {
         mLastDiscoveryPreference = discoveryPreference;
+        mLastDiscoveryPreferenceIncludesThisPackage =
+                activelyScanningPackages.contains(mComponentName.getPackageName());
         if (mConnectionReady) {
             mActiveConnection.updateDiscoveryPreference(discoveryPreference);
         }
@@ -209,11 +219,15 @@
 
     private boolean shouldBind() {
         if (mRunning) {
-            // Bind when there is a discovery preference or an active route session.
-            return (mLastDiscoveryPreference != null
-                    && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty())
-                    || !getSessionInfos().isEmpty()
-                    || mIsManagerScanning;
+            boolean shouldBind =
+                    mLastDiscoveryPreference != null
+                            && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty();
+            if (mIsSelfScanOnlyProvider) {
+                shouldBind &= mLastDiscoveryPreferenceIncludesThisPackage;
+            }
+            shouldBind |= mIsManagerScanning;
+            shouldBind |= !getSessionInfos().isEmpty();
+            return shouldBind;
         }
         return false;
     }
@@ -301,7 +315,11 @@
         if (mActiveConnection == connection) {
             mConnectionReady = true;
             if (mLastDiscoveryPreference != null) {
-                updateDiscoveryPreference(mLastDiscoveryPreference);
+                updateDiscoveryPreference(
+                        mLastDiscoveryPreferenceIncludesThisPackage
+                                ? Set.of(mComponentName.getPackageName())
+                                : Set.of(),
+                        mLastDiscoveryPreference);
             }
         }
     }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
index 46bccaf..bd252e7 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -16,6 +16,8 @@
 
 package com.android.server.media;
 
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
+
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -34,6 +36,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 
 /**
  * Watches changes of packages, or scan them for finding media route providers.
@@ -41,8 +44,8 @@
 final class MediaRoute2ProviderWatcher {
     private static final String TAG = "MR2ProviderWatcher";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final PackageManager.ResolveInfoFlags RESOLVE_INFO_FLAGS_NONE =
-            PackageManager.ResolveInfoFlags.of(0);
+    private static final PackageManager.ResolveInfoFlags RESOLVE_INFO_FLAGS =
+            PackageManager.ResolveInfoFlags.of(GET_RESOLVED_FILTER);
 
     private final Context mContext;
     private final Callback mCallback;
@@ -118,16 +121,26 @@
         int targetIndex = 0;
         Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
         for (ResolveInfo resolveInfo :
-                mPackageManager.queryIntentServicesAsUser(
-                        intent, RESOLVE_INFO_FLAGS_NONE, mUserId)) {
+                mPackageManager.queryIntentServicesAsUser(intent, RESOLVE_INFO_FLAGS, mUserId)) {
             ServiceInfo serviceInfo = resolveInfo.serviceInfo;
             if (serviceInfo != null) {
+                boolean isSelfScanOnlyProvider = false;
+                Iterator<String> categoriesIterator = resolveInfo.filter.categoriesIterator();
+                if (categoriesIterator != null) {
+                    while (categoriesIterator.hasNext()) {
+                        isSelfScanOnlyProvider |=
+                                MediaRoute2ProviderService.CATEGORY_SELF_SCAN_ONLY.equals(
+                                        categoriesIterator.next());
+                    }
+                }
                 int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
                 if (sourceIndex < 0) {
                     MediaRoute2ProviderServiceProxy proxy =
-                            new MediaRoute2ProviderServiceProxy(mContext,
-                            new ComponentName(serviceInfo.packageName, serviceInfo.name),
-                            mUserId);
+                            new MediaRoute2ProviderServiceProxy(
+                                    mContext,
+                                    new ComponentName(serviceInfo.packageName, serviceInfo.name),
+                                    isSelfScanOnlyProvider,
+                                    mUserId);
                     proxy.start();
                     mProxies.add(targetIndex++, proxy);
                     mCallback.onAddProviderService(proxy);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index b79991e..1059c19 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1478,6 +1478,7 @@
         final ArrayList<RouterRecord> mRouterRecords = new ArrayList<>();
         final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
         RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
+        Set<String> mActivelyScanningPackages = Set.of();
         final UserHandler mHandler;
 
         UserRecord(int userId) {
@@ -1525,7 +1526,12 @@
                 pw.println(indent + "<no manager records>");
             }
 
-            mCompositeDiscoveryPreference.dump(pw, indent);
+            pw.println(indent + "Composite discovery preference:");
+            mCompositeDiscoveryPreference.dump(pw, indent + "  ");
+            pw.println(
+                    indent
+                            + "Packages actively scanning: "
+                            + String.join(", ", mActivelyScanningPackages));
 
             if (!mHandler.runWithScissors(() -> mHandler.dump(pw, indent), 1000)) {
                 pw.println(indent + "<could not dump handler state>");
@@ -1834,7 +1840,9 @@
         public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) {
             proxy.setCallback(this);
             mRouteProviders.add(proxy);
-            proxy.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
+            proxy.updateDiscoveryPreference(
+                    mUserRecord.mActivelyScanningPackages,
+                    mUserRecord.mCompositeDiscoveryPreference);
         }
 
         @Override
@@ -2341,8 +2349,8 @@
                     return;
                 }
                 notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo);
-                notifySessionInfoChangedToRouters(getRouterRecords(false),
-                        mSystemProvider.getDefaultSessionInfo());
+                notifySessionInfoChangedToRouters(
+                        getRouterRecords(false), mSystemProvider.getDefaultSessionInfo());
                 return;
             }
 
@@ -2711,8 +2719,8 @@
             if (service == null) {
                 return;
             }
-            List<RouteDiscoveryPreference> discoveryPreferences = Collections.emptyList();
-            List<RouterRecord> routerRecords = getRouterRecords();
+            List<RouterRecord> activeRouterRecords = Collections.emptyList();
+            List<RouterRecord> allRouterRecords = getRouterRecords();
             List<ManagerRecord> managerRecords = getManagerRecords();
 
             boolean isManagerScanning = false;
@@ -2723,15 +2731,16 @@
                                 <= sPackageImportanceForScanning);
 
                 if (isManagerScanning) {
-                    discoveryPreferences = routerRecords.stream()
-                            .map(record -> record.mDiscoveryPreference)
-                            .collect(Collectors.toList());
+                    activeRouterRecords = allRouterRecords;
                 } else {
-                    discoveryPreferences = routerRecords.stream().filter(record ->
-                            service.mActivityManager.getPackageImportance(record.mPackageName)
-                                    <= sPackageImportanceForScanning)
-                            .map(record -> record.mDiscoveryPreference)
-                            .collect(Collectors.toList());
+                    activeRouterRecords =
+                            allRouterRecords.stream()
+                                    .filter(
+                                            record ->
+                                                    service.mActivityManager.getPackageImportance(
+                                                                    record.mPackageName)
+                                                            <= sPackageImportanceForScanning)
+                                    .collect(Collectors.toList());
                 }
             }
 
@@ -2748,22 +2757,30 @@
             // to query route providers once to obtain all of the routes of interest, which
             // can be subsequently filtered for the individual discovery preferences.
             Set<String> preferredFeatures = new HashSet<>();
+            Set<String> activelyScanningPackages = new HashSet<>();
             boolean activeScan = false;
-            for (RouteDiscoveryPreference preference : discoveryPreferences) {
+            for (RouterRecord activeRouterRecord : activeRouterRecords) {
+                RouteDiscoveryPreference preference = activeRouterRecord.mDiscoveryPreference;
                 preferredFeatures.addAll(preference.getPreferredFeatures());
-                activeScan |= preference.shouldPerformActiveScan();
+                if (preference.shouldPerformActiveScan()) {
+                    activeScan = true;
+                    activelyScanningPackages.add(activeRouterRecord.mPackageName);
+                }
             }
             RouteDiscoveryPreference newPreference = new RouteDiscoveryPreference.Builder(
                     List.copyOf(preferredFeatures), activeScan || isManagerScanning).build();
 
             synchronized (service.mLock) {
-                if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)) {
+                if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)
+                        && activelyScanningPackages.equals(mUserRecord.mActivelyScanningPackages)) {
                     return;
                 }
                 mUserRecord.mCompositeDiscoveryPreference = newPreference;
+                mUserRecord.mActivelyScanningPackages = activelyScanningPackages;
             }
             for (MediaRoute2Provider provider : mRouteProviders) {
-                provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
+                provider.updateDiscoveryPreference(
+                        activelyScanningPackages, mUserRecord.mCompositeDiscoveryPreference);
             }
         }
 
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 426bc5e..f4e6abd 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -42,6 +42,7 @@
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
@@ -196,7 +197,8 @@
     }
 
     @Override
-    public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+    public void updateDiscoveryPreference(
+            Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference) {
         // Do nothing
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d7eff52..9c780bb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1086,6 +1086,8 @@
 
         final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
         if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) {
+            Slog.d(TAG, "Can't start dreaming when attempting to dream from short power"
+                    + " press (isScreenOn=" + isScreenOn + ")");
             noDreamAction.run();
             return;
         }
@@ -6010,6 +6012,12 @@
     }
 
     @Override
+    @WindowManagerFuncs.LidState
+    public int getLidState() {
+        return mDefaultDisplayPolicy.getLidState();
+    }
+
+    @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 887f946..3da7812 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -218,6 +218,14 @@
      * between it and the policy.
      */
     public interface WindowManagerFuncs {
+        @IntDef(prefix = { "LID_" }, value = {
+                LID_ABSENT,
+                LID_CLOSED,
+                LID_OPEN,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface LidState{}
+
         public static final int LID_ABSENT = -1;
         public static final int LID_CLOSED = 0;
         public static final int LID_OPEN = 1;
@@ -231,8 +239,9 @@
         public static final int CAMERA_LENS_COVERED = 1;
 
         /**
-         * Returns a code that describes the current state of the lid switch.
+         * Returns a {@link LidState} that describes the current state of the lid switch.
          */
+        @LidState
         public int getLidState();
 
         /**
@@ -282,7 +291,7 @@
         /**
          * Convert the lid state to a human readable format.
          */
-        static String lidStateToString(int lid) {
+        static String lidStateToString(@LidState int lid) {
             switch (lid) {
                 case LID_ABSENT:
                     return "LID_ABSENT";
@@ -1241,4 +1250,11 @@
      * @return {@code true} if the key will be handled globally.
      */
     boolean isGlobalKey(int keyCode);
+
+    /**
+     * Returns a {@link WindowManagerFuncs.LidState} that describes the current state of
+     * the lid switch.
+     */
+    @WindowManagerFuncs.LidState
+    int getLidState();
 }
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 9bc0ee22..a694e31 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -265,7 +265,7 @@
                     + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
                     + ", workSource=" + workSource);
         }
-        notifyWakeLockListener(callback, true);
+        notifyWakeLockListener(callback, tag, true);
         final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
         if (monitorType >= 0) {
             try {
@@ -392,7 +392,7 @@
                     + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
                     + ", workSource=" + workSource);
         }
-        notifyWakeLockListener(callback, false);
+        notifyWakeLockListener(callback, tag, false);
         final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
         if (monitorType >= 0) {
             try {
@@ -1011,13 +1011,13 @@
         return enabled && dndOff;
     }
 
-    private void notifyWakeLockListener(IWakeLockCallback callback, boolean isEnabled) {
+    private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled) {
         if (callback != null) {
             mHandler.post(() -> {
                 try {
                     callback.onStateChanged(isEnabled);
                 } catch (RemoteException e) {
-                    throw new IllegalArgumentException("Wakelock.mCallback is already dead.", e);
+                    Slog.e(TAG, "Wakelock.mCallback [" + tag + "] is already dead.", e);
                 }
             });
         }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b8c5b3f..712be36 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -33,6 +33,7 @@
 import static android.os.PowerManagerInternal.wakefulnessToString;
 
 import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -5733,6 +5734,11 @@
         @Override // Binder call
         public void wakeUp(long eventTime, @WakeReason int reason, String details,
                 String opPackageName) {
+            if (mPolicy.getLidState() == LID_CLOSED) {
+                Slog.d(TAG, "Ignoring wake up call due to the lid being closed");
+                return;
+            }
+
             final long now = mClock.uptimeMillis();
             if (eventTime > now) {
                 Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3db0315..2b2100e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5630,11 +5630,18 @@
             setClientVisible(visible);
         }
 
+        final DisplayContent displayContent = getDisplayContent();
         if (!visible) {
             mImeInsetsFrozenUntilStartInput = true;
+            if (usingShellTransitions) {
+                final WindowState wallpaperTarget =
+                        displayContent.mWallpaperController.getWallpaperTarget();
+                if (wallpaperTarget != null && wallpaperTarget.mActivityRecord == this) {
+                    displayContent.mWallpaperController.hideWallpapers(wallpaperTarget);
+                }
+            }
         }
 
-        final DisplayContent displayContent = getDisplayContent();
         if (!displayContent.mClosingApps.contains(this)
                 && !displayContent.mOpeningApps.contains(this)
                 && !fromTransition) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3e3eb57..0ebec11 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1821,7 +1821,8 @@
         // If Activity's launching into PiP, move the mStartActivity immediately to pinned mode.
         // Note that mStartActivity and source should be in the same Task at this point.
         if (mOptions != null && mOptions.isLaunchIntoPip()
-                && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()) {
+                && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()
+                && balCode != BAL_BLOCK) {
             mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
                     sourceRecord, "launch-into-pip");
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index e07c654..32f1f42 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -262,9 +262,17 @@
      */
     public abstract void setVr2dDisplayId(int vr2dDisplayId);
 
+    /**
+     * Registers a {@link ScreenObserver}.
+     */
     public abstract void registerScreenObserver(ScreenObserver observer);
 
     /**
+     * Unregisters the given {@link ScreenObserver}.
+     */
+    public abstract void unregisterScreenObserver(ScreenObserver observer);
+
+    /**
      * Returns is the caller has the same uid as the Recents component
      */
     public abstract boolean isCallerRecents(int callingUid);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 750ed98..a650d77 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -296,6 +296,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
@@ -652,7 +653,8 @@
      */
     float mMinPercentageMultiWindowSupportWidth;
 
-    final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
+    final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers =
+            Collections.synchronizedList(new ArrayList<>());
 
     // VR Vr2d Display Id.
     int mVr2dDisplayId = INVALID_DISPLAY;
@@ -3558,10 +3560,10 @@
                 mRootWindowContainer.forAllDisplays(displayContent -> {
                     mKeyguardController.keyguardGoingAway(displayContent.getDisplayId(), flags);
                 });
-                WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
-                if (wallpaperManagerInternal != null) {
-                    wallpaperManagerInternal.onKeyguardGoingAway();
-                }
+            }
+            WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
+            if (wallpaperManagerInternal != null) {
+                wallpaperManagerInternal.onKeyguardGoingAway();
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -3649,6 +3651,8 @@
                     Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r);
                     return;
                 }
+                EventLogTags.writeWmEnterPip(r.mUserId, System.identityHashCode(r),
+                        r.shortComponentName, Boolean.toString(isAutoEnter));
                 r.setPictureInPictureParams(params);
                 r.mAutoEnteringPip = isAutoEnter;
                 mRootWindowContainer.moveActivityToPinnedRootTask(r,
@@ -5867,6 +5871,11 @@
         }
 
         @Override
+        public void unregisterScreenObserver(ScreenObserver observer) {
+            mScreenObservers.remove(observer);
+        }
+
+        @Override
         public boolean isCallerRecents(int callingUid) {
             return ActivityTaskManagerService.this.isCallerRecents(callingUid);
         }
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index e88cfbf..527edc1 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -190,6 +190,11 @@
                 return false;
             }
             List<IBinder> binderTokens = getOriginatingTokensThatAllowBal();
+            if (binderTokens.isEmpty()) {
+                // no tokens to allow anything
+                return false;
+            }
+
             // The callback will decide.
             return mBackgroundActivityStartCallback.isActivityStartAllowed(
                     binderTokens, uid, packageName);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e44d279..6aec969 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6511,6 +6511,22 @@
                 .getKeyguardController().isDisplayOccluded(mDisplayId);
     }
 
+    /**
+     * @return the task that is occluding the keyguard
+     */
+    @Nullable
+    Task getTaskOccludingKeyguard() {
+        final KeyguardController keyguardController = mRootWindowContainer.mTaskSupervisor
+                .getKeyguardController();
+        if (keyguardController.getTopOccludingActivity(mDisplayId) != null) {
+            return keyguardController.getTopOccludingActivity(mDisplayId).getRootTask();
+        }
+        if (keyguardController.getDismissKeyguardActivity(mDisplayId) != null) {
+            return keyguardController.getDismissKeyguardActivity(mDisplayId).getRootTask();
+        }
+        return null;
+    }
+
     @VisibleForTesting
     void removeAllTasks() {
         forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); });
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 77e70a2..da102d0 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -225,6 +225,7 @@
     /** Currently it can only be non-null when physical display switch happens. */
     private DecorInsets.Cache mCachedDecorInsets;
 
+    @WindowManagerFuncs.LidState
     private volatile int mLidState = LID_ABSENT;
     private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     private volatile boolean mHdmiPlugged;
@@ -752,10 +753,11 @@
         return mNavigationBarCanMove;
     }
 
-    public void setLidState(int lidState) {
+    public void setLidState(@WindowManagerFuncs.LidState int lidState) {
         mLidState = lidState;
     }
 
+    @WindowManagerFuncs.LidState
     public int getLidState() {
         return mLidState;
     }
@@ -1854,6 +1856,9 @@
              */
             final Rect mConfigFrame = new Rect();
 
+            /** The count of insets sources when calculating this info. */
+            int mLastInsetsSourceCount;
+
             private boolean mNeedUpdate = true;
 
             void update(DisplayContent dc, int rotation, int w, int h) {
@@ -1875,6 +1880,7 @@
                 mNonDecorFrame.inset(mNonDecorInsets);
                 mConfigFrame.set(displayFrame);
                 mConfigFrame.inset(mConfigInsets);
+                mLastInsetsSourceCount = dc.getDisplayPolicy().mInsetsSourceWindowsExceptIme.size();
                 mNeedUpdate = false;
             }
 
@@ -1883,6 +1889,7 @@
                 mConfigInsets.set(other.mConfigInsets);
                 mNonDecorFrame.set(other.mNonDecorFrame);
                 mConfigFrame.set(other.mConfigFrame);
+                mLastInsetsSourceCount = other.mLastInsetsSourceCount;
                 mNeedUpdate = false;
             }
 
@@ -1981,6 +1988,19 @@
         newInfo.update(mDisplayContent, rotation, dw, dh);
         final DecorInsets.Info currentInfo = getDecorInsetsInfo(rotation, dw, dh);
         if (newInfo.mConfigFrame.equals(currentInfo.mConfigFrame)) {
+            // Even if the config frame is not changed in current rotation, it may change the
+            // insets in other rotations if the source count is changed.
+            if (newInfo.mLastInsetsSourceCount != currentInfo.mLastInsetsSourceCount) {
+                for (int i = mDecorInsets.mInfoForRotation.length - 1; i >= 0; i--) {
+                    if (i != rotation) {
+                        final boolean flipSize = (i + rotation) % 2 == 1;
+                        final int w = flipSize ? dh : dw;
+                        final int h = flipSize ? dw : dh;
+                        mDecorInsets.mInfoForRotation[i].update(mDisplayContent, i, w, h);
+                    }
+                }
+                mDecorInsets.mInfoForRotation[rotation].set(newInfo);
+            }
             return false;
         }
         if (mCachedDecorInsets != null && !mCachedDecorInsets.canPreserve()
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 87de0f6..bc1ddf8 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -513,6 +513,7 @@
             }
 
             if (mDisplayContent.inTransition()
+                    && mDisplayContent.getDisplayPolicy().isScreenOnFully()
                     && !mDisplayContent.mTransitionController.useShellTransitionsRotation()) {
                 // Rotation updates cannot be performed while the previous rotation change animation
                 // is still in progress. Skip this update. We will try updating again after the
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 244656c..d957591 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -80,3 +80,6 @@
 
 # Request surface flinger to show / hide the wallpaper surface.
 33001 wm_wallpaper_surface (Display Id|1|5),(Visible|1),(Target|3)
+
+# Entering pip called
+38000 wm_enter_pip (User|1|5),(Token|1|5),(Component Name|3),(is Auto Enter|3)
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index ff2985c..e8a4c1c 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -91,6 +91,21 @@
     }
 
     @Override
+    void setClientVisible(boolean clientVisible) {
+        final boolean wasClientVisible = isClientVisible();
+        super.setClientVisible(clientVisible);
+        // The layer of ImePlaceholder needs to be updated on a higher z-order for
+        // non-activity window (For activity window, IME is already on top of it).
+        if (!wasClientVisible && isClientVisible()) {
+            final InsetsControlTarget imeControlTarget = getControlTarget();
+            if (imeControlTarget != null && imeControlTarget.getWindow() != null
+                    && imeControlTarget.getWindow().mActivityRecord == null) {
+                mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */);
+            }
+        }
+    }
+
+    @Override
     void setServerVisible(boolean serverVisible) {
         mServerVisible = serverVisible;
         if (!mFrozen) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 5f6d660..99878a3 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
@@ -411,19 +410,20 @@
         if (waitAppTransition) {
             mService.deferWindowLayout();
             try {
-                mRootWindowContainer.getDefaultDisplay()
-                        .requestTransitionAndLegacyPrepare(
-                                isDisplayOccluded(DEFAULT_DISPLAY)
-                                        ? TRANSIT_KEYGUARD_OCCLUDE
-                                        : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
+                if (isDisplayOccluded(DEFAULT_DISPLAY)) {
+                    mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
+                            TRANSIT_KEYGUARD_OCCLUDE,
+                            topActivity != null ? topActivity.getRootTask() : null);
+                } else {
+                    mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
+                            TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
+                }
                 updateKeyguardSleepToken(DEFAULT_DISPLAY);
                 mWindowManager.executeAppTransition();
             } finally {
                 mService.continueWindowLayout();
             }
         }
-        dismissMultiWindowModeForTaskIfNeeded(displayId, topActivity != null
-                ? topActivity.getRootTask() : null);
     }
 
     /**
@@ -473,6 +473,14 @@
         return getDisplayState(displayId).mOccluded;
     }
 
+    ActivityRecord getTopOccludingActivity(int displayId) {
+        return getDisplayState(displayId).mTopOccludesActivity;
+    }
+
+    ActivityRecord getDismissKeyguardActivity(int displayId) {
+        return getDisplayState(displayId).mDismissingKeyguardActivity;
+    }
+
     /**
      * @return true if Keyguard can be currently dismissed without entering credentials.
      */
@@ -488,22 +496,6 @@
         return getDisplayState(DEFAULT_DISPLAY).mShowingDream;
     }
 
-    private void dismissMultiWindowModeForTaskIfNeeded(int displayId,
-            @Nullable Task currentTaskControllingOcclusion) {
-        // TODO(b/113840485): Handle docked stack for individual display.
-        if (!getDisplayState(displayId).mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
-            return;
-        }
-
-        // Dismiss freeform windowing mode
-        if (currentTaskControllingOcclusion == null) {
-            return;
-        }
-        if (currentTaskControllingOcclusion.inFreeformWindowingMode()) {
-            currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        }
-    }
-
     private void updateKeyguardSleepToken() {
         for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
              displayNdx >= 0; displayNdx--) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5369159..7e83959 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2376,6 +2376,7 @@
             if (!displayShouldSleep && display.mTransitionController.isShellTransitionsEnabled()
                     && !display.mTransitionController.isCollecting()) {
                 int transit = TRANSIT_NONE;
+                Task startTask = null;
                 if (!display.getDisplayPolicy().isAwake()) {
                     // Note that currently this only happens on default display because non-default
                     // display is always awake.
@@ -2383,12 +2384,12 @@
                 } else if (display.isKeyguardOccluded()) {
                     // The display was awake so this is resuming activity for occluding keyguard.
                     transit = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+                    startTask = display.getTaskOccludingKeyguard();
                 }
                 if (transit != TRANSIT_NONE) {
                     display.mTransitionController.requestStartTransition(
                             display.mTransitionController.createTransition(transit),
-                            null /* startTask */, null /* remoteTransition */,
-                            null /* displayChange */);
+                            startTask, null /* remoteTransition */, null /* displayChange */);
                 }
             }
             // Set the sleeping state of the root tasks on the display.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 663db86..b938b9e 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -687,7 +687,11 @@
             // All windows are synced already.
             return;
         }
-        if (!isInTransition(wc)) return;
+        if (wc.mDisplayContent == null || !isInTransition(wc)) return;
+        if (!wc.mDisplayContent.getDisplayPolicy().isScreenOnFully()
+                || wc.mDisplayContent.getDisplayInfo().state == Display.STATE_OFF) {
+            mFlags |= WindowManager.TRANSIT_FLAG_INVISIBLE;
+        }
 
         if (mContainerFreezer == null) {
             mContainerFreezer = new ScreenshotFreezer();
@@ -2245,8 +2249,10 @@
             while (leashReference.getParent() != ancestor) {
                 leashReference = leashReference.getParent();
             }
+
             final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
                     "Transition Root: " + leashReference.getName()).build();
+            rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
             startT.setLayer(rootLeash, leashReference.getLastLayer());
             outInfo.addRootLeash(endDisplayId, rootLeash,
                     ancestor.getBounds().left, ancestor.getBounds().top);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index edafe06..20ce98c 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -146,11 +146,10 @@
             }
         } else {
             final ActivityRecord ar = w.mActivityRecord;
-            final TransitionController tc = w.mTransitionController;
             // The animating window can still be visible on screen if it is in transition, so we
             // should check whether this window can be wallpaper target even when visibleRequested
             // is false.
-            if (ar != null && !ar.isVisibleRequested() && !tc.inTransition(ar)) {
+            if (ar != null && !ar.isVisibleRequested() && !ar.isVisible()) {
                 // An activity that is not going to remain visible shouldn't be the target.
                 return false;
             }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 7e34d15..8e85e5b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -960,20 +960,6 @@
                         errorCallbackToken, organizer);
                 break;
             }
-            default: {
-                // The other operations may change task order so they are skipped while in lock
-                // task mode. The above operations are still allowed because they don't move
-                // tasks. And it may be necessary such as clearing launch root after entering
-                // lock task mode.
-                if (isInLockTaskMode) {
-                    Slog.w(TAG, "Skip applying hierarchy operation " + hop
-                            + " while in lock task mode");
-                    return effects;
-                }
-            }
-        }
-
-        switch (type) {
             case HIERARCHY_OP_TYPE_PENDING_INTENT: {
                 final Bundle launchOpts = hop.getLaunchOptions();
                 ActivityOptions activityOptions = launchOpts != null
@@ -1012,6 +998,20 @@
                 }
                 break;
             }
+            default: {
+                // The other operations may change task order so they are skipped while in lock
+                // task mode. The above operations are still allowed because they don't move
+                // tasks. And it may be necessary such as clearing launch root after entering
+                // lock task mode.
+                if (isInLockTaskMode) {
+                    Slog.w(TAG, "Skip applying hierarchy operation " + hop
+                            + " while in lock task mode");
+                    return effects;
+                }
+            }
+        }
+
+        switch (type) {
             case HIERARCHY_OP_TYPE_START_SHORTCUT: {
                 final Bundle launchOpts = hop.getLaunchOptions();
                 final String callingPackage = launchOpts.getString(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c96bfd7..7464045 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2191,7 +2191,7 @@
 
     private void suspendAppsForQuietProfiles(boolean toSuspend) {
         PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
-        List<UserInfo> users = mUserManager.getUsers();
+        List<UserInfo> users = mUserManagerInternal.getUsers(true /* excludeDying */);
         for (UserInfo user : users) {
             if (user.isManagedProfile() && user.isQuietModeEnabled()) {
                 pmi.setPackagesSuspendedForQuietMode(user.id, toSuspend);
@@ -11113,7 +11113,7 @@
                 || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS);
     }
 
-    private boolean canUserUseLockTaskLocked(int userId) {
+    private boolean canDPCManagedUserUseLockTaskLocked(int userId) {
         if (isUserAffiliatedWithDeviceLocked(userId)) {
             return true;
         }
@@ -11122,19 +11122,16 @@
         if (mOwners.hasDeviceOwner()) {
             return false;
         }
-
-        if (!isPermissionCheckFlagEnabled() && !isPolicyEngineForFinanceFlagEnabled()) {
-            final ComponentName profileOwner = getProfileOwnerAsUser(userId);
-            if (profileOwner == null) {
-                return false;
-            }
+        
+        final ComponentName profileOwner = getProfileOwnerAsUser(userId);
+        if (profileOwner == null) {
+            return false;
         }
-
         // Managed profiles are not allowed to use lock task
         if (isManagedProfile(userId)) {
             return false;
         }
-
+        
         return true;
     }
     private void enforceCanQueryLockTaskLocked(ComponentName who, String callerPackageName) {
@@ -11142,7 +11139,8 @@
         final int userId = caller.getUserId();
 
         enforceCanQuery(MANAGE_DEVICE_POLICY_LOCK_TASK, caller.getPackageName(), userId);
-        if (!canUserUseLockTaskLocked(userId)) {
+        if ((isDeviceOwner(caller) || isProfileOwner(caller))
+                && !canDPCManagedUserUseLockTaskLocked(userId)) {
             throw new SecurityException("User " + userId + " is not allowed to use lock task");
         }
     }
@@ -11158,7 +11156,8 @@
                 caller.getPackageName(),
                 userId
         );
-        if (!canUserUseLockTaskLocked(userId)) {
+        if ((isDeviceOwner(caller) || isProfileOwner(caller))
+                && !canDPCManagedUserUseLockTaskLocked(userId)) {
             throw new SecurityException("User " + userId + " is not allowed to use lock task");
         }
         return enforcingAdmin;
@@ -11169,7 +11168,7 @@
                 || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
 
         final int userId =  caller.getUserId();
-        if (!canUserUseLockTaskLocked(userId)) {
+        if (!canDPCManagedUserUseLockTaskLocked(userId)) {
             throw new SecurityException("User " + userId + " is not allowed to use lock task");
         }
     }
@@ -15101,7 +15100,7 @@
             final List<UserInfo> userInfos = mUserManager.getAliveUsers();
             for (int i = userInfos.size() - 1; i >= 0; i--) {
                 int userId = userInfos.get(i).id;
-                if (canUserUseLockTaskLocked(userId)) {
+                if (canDPCManagedUserUseLockTaskLocked(userId)) {
                     continue;
                 }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index cda5456..770f04a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -68,6 +68,7 @@
 import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalAnswers.answer;
@@ -2489,6 +2490,28 @@
         assertProcStates(app2, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
     }
 
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoOne_AboveClient_SameProcess() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
+        doReturn(app).when(sService).getTopApp();
+        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE);
+
+        assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
+
+        // Simulate binding to a service in the same process using BIND_ABOVE_CLIENT and
+        // verify that its OOM adjustment level is unaffected.
+        bindService(app, app, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
+        app.mServices.updateHasAboveClientLocked();
+        assertFalse(app.mServices.hasAboveClient());
+
+        sService.mOomAdjuster.updateOomAdjLocked(app, OOM_ADJ_REASON_NONE);
+        assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
+    }
+
     private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
             String packageName, boolean hasShownUi) {
         long now = SystemClock.uptimeMillis();
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 317fd58..dccacb4 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -58,6 +58,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -103,11 +104,13 @@
 import com.android.server.pm.UserJourneyLogger;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerService;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerService;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -187,6 +190,7 @@
             doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
             doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
             doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
+            doNothing().when(mInjector).lockDeviceNowAndWaitForKeyguardShown();
             mockIsUsersOnSecondaryDisplaysEnabled(false);
             // All UserController params are set to default.
 
@@ -951,6 +955,45 @@
                 .systemServiceManagerOnUserCompletedEvent(eq(user2), eq(event2a));
     }
 
+    @Test
+    public void testStallUserSwitchUntilTheKeyguardIsShown() throws Exception {
+        // enable user switch ui, because keyguard is only shown then
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
+        // mock the device to be secure in order to expect the keyguard to be shown
+        when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
+
+        // call real lockDeviceNowAndWaitForKeyguardShown method for this test
+        doCallRealMethod().when(mInjector).lockDeviceNowAndWaitForKeyguardShown();
+
+        // call startUser on a thread because we're expecting it to be blocked
+        Thread threadStartUser = new Thread(()-> {
+            mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
+        });
+        threadStartUser.start();
+
+        // make sure the switch is stalled...
+        Thread.sleep(2000);
+        // by checking REPORT_USER_SWITCH_MSG is not sent yet
+        assertNull(mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG));
+        // and the thread is still alive
+        assertTrue(threadStartUser.isAlive());
+
+        // mock send the keyguard shown event
+        ArgumentCaptor<ActivityTaskManagerInternal.ScreenObserver> captor = ArgumentCaptor.forClass(
+                ActivityTaskManagerInternal.ScreenObserver.class);
+        verify(mInjector.mActivityTaskManagerInternal).registerScreenObserver(captor.capture());
+        captor.getValue().onKeyguardStateChanged(true);
+
+        // verify the switch now moves on...
+        Thread.sleep(1000);
+        // by checking REPORT_USER_SWITCH_MSG is sent
+        assertNotNull(mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG));
+        // and the thread is finished
+        assertFalse(threadStartUser.isAlive());
+    }
+
     private void setUpAndStartUserInBackground(int userId) throws Exception {
         setUpUser(userId, 0);
         mUserController.startUser(userId, USER_START_MODE_BACKGROUND);
@@ -1092,6 +1135,7 @@
         private final IStorageManager mStorageManagerMock;
         private final UserManagerInternal mUserManagerInternalMock;
         private final WindowManagerService mWindowManagerMock;
+        private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
         private final KeyguardManager mKeyguardManagerMock;
         private final LockPatternUtils mLockPatternUtilsMock;
 
@@ -1111,6 +1155,7 @@
             mUserManagerMock = mock(UserManagerService.class);
             mUserManagerInternalMock = mock(UserManagerInternal.class);
             mWindowManagerMock = mock(WindowManagerService.class);
+            mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
             mStorageManagerMock = mock(IStorageManager.class);
             mKeyguardManagerMock = mock(KeyguardManager.class);
             when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
@@ -1172,6 +1217,11 @@
         }
 
         @Override
+        ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+            return mActivityTaskManagerInternal;
+        }
+
+        @Override
         KeyguardManager getKeyguardManager() {
             return mKeyguardManagerMock;
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index fb3a5f6..a442303 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -316,13 +316,13 @@
         assertThat(aidlContext.isAod).isEqualTo(false);
         assertThat(aidlContext.isCrypto).isEqualTo(false);
 
-        context = mProvider.updateContext(mOpContext, false /* crypto */);
+        context = mProvider.updateContext(mOpContext, true /* crypto */);
         aidlContext = context.toAidlContext();
         assertThat(context).isSameInstanceAs(mOpContext);
         assertThat(aidlContext.id).isEqualTo(0);
         assertThat(aidlContext.reason).isEqualTo(OperationReason.UNKNOWN);
         assertThat(aidlContext.isAod).isEqualTo(false);
-        assertThat(aidlContext.isCrypto).isEqualTo(false);
+        assertThat(aidlContext.isCrypto).isEqualTo(true);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
index 5cf5960..32284fd 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
@@ -98,7 +98,7 @@
         for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
             final OperationContextExt context = new OperationContextExt(newAidlContext(), true);
             when(mBiometricContext.getDisplayState()).thenReturn(entry.getKey());
-            assertThat(context.update(mBiometricContext).getDisplayState())
+            assertThat(context.update(mBiometricContext, context.isCrypto()).getDisplayState())
                     .isEqualTo(entry.getValue());
         }
     }
@@ -139,7 +139,7 @@
         final OperationContextExt context = new OperationContextExt(newAidlContext(),
                 sessionType == OperationReason.BIOMETRIC_PROMPT);
 
-        assertThat(context.update(mBiometricContext)).isSameInstanceAs(context);
+        assertThat(context.update(mBiometricContext, context.isCrypto())).isSameInstanceAs(context);
 
         if (sessionInfo != null) {
             assertThat(context.getId()).isEqualTo(sessionInfo.getId());
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/OWNERS b/services/tests/servicestests/src/com/android/server/contentcapture/OWNERS
new file mode 100644
index 0000000..b3583a7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 544200
+
+include /core/java/android/view/contentcapture/OWNERS
+
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 3bef413..962e867 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -473,6 +473,42 @@
     }
 
     @Test
+    public void testSwitchBetweenModesNoUserInteractions() throws Exception {
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Sensor reads 123 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+        when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
+        when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1.0f);
+        when(mBrightnessMappingStrategy.getUserLux()).thenReturn(-1.0f);
+
+        // No user brightness interaction.
+
+        mController.switchToIdleMode();
+        when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+        when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1.0f);
+        when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1.0f);
+
+        // Sensor reads 1000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+        // Do not fast-forward time.
+        mTestLooper.dispatchAll();
+
+        mController.switchToInteractiveScreenBrightnessMode();
+        // Do not fast-forward time
+        mTestLooper.dispatchAll();
+
+        // Ensure that there are no data points added, since the user has never adjusted the
+        // brightness
+        verify(mBrightnessMappingStrategy, times(0))
+                .addUserDataPoint(anyFloat(), anyFloat());
+    }
+
+    @Test
     public void testSwitchToIdleMappingStrategy() throws Exception {
         ArgumentCaptor<SensorEventListener> listenerCaptor =
                 ArgumentCaptor.forClass(SensorEventListener.class);
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
index 849aae2..2f03965 100644
--- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
@@ -34,7 +34,10 @@
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.BatteryStats;
 import android.os.Handler;
+import android.os.IWakeLockCallback;
 import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
@@ -219,6 +222,34 @@
         verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt());
     }
 
+    @Test
+    public void testOnWakeLockListener_RemoteException_NoRethrow() {
+        createNotifier();
+
+        IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+            @Override public void onStateChanged(boolean enabled) throws RemoteException {
+                throw new RemoteException("Just testing");
+            }
+        };
+
+        final int uid = 1234;
+        final int pid = 5678;
+        mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+                exceptingCallback);
+        mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+                exceptingCallback);
+        mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+                exceptingCallback,
+                PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, /* newWorkSource= */ null, /* newHistoryTag= */ null,
+                exceptingCallback);
+        mTestLooper.dispatchAll();
+        // If we didn't throw, we're good!
+    }
+
     private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
         @Override
         Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 933f002..7aec045 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -26,6 +26,9 @@
 import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
 
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertFalse;
@@ -145,6 +148,7 @@
     @Mock private ActivityManagerInternal mActivityManagerInternalMock;
     @Mock private AttentionManagerInternal mAttentionManagerInternalMock;
     @Mock private DreamManagerInternal mDreamManagerInternalMock;
+    @Mock private WindowManagerPolicy mPolicyMock;
     @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
     @Mock private Notifier mNotifierMock;
     @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@@ -205,6 +209,7 @@
                 .thenReturn(true);
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
+        when(mPolicyMock.getLidState()).thenReturn(LID_ABSENT);
 
         addLocalServiceMock(LightsManager.class, mLightsManagerMock);
         addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
@@ -212,6 +217,7 @@
         addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
         addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
         addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
+        addLocalServiceMock(WindowManagerPolicy.class, mPolicyMock);
 
         mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
@@ -678,6 +684,20 @@
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
     }
 
+    @Test
+    public void testWakefulnessAwake_ShouldNotWakeUpWhenLidClosed() {
+        when(mPolicyMock.getLidState()).thenReturn(LID_CLOSED);
+        createService();
+        startSystem();
+        forceSleep();
+
+        mService.getBinderServiceInstance().wakeUp(mClock.now(),
+                PowerManager.WAKE_REASON_POWER_BUTTON,
+                "testing IPowerManager.wakeUp()", "pkg.name");
+
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+    }
+
     /**
      * Tests a series of variants that control whether a device wakes-up when it is plugged in
      * or docked.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 6ef81f6..9166b3d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5618,6 +5618,26 @@
     }
 
     @Test
+    public void testVisitUris_publicVersion() throws Exception {
+        final Icon smallIconPublic = Icon.createWithContentUri("content://media/small/icon");
+        final Icon largeIconPrivate = Icon.createWithContentUri("content://media/large/icon");
+
+        Notification publicVersion = new Notification.Builder(mContext, "a")
+                .setContentTitle("notification with uris")
+                .setSmallIcon(smallIconPublic)
+                .build();
+        Notification n = new Notification.Builder(mContext, "a")
+                .setLargeIcon(largeIconPrivate)
+                .setPublicVersion(publicVersion)
+                .build();
+
+        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+        n.visitUris(visitor);
+        verify(visitor, times(1)).accept(eq(smallIconPublic.getUri()));
+        verify(visitor, times(1)).accept(eq(largeIconPrivate.getUri()));
+    }
+
+    @Test
     public void testVisitUris_audioContentsString() throws Exception {
         final Uri audioContents = Uri.parse("content://com.example/audio");
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 0e627b2..6668f85 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -87,8 +87,6 @@
     // This list should be emptied! Items can be removed as bugs are fixed.
     private static final Multimap<Class<?>, String> KNOWN_BAD =
             ImmutableMultimap.<Class<?>, String>builder()
-                    .put(Notification.Builder.class, "setPublicVersion") // b/276294099
-                    .put(RemoteViews.class, "setIcon") // b/281018094
                     .put(Notification.WearableExtender.class, "addAction") // TODO: b/281044385
                     .put(Person.Builder.class, "setUri") // TODO: b/281044385
                     .put(RemoteViews.class, "setRemoteAdapter") // TODO: b/281044385
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 5ec3604..b0609dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -51,14 +51,18 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.DisplayInfo;
 import android.view.DisplayShape;
+import android.view.InsetsFrameProvider;
 import android.view.InsetsSource;
 import android.view.InsetsState;
 import android.view.PrivacyIndicatorBounds;
+import android.view.Surface;
+import android.view.WindowInsets;
 import android.view.WindowInsets.Side;
 import android.view.WindowManager;
 
@@ -350,6 +354,36 @@
                 && displayPolicy.updateDecorInsetsInfo());
         assertEquals(STATUS_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
                 di.logicalWidth, di.logicalHeight).mConfigInsets.top);
+
+        // Add a window that provides the same insets in current rotation. But it specifies
+        // different insets in other rotations.
+        final WindowState bar2 = createWindow(null, statusBar.mAttrs.type, "bar2");
+        bar2.mAttrs.providedInsets = new InsetsFrameProvider[] {
+                new InsetsFrameProvider(bar2, 0, WindowInsets.Type.statusBars())
+                        .setInsetsSize(Insets.of(0, STATUS_BAR_HEIGHT, 0, 0))
+        };
+        bar2.mAttrs.setFitInsetsTypes(0);
+        bar2.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
+        final int doubleHeightFor90 = STATUS_BAR_HEIGHT * 2;
+        for (int i = ROTATION_0; i <= Surface.ROTATION_270; i++) {
+            final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+            params.setFitInsetsTypes(0);
+            if (i == Surface.ROTATION_90) {
+                params.providedInsets = new InsetsFrameProvider[] {
+                        new InsetsFrameProvider(bar2, 0, WindowInsets.Type.statusBars())
+                                .setInsetsSize(Insets.of(0, doubleHeightFor90, 0, 0))
+                };
+            } else {
+                params.providedInsets = bar2.mAttrs.providedInsets;
+            }
+            bar2.mAttrs.paramsForRotation[i] = params;
+        }
+        displayPolicy.addWindowLw(bar2, bar2.mAttrs);
+        // Current rotation is 0 and the top insets is still STATUS_BAR_HEIGHT, so no change.
+        assertFalse(displayPolicy.updateDecorInsetsInfo());
+        // The insets in other rotations should be still updated.
+        assertEquals(doubleHeightFor90, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90,
+                di.logicalHeight, di.logicalWidth).mConfigInsets.top);
     }
 
     @SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index d1d83f6..d7e736d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -25,6 +25,7 @@
 import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
@@ -38,6 +39,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.spy;
@@ -420,10 +422,10 @@
 
     @Test
     public void testUpdateAboveInsetsState_zOrderChanged() {
-        final WindowState ime = createTestWindow("ime");
-        final WindowState app = createTestWindow("app");
-        final WindowState statusBar = createTestWindow("statusBar");
-        final WindowState navBar = createTestWindow("navBar");
+        final WindowState ime = createNonAppWindow("ime");
+        final WindowState app = createNonAppWindow("app");
+        final WindowState statusBar = createNonAppWindow("statusBar");
+        final WindowState navBar = createNonAppWindow("navBar");
 
         final InsetsSourceProvider imeSourceProvider =
                 getController().getOrCreateSourceProvider(ID_IME, ime());
@@ -431,7 +433,9 @@
 
         waitUntilHandlersIdle();
         clearInvocations(mDisplayContent);
+        imeSourceProvider.updateControlForTarget(app, false /* force */);
         imeSourceProvider.setClientVisible(true);
+        verify(mDisplayContent).assignWindowLayers(anyBoolean());
         waitUntilHandlersIdle();
         // The visibility change should trigger a traversal to notify the change.
         verify(mDisplayContent).notifyInsetsChanged(any());
@@ -547,6 +551,7 @@
                 control2.getInsetsHint().bottom);
     }
 
+    /** Creates a window which is associated with ActivityRecord. */
     private WindowState createTestWindow(String name) {
         final WindowState win = createWindow(null, TYPE_APPLICATION, name);
         win.setHasSurface(true);
@@ -554,6 +559,14 @@
         return win;
     }
 
+    /** Creates a non-activity window. */
+    private WindowState createNonAppWindow(String name) {
+        final WindowState win = createWindow(null, LAST_APPLICATION_WINDOW + 1, name);
+        win.setHasSurface(true);
+        spyOn(win);
+        return win;
+    }
+
     private InsetsStateController getController() {
         return mDisplayContent.getInsetsStateController();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index adf3f39..192632c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -18,6 +18,8 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -354,4 +356,9 @@
     public boolean isGlobalKey(int keyCode) {
         return false;
     }
+
+    @Override
+    public int getLidState() {
+        return LID_ABSENT;
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 863523f..ddc729f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -485,6 +485,7 @@
                 new InsetsFrameProvider(owner, 0, WindowInsets.Type.statusBars())
                         .setInsetsSize(Insets.of(0, STATUS_BAR_HEIGHT, 0, 0))
         };
+        statusBar.mAttrs.setFitInsetsTypes(0);
         dc.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
         return statusBar;
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 7f496d8..122a6cb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
 import android.tools.common.traces.component.ComponentNameMatcher
@@ -102,6 +103,7 @@
 
     @Presubmit
     @Test
+    @PlatinumTest(focusArea = "ime")
     @IwTest(focusArea = "ime")
     override fun cujCompleted() {
         super.cujCompleted()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index c693ca7..5aa4382 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -103,6 +104,7 @@
 
     @Presubmit
     @Test
+    @PlatinumTest(focusArea = "ime")
     @IwTest(focusArea = "ime")
     override fun cujCompleted() {
         super.cujCompleted()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index d5208e0..6731089 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -18,6 +18,7 @@
 
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -71,6 +72,7 @@
 
     @Presubmit
     @Test
+    @PlatinumTest(focusArea = "ime")
     @IwTest(focusArea = "ime")
     override fun cujCompleted() {
         runAndIgnoreAssumptionViolation { entireScreenCovered() }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index d133529..c70e3cb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -52,6 +53,7 @@
 
     @Presubmit
     @Test
+    @PlatinumTest(focusArea = "ime")
     @IwTest(focusArea = "ime")
     override fun cujCompleted() {
         super.cujCompleted()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 855ea3e..7fbcfec 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.rotation
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -129,6 +130,7 @@
     }
 
     @Test
+    @PlatinumTest(focusArea = "framework")
     @IwTest(focusArea = "framework")
     override fun cujCompleted() {
         super.cujCompleted()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 0cbbb83..44ae14a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.rotation
 
 import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.ScenarioBuilder
 import android.tools.common.traces.component.ComponentNameMatcher
@@ -213,6 +214,7 @@
     }
 
     @Test
+    @PlatinumTest(focusArea = "framework")
     @IwTest(focusArea = "framework")
     override fun cujCompleted() {
         appWindowFullScreen()