Merge "Use SkCanvasAndroid functions instead of deprecated canvasMethods"
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index f20854b..c6b9d20 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -190,8 +190,7 @@
     /**
      * Wake lock flag: Turn the screen on when the wake lock is acquired.
      * <p>
-     * This flag requires {@link android.Manifest.permission#TURN_SCREEN_ON} for apps targeting
-     * Android version {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and higher.
+     * This flag will require {@link android.Manifest.permission#TURN_SCREEN_ON} in future releases.
      * </p><p>
      * Normally wake locks don't actually wake the device, they just cause the screen to remain on
      * once it's already on. This flag will cause the device to wake up when the wake lock is
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index 29d7902..fde7c08 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -55,7 +55,10 @@
     ],
 
     platform_apis: true,
-    test_suites: ["device-tests"],
+    test_suites: [
+        "device-tests",
+        "automotive-tests",
+    ],
 
     certificate: "platform",
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 467e9c7..1959eb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -300,9 +300,12 @@
         return mAllowSeamlessRotationDespiteNavBarMoving;
     }
 
-    /** @return whether the navigation bar will change sides during rotation. */
+    /**
+     * Returns {@code true} if the navigation bar will change sides during rotation and the display
+     * is not square.
+     */
     public boolean navigationBarCanMove() {
-        return mNavigationBarCanMove;
+        return mNavigationBarCanMove && mWidth != mHeight;
     }
 
     /** @return the rotation that would make the physical display "upside down". */
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 4176fe8..bda25d5 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
@@ -24,6 +24,7 @@
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
 import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
@@ -172,7 +173,9 @@
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
             @NonNull TransitionRequestInfo request) {
-        if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) {
+        if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()
+                && request.getTriggerTask() != null && mSplitHandler.getSplitItemPosition(
+                        request.getTriggerTask().token) != SPLIT_POSITION_UNDEFINED) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while "
                     + "Split-Screen is active, so treat it as Mixed.");
             if (request.getRemoteTransition() != null) {
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 3bf278c..e52fd00 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
@@ -260,6 +260,12 @@
         // This is the only way to get display-id currently, so check display capabilities here.
         final DisplayLayout displayLayout = displayController.getDisplayLayout(
                 topTaskInfo.displayId);
+        // This condition should be true when using gesture navigation or the screen size is large
+        // (>600dp) because the bar is small relative to screen.
+        if (displayLayout.allowSeamlessRotationDespiteNavBarMoving()) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  nav bar allows seamless.");
+            return ROTATION_ANIMATION_SEAMLESS;
+        }
         // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
         // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
         // will not enter the reverse portrait orientation, so actually the orientation won't
@@ -272,13 +278,9 @@
             return animationHint;
         }
 
-        // If the navigation bar can't change sides, then it will jump when we change orientations
-        // and we don't rotate seamlessly - unless that is allowed, e.g. with gesture navigation
-        // where the navbar is low-profile enough that this isn't very noticeable.
-        if (!displayLayout.allowSeamlessRotationDespiteNavBarMoving()
-                && (!(displayLayout.navigationBarCanMove()
-                        && (displayChange.getStartAbsBounds().width()
-                                != displayChange.getStartAbsBounds().height())))) {
+        // If the navigation bar cannot change sides, then it will jump when changing orientation
+        // so do not use seamless rotation.
+        if (!displayLayout.navigationBarCanMove()) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                     "  nav bar changes sides, so not seamless.");
             return animationHint;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 961e3e9..ff380e9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -702,8 +702,8 @@
                 createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
 
         final DisplayController displays = createTestDisplayController();
-        final @Surface.Rotation int upsideDown = displays
-                .getDisplayLayout(DEFAULT_DISPLAY).getUpsideDownRotation();
+        final DisplayLayout displayLayout = displays.getDisplayLayout(DEFAULT_DISPLAY);
+        final @Surface.Rotation int upsideDown = displayLayout.getUpsideDownRotation();
 
         TransitionInfo.Change displayChange = new ChangeBuilder(TRANSIT_CHANGE)
                 .setFlags(FLAG_IS_DISPLAY).setRotate().build();
@@ -743,7 +743,8 @@
         assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint(
                 displayChange, noTask, displays));
 
-        // Not seamless if one of rotations is upside-down
+        // Not seamless if the nav bar cares rotation and one of rotations is upside-down.
+        doReturn(false).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving();
         displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
                 .setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build();
         final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE)
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index cc2cf48..60bc226 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -827,9 +827,9 @@
     <!-- UI debug setting: show touches location summary [CHAR LIMIT=50] -->
     <string name="show_touches_summary">Show visual feedback for taps</string>
 
-    <!-- UI debug setting: show key presses? [CHAR LIMIT=25] -->
+    <!-- UI debug setting: show key presses? [CHAR LIMIT=50] -->
     <string name="show_key_presses">Show key presses</string>
-    <!-- UI debug setting: show physical key presses summary [CHAR LIMIT=50] -->
+    <!-- UI debug setting: show physical key presses summary [CHAR LIMIT=150] -->
     <string name="show_key_presses_summary">Show visual feedback for physical key presses</string>
 
     <!-- UI debug setting: show where surface updates happen? [CHAR LIMIT=25] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 0332c9f..363dd01 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -208,7 +208,7 @@
         @Override
         public void run() {
             final View host = mHost.get();
-            if (host != null) {
+            if (host != null && host.isVisibleToUser()) {
                 host.announceForAccessibility(mTextToAnnounce);
             }
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index c1344e0..7c511a3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -275,13 +275,6 @@
          */
         @Override
         public void finish(boolean fromPrimaryAuth, int targetUserId) {
-            if (!mKeyguardStateController.canDismissLockScreen() && !fromPrimaryAuth) {
-                Log.e(TAG,
-                        "Tried to dismiss keyguard when lockscreen is not dismissible and user "
-                                + "was not authenticated with a primary security method "
-                                + "(pin/password/pattern).");
-                return;
-            }
             // If there's a pending runnable because the user interacted with a widget
             // and we're leaving keyguard, then run it.
             boolean deferKeyguardDone = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 7cedecc..239a0cc 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -326,7 +326,7 @@
         }
 
         if (!Objects.equals(prevContentDescription, mView.getContentDescription())
-                && mView.getContentDescription() != null) {
+                && mView.getContentDescription() != null && mView.isVisibleToUser()) {
             mView.announceForAccessibility(mView.getContentDescription());
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 8af92ce..806d1af 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -34,6 +34,7 @@
 import android.content.res.Resources;
 import android.graphics.RectF;
 import android.os.Handler;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -277,9 +278,11 @@
 
     // invalidate the view's own bounds all the way up the view hierarchy
     public static void invalidateGlobalRegion(View view) {
+        Trace.beginSection("SwipeHelper.invalidateGlobalRegion");
         invalidateGlobalRegion(
             view,
             new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
+        Trace.endSection();
     }
 
     // invalidate a rectangle relative to the view's coordinate system all the way up the view
@@ -492,7 +495,7 @@
                 }
                 if (!mCancelled || wasRemoved) {
                     mCallback.onChildDismissed(animView);
-                    resetSwipeOfView(animView);
+                    resetViewIfSwiping(animView);
                 }
                 if (endAction != null) {
                     endAction.accept(mCancelled);
@@ -547,7 +550,7 @@
 
             if (!cancelled) {
                 updateSwipeProgressFromOffset(animView, canBeDismissed);
-                resetSwipeOfView(animView);
+                resetViewIfSwiping(animView);
                 // Clear the snapped view after success, assuming it's not being swiped now
                 if (animView == mTouchedView && !mIsSwiping) {
                     mTouchedView = null;
@@ -811,7 +814,7 @@
         return mIsSwiping ? mTouchedView : null;
     }
 
-    protected void resetSwipeOfView(View view) {
+    protected void resetViewIfSwiping(View view) {
         if (getSwipedView() == view) {
             resetSwipeState();
         }
@@ -825,6 +828,12 @@
         resetSwipeStates(/* resetAll= */ true);
     }
 
+    public void forceResetSwipeState(@NonNull View view) {
+        if (view.getTranslationX() == 0) return;
+        setTranslation(view, 0);
+        updateSwipeProgressFromOffset(view, /* dismissable= */ true, 0);
+    }
+
     /** This method resets the swipe state, and if `resetAll` is true, also resets the snap state */
     private void resetSwipeStates(boolean resetAll) {
         final View touchedView = mTouchedView;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 634a7e3..bfba811 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -355,8 +355,7 @@
 
     // TODO(b/280426085): Tracking Bug
     @JvmField
-    val NEW_BLUETOOTH_REPOSITORY =
-            unreleasedFlag(612, "new_bluetooth_repository", teamfood = true)
+    val NEW_BLUETOOTH_REPOSITORY = releasedFlag(612, "new_bluetooth_repository")
 
     // 700 - dialer/calls
     // TODO(b/254512734): Tracking Bug
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 db23109..1c2e85b 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
@@ -404,8 +404,7 @@
             val darkClockColor = wallpaperColorScheme?.accent2?.s600
             /** Note that when [wallpaperColors] is null, isWallpaperDark is true. */
             val isWallpaperDark: Boolean =
-                (wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) !=
-                    WallpaperColors.HINT_SUPPORTS_DARK_TEXT
+                (wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0
             clock.events.onSeedColorChanged(
                 if (isWallpaperDark) lightClockColor else darkClockColor
             )
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 81fe3ca..fba0120 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -27,6 +27,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.fragments.FragmentService
 import com.android.systemui.navigationbar.NavigationModeController
@@ -45,6 +46,7 @@
 @VisibleForTesting
 internal const val INSET_DEBOUNCE_MILLIS = 500L
 
+@SysUISingleton
 class NotificationsQSContainerController @Inject constructor(
         view: NotificationsQuickSettingsContainer,
         private val navigationModeController: NavigationModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 1752ff6..7c161d5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -157,6 +157,15 @@
         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
         @Provides
         @SysUISingleton
+        fun providesNotificationsQuickSettingsContainer(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): NotificationsQuickSettingsContainer {
+            return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
         @Named(SHADE_HEADER)
         fun providesShadeHeaderView(
             notificationShadeWindowView: NotificationShadeWindowView,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 36025e8..0b5e436 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3992,7 +3992,7 @@
             mNotificationsController.resetUserExpandedStates();
             clearTemporaryViews();
             clearUserLockedViews();
-            cancelActiveSwipe();
+            resetAllSwipeState();
         }
     }
 
@@ -4058,7 +4058,7 @@
                 mGroupExpansionManager.collapseGroups();
                 mExpandHelper.cancelImmediately();
                 if (!mIsExpansionChanging) {
-                    cancelActiveSwipe();
+                    resetAllSwipeState();
                 }
                 finalizeClearAllAnimation();
             }
@@ -4387,7 +4387,7 @@
         boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
         if (nowFullyHidden != wasFullyHidden) {
             updateVisibility();
-            mSwipeHelper.resetTouchState();
+            resetAllSwipeState();
         }
         if (!wasHiddenAtAll && nowHiddenAtAll) {
             resetExposedMenuView(true /* animate */, true /* animate */);
@@ -5847,9 +5847,14 @@
         }
     }
 
-    private void cancelActiveSwipe() {
+    private void resetAllSwipeState() {
+        Trace.beginSection("NSSL.resetAllSwipeState()");
         mSwipeHelper.resetTouchState();
+        for (int i = 0; i < getChildCount(); i++) {
+            mSwipeHelper.forceResetSwipeState(getChildAt(i));
+        }
         updateContinuousShadowDrawing();
+        Trace.endSection();
     }
 
     void updateContinuousShadowDrawing() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index ce2658d..fc213b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -309,7 +309,8 @@
             float newNotificationEnd = newYTranslation + newHeight;
             boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned();
             if (mClipNotificationScrollToTop
-                    && ((isHeadsUp && !firstHeadsUp) || child.isHeadsUpAnimatingAway())
+                    && !firstHeadsUp
+                    && (isHeadsUp || child.isHeadsUpAnimatingAway())
                     && newNotificationEnd > firstHeadsUpEnd
                     && !ambientState.isShadeExpanded()) {
                 // The bottom of this view is peeking out from under the previous view.
@@ -613,13 +614,12 @@
                     updateViewWithShelf(view, viewState, shelfStart);
                 }
             }
-            // Avoid pulsing notification flicker during AOD to LS
-            // A pulsing notification is already expanded, no need to expand it again with animation
-            if (ambientState.isPulsingRow(view)) {
-                expansionFraction = 1.0f;
+            viewState.height = getMaxAllowedChildHeight(view);
+            if (!view.isPinned() && !view.isHeadsUpAnimatingAway()
+                    && !ambientState.isPulsingRow(view)) {
+                // The expansion fraction should not affect HUNs or pulsing notifications.
+                viewState.height *= expansionFraction;
             }
-            // Clip height of view right before shelf.
-            viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
         }
 
         algorithmState.mCurrentYPosition +=
@@ -779,6 +779,8 @@
                 }
             }
             if (row.isPinned()) {
+                // Make sure row yTranslation is at maximum the HUN yTranslation,
+                // which accounts for AmbientState.stackTopMargin in split-shade.
                 childState.setYTranslation(
                         Math.max(childState.getYTranslation(), headsUpTranslation));
                 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
@@ -803,7 +805,11 @@
                 }
             }
             if (row.isHeadsUpAnimatingAway()) {
-                childState.setYTranslation(Math.max(childState.getYTranslation(), mHeadsUpInset));
+                // Make sure row yTranslation is at maximum the HUN yTranslation,
+                // which accounts for AmbientState.stackTopMargin in split-shade.
+                childState.setYTranslation(
+                        Math.max(childState.getYTranslation(), headsUpTranslation));
+                // keep it visible for the animation
                 childState.hidden = false;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 4ccbc5a..76409c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -27,8 +27,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shade.NotificationPanelView;
 import com.android.systemui.shade.NotificationPanelViewController;
-import com.android.systemui.shade.NotificationShadeWindowView;
-import com.android.systemui.shade.NotificationsQuickSettingsContainer;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -122,14 +120,6 @@
     abstract ShadeViewController bindsShadeViewController(
             NotificationPanelViewController notificationPanelViewController);
 
-    /** */
-    @Provides
-    @CentralSurfacesComponent.CentralSurfacesScope
-    public static NotificationsQuickSettingsContainer getNotificationsQuickSettingsContainer(
-            NotificationShadeWindowView notificationShadeWindowView) {
-        return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
-    }
-
     @Binds
     @IntoSet
     abstract StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index f59fd99..e561f1f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -587,21 +587,6 @@
     }
 
     @Test
-    public void testSecurityCallbackFinish() {
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.isUserUnlocked(0)).thenReturn(true);
-        mKeyguardSecurityContainerController.finish(true, 0);
-        verify(mViewMediatorCallback).keyguardDone(anyBoolean(), anyInt());
-    }
-
-    @Test
-    public void testSecurityCallbackFinish_cannotDismissLockScreenAndNotStrongAuth() {
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
-        mKeyguardSecurityContainerController.finish(false, 0);
-        verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt());
-    }
-
-    @Test
     public void testOnStartingToHide() {
         mKeyguardSecurityContainerController.onStartingToHide();
         verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 7632d01..df65c09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -29,6 +30,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.animation.Animator;
@@ -670,6 +672,28 @@
     }
 
     @Test
+    public void testForceResetSwipeStateDoesNothingIfTranslationIsZero() {
+        doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
+        doReturn(0f).when(mNotificationRow).getTranslationX();
+
+        mSwipeHelper.forceResetSwipeState(mNotificationRow);
+
+        verify(mNotificationRow).getTranslationX();
+        verifyNoMoreInteractions(mNotificationRow);
+    }
+
+    @Test
+    public void testForceResetSwipeStateResetsTranslationAndAlpha() {
+        doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
+        doReturn(10f).when(mNotificationRow).getTranslationX();
+
+        mSwipeHelper.forceResetSwipeState(mNotificationRow);
+
+        verify(mNotificationRow).setTranslation(eq(0f));
+        verify(mNotificationRow).setContentAlpha(eq(1f));
+    }
+
+    @Test
     public void testContentAlphaRemainsUnchangedWhenFeatureFlagIsDisabled() {
 
         // Returning true prevents alpha fade. In an unmocked scenario the callback is instantiated
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 7b1565e..d107a0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -34,7 +34,6 @@
 @SmallTest
 class StackScrollAlgorithmTest : SysuiTestCase() {
 
-
     @JvmField @Rule
     var expect: Expect = Expect.create()
 
@@ -81,26 +80,58 @@
     fun resetViewStates_defaultHun_yTranslationIsInset() {
         whenever(notificationRow.isPinned).thenReturn(true)
         whenever(notificationRow.isHeadsUp).thenReturn(true)
-
-        stackScrollAlgorithm.resetViewStates(ambientState, 0)
-
-        assertThat(notificationRow.viewState.yTranslation)
-                .isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
+        resetViewStates_hunYTranslationIsInset()
     }
 
     @Test
-    fun resetViewStates_stackMargin_changesHunYTranslation() {
+    fun resetViewStates_defaultHunWithStackMargin_changesHunYTranslation() {
         whenever(notificationRow.isPinned).thenReturn(true)
         whenever(notificationRow.isHeadsUp).thenReturn(true)
-        val minHeadsUpTranslation = context.resources
-                .getDimensionPixelSize(R.dimen.notification_side_paddings)
+        resetViewStates_stackMargin_changesHunYTranslation()
+    }
 
-        // split shade case with top margin introduced by shade's status bar
-        ambientState.stackTopMargin = 100
-        stackScrollAlgorithm.resetViewStates(ambientState, 0)
+    @Test
+    fun resetViewStates_hunAnimatingAway_yTranslationIsInset() {
+        whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+        resetViewStates_hunYTranslationIsInset()
+    }
 
-        // top margin presence should decrease heads up translation up to minHeadsUpTranslation
-        assertThat(notificationRow.viewState.yTranslation).isEqualTo(minHeadsUpTranslation)
+    @Test
+    fun resetViewStates_hunAnimatingAway_StackMarginChangesHunYTranslation() {
+        whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+        resetViewStates_stackMargin_changesHunYTranslation()
+    }
+
+    @Test
+    fun resetViewStates_hunAnimatingAway_bottomNotClipped() {
+        whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+        stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+        assertThat(notificationRow.viewState.clipBottomAmount).isEqualTo(0)
+    }
+
+    @Test
+    fun resetViewStates_hunsOverlapping_bottomHunClipped() {
+        val topHun = mockExpandableNotificationRow()
+        val bottomHun = mockExpandableNotificationRow()
+        whenever(topHun.isHeadsUp).thenReturn(true)
+        whenever(topHun.isPinned).thenReturn(true)
+        whenever(bottomHun.isHeadsUp).thenReturn(true)
+        whenever(bottomHun.isPinned).thenReturn(true)
+
+        resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun)
+    }
+
+    @Test
+    fun resetViewStates_hunsOverlappingAndBottomHunAnimatingAway_bottomHunClipped() {
+        val topHun = mockExpandableNotificationRow()
+        val bottomHun = mockExpandableNotificationRow()
+        whenever(topHun.isHeadsUp).thenReturn(true)
+        whenever(topHun.isPinned).thenReturn(true)
+        whenever(bottomHun.isHeadsUpAnimatingAway).thenReturn(true)
+
+        resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun)
     }
 
     @Test
@@ -835,6 +866,57 @@
         ambientState.stackHeight = ambientState.stackEndHeight * fraction
     }
 
+    private fun resetViewStates_hunYTranslationIsInset() {
+        stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+        assertThat(notificationRow.viewState.yTranslation)
+                .isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
+    }
+
+    private fun resetViewStates_stackMargin_changesHunYTranslation() {
+        val stackTopMargin = 50
+        val headsUpTranslationY = stackScrollAlgorithm.mHeadsUpInset - stackTopMargin
+
+        // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild
+        ambientState.shelf = notificationShelf
+
+        // split shade case with top margin introduced by shade's status bar
+        ambientState.stackTopMargin = stackTopMargin
+        stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+        // heads up translation should be decreased by the top margin
+        assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTranslationY)
+    }
+
+    private fun resetViewStates_hunsOverlapping_bottomHunClipped(
+            topHun: ExpandableNotificationRow,
+            bottomHun: ExpandableNotificationRow
+    ) {
+        val topHunHeight = mContext.resources.getDimensionPixelSize(
+                R.dimen.notification_content_min_height)
+        val bottomHunHeight = mContext.resources.getDimensionPixelSize(
+                R.dimen.notification_max_heads_up_height)
+        whenever(topHun.intrinsicHeight).thenReturn(topHunHeight)
+        whenever(bottomHun.intrinsicHeight).thenReturn(bottomHunHeight)
+
+        // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild
+        ambientState.shelf = notificationShelf
+
+        // add two overlapping HUNs
+        hostView.removeAllViews()
+        hostView.addView(topHun)
+        hostView.addView(bottomHun)
+
+        stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+        // the height shouldn't change
+        assertThat(topHun.viewState.height).isEqualTo(topHunHeight)
+        assertThat(bottomHun.viewState.height).isEqualTo(bottomHunHeight)
+        // the HUN at the bottom should be clipped
+        assertThat(topHun.viewState.clipBottomAmount).isEqualTo(0)
+        assertThat(bottomHun.viewState.clipBottomAmount).isEqualTo(bottomHunHeight - topHunHeight)
+    }
+
     private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
             expansionFraction: Float,
             expectedAlpha: Float,
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3caeeae..fc971e4 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -283,7 +283,7 @@
     private static final long ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS = 60 * 1000L;
 
     /**
-     * Apps targeting Android U and above need to define
+     * Apps targeting Android V and above need to define
      * {@link android.Manifest.permission#TURN_SCREEN_ON} in their manifest for
      * {@link android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP} to have any effect.
      * Note that most applications should use {@link android.R.attr#turnScreenOn} or
@@ -291,7 +291,7 @@
      * previous foreground app from being resumed first when the screen turns on.
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
     public static final long REQUIRE_TURN_SCREEN_ON_PERMISSION = 216114297L;
 
     /** Reason ID for holding display suspend blocker. */
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 6509591..a0e6cf5 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -82,7 +82,10 @@
     ],
 
     platform_apis: true,
-    test_suites: ["device-tests"],
+    test_suites: [
+        "device-tests",
+        "automotive-tests",
+    ],
 
     certificate: "platform",