Merge "Only set SYNC flag if >1 track" into udc-qpr-dev
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 94bff89..4700720 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -370,8 +370,9 @@
 
     /**
      * Returns the default size of the surface associated with the display, or null if the surface
-     * is not provided for layer mirroring by SurfaceFlinger.
-     * Only used for mirroring started from MediaProjection.
+     * is not provided for layer mirroring by SurfaceFlinger. Size is rotated to reflect the current
+     * display device orientation.
+     * Used for mirroring from MediaProjection, or a physical display based on display flags.
      */
     public abstract Point getDisplaySurfaceDefaultSize(int displayId);
 
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 75640bd..f3b4c6d 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -92,6 +92,7 @@
                 mapParcel.recycle();
                 if (buffer != null) {
                     mRankingMapFd.unmap(buffer);
+                    mRankingMapFd.close();
                 }
             }
         } else {
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index a84ac55..55ded9c 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -136,7 +136,11 @@
         NotificationListenerService.RankingMap retrievedRankings =
                 retrievedRankingUpdate.getRankingMap();
         assertNotNull(retrievedRankings);
-        assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+        // The rankingUpdate file descriptor is only non-null in the new path.
+        if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+                SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+            assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+        }
         NotificationListenerService.Ranking retrievedRanking =
                 new NotificationListenerService.Ranking();
         assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
diff --git a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
index 78cd718..39ec09b 100644
--- a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
+++ b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
@@ -34,8 +34,8 @@
         android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
         android:contentDescription="@string/screenshot_dismiss_work_profile">
         <ImageView
-            android:layout_width="16dp"
-            android:layout_height="16dp"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
             android:layout_gravity="center"
             android:background="@drawable/circular_background"
             android:backgroundTint="?androidprv:attr/materialColorSurfaceContainerHigh"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 04692c4..9f3908a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -27,6 +27,7 @@
 import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER;
 import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
 import static com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES;
 
 import android.app.ActivityManager;
@@ -370,8 +371,12 @@
 
                 @Override
                 public void onOrientationChanged(int orientation) {
-                    KeyguardSecurityContainerController.this
+                    if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
+                        // TODO(b/295603468)
+                        // Fix reinflation of views when flag is enabled.
+                        KeyguardSecurityContainerController.this
                             .onDensityOrFontScaleOrOrientationChanged();
+                    }
                 }
             };
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index a6ada33..7ed3c26 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -623,6 +623,10 @@
     // TODO(b/251205791): Tracking Bug
     @JvmField val SCREENSHOT_APP_CLIPS = releasedFlag("screenshot_app_clips")
 
+    /** TODO(b/295143676): Tracking bug. When enable, captures a screenshot for each display. */
+    @JvmField
+    val MULTI_DISPLAY_SCREENSHOT = unreleasedFlag("multi_display_screenshot")
+
     // 1400 - columbus
     // TODO(b/254512756): Tracking Bug
     val QUICK_TAP_IN_PCC = releasedFlag("quick_tap_in_pcc")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index 635961b..e501ece 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -54,7 +54,11 @@
         if (event.handleAction()) {
             when (event.keyCode) {
                 KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
-                KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
+                KeyEvent.KEYCODE_SPACE,
+                KeyEvent.KEYCODE_ENTER ->
+                    if (isDeviceInteractive()) {
+                        return collapseShadeLockedOrShowPrimaryBouncer()
+                    }
             }
         }
         return false
@@ -90,16 +94,22 @@
                 (statusBarStateController.state != StatusBarState.SHADE) &&
                 statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
         if (shouldUnlockOnMenuPressed) {
-            shadeController.animateCollapseShadeForced()
-            return true
+            return collapseShadeLockedOrShowPrimaryBouncer()
         }
         return false
     }
 
-    private fun dispatchSpaceEvent(): Boolean {
-        if (isDeviceInteractive() && statusBarStateController.state != StatusBarState.SHADE) {
-            shadeController.animateCollapseShadeForced()
-            return true
+    private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
+        when (statusBarStateController.state) {
+            StatusBarState.SHADE -> return false
+            StatusBarState.SHADE_LOCKED -> {
+                shadeController.animateCollapseShadeForced()
+                return true
+            }
+            StatusBarState.KEYGUARD -> {
+                statusBarKeyguardViewManager.showPrimaryBouncer(true)
+                return true
+            }
         }
         return false
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 832a25b..802ed80 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -21,8 +21,6 @@
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.app.StatusBarManager;
-import android.media.AudioManager;
-import android.media.session.MediaSessionLegacyHelper;
 import android.os.PowerManager;
 import android.util.Log;
 import android.view.GestureDetector;
@@ -47,6 +45,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -101,6 +100,7 @@
     private final NotificationInsetsController mNotificationInsetsController;
     private final boolean mIsTrackpadCommonEnabled;
     private final FeatureFlags mFeatureFlags;
+    private final KeyEventInteractor mKeyEventInteractor;
     private GestureDetector mPulsingWakeupGestureHandler;
     private GestureDetector mDreamingWakeupGestureHandler;
     private View mBrightnessMirror;
@@ -164,7 +164,8 @@
             FeatureFlags featureFlags,
             SystemClock clock,
             BouncerMessageInteractor bouncerMessageInteractor,
-            BouncerLogger bouncerLogger) {
+            BouncerLogger bouncerLogger,
+            KeyEventInteractor keyEventInteractor) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
         mStatusBarStateController = statusBarStateController;
@@ -190,6 +191,7 @@
         mNotificationInsetsController = notificationInsetsController;
         mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
         mFeatureFlags = featureFlags;
+        mKeyEventInteractor = keyEventInteractor;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -457,44 +459,17 @@
 
             @Override
             public boolean interceptMediaKey(KeyEvent event) {
-                return mService.interceptMediaKey(event);
+                return mKeyEventInteractor.interceptMediaKey(event);
             }
 
             @Override
             public boolean dispatchKeyEventPreIme(KeyEvent event) {
-                return mService.dispatchKeyEventPreIme(event);
+                return mKeyEventInteractor.dispatchKeyEventPreIme(event);
             }
 
             @Override
             public boolean dispatchKeyEvent(KeyEvent event) {
-                boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
-                switch (event.getKeyCode()) {
-                    case KeyEvent.KEYCODE_BACK:
-                        if (!down) {
-                            mBackActionInteractor.onBackRequested();
-                        }
-                        return true;
-                    case KeyEvent.KEYCODE_MENU:
-                        if (!down) {
-                            return mService.onMenuPressed();
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_SPACE:
-                        if (!down) {
-                            return mService.onSpacePressed();
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_VOLUME_DOWN:
-                    case KeyEvent.KEYCODE_VOLUME_UP:
-                        if (mStatusBarStateController.isDozing()) {
-                            MediaSessionLegacyHelper.getHelper(mView.getContext())
-                                    .sendVolumeKeyEvent(
-                                            event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
-                            return true;
-                        }
-                        break;
-                }
-                return false;
+                return mKeyEventInteractor.dispatchKeyEvent(event);
             }
         });
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 5c28be3..af09bf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -25,7 +25,6 @@
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.UserHandle;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationAdapter;
 import android.view.View;
@@ -276,19 +275,11 @@
 
     void userActivity();
 
-    boolean interceptMediaKey(KeyEvent event);
-
-    boolean dispatchKeyEventPreIme(KeyEvent event);
-
-    boolean onMenuPressed();
-
     void endAffordanceLaunch();
 
     /** Should the keyguard be hidden immediately in response to a back press/gesture. */
     boolean shouldKeyguardHideImmediately();
 
-    boolean onSpacePressed();
-
     void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
             Runnable cancelAction);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 8ffd43a..ccb87bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -89,7 +89,6 @@
 import android.view.Display;
 import android.view.IRemoteAnimationRunner;
 import android.view.IWindowManager;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.ThreadedRenderer;
 import android.view.View;
@@ -2636,44 +2635,6 @@
     }
 
     @Override
-    public boolean interceptMediaKey(KeyEvent event) {
-        return mState == StatusBarState.KEYGUARD
-                && mStatusBarKeyguardViewManager.interceptMediaKey(event);
-    }
-
-    /**
-     * While IME is active and a BACK event is detected, check with
-     * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event
-     * should be handled before routing to IME, in order to prevent the user having to hit back
-     * twice to exit bouncer.
-     */
-    @Override
-    public boolean dispatchKeyEventPreIme(KeyEvent event) {
-        switch (event.getKeyCode()) {
-            case KeyEvent.KEYCODE_BACK:
-                if (mState == StatusBarState.KEYGUARD
-                        && mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) {
-                    return mBackActionInteractor.onBackRequested();
-                }
-        }
-        return false;
-    }
-
-    protected boolean shouldUnlockOnMenuPressed() {
-        return mDeviceInteractive && mState != StatusBarState.SHADE
-            && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed();
-    }
-
-    @Override
-    public boolean onMenuPressed() {
-        if (shouldUnlockOnMenuPressed()) {
-            mShadeController.animateCollapseShadeForced();
-            return true;
-        }
-        return false;
-    }
-
-    @Override
     public void endAffordanceLaunch() {
         releaseGestureWakeLock();
         mCameraLauncherLazy.get().setLaunchingAffordance(false);
@@ -2692,15 +2653,6 @@
         return (isScrimmedBouncer || isBouncerOverDream);
     }
 
-    @Override
-    public boolean onSpacePressed() {
-        if (mDeviceInteractive && mState != StatusBarState.SHADE) {
-            mShadeController.animateCollapseShadeForced();
-            return true;
-        }
-        return false;
-    }
-
     private void showBouncerOrLockScreenIfKeyguard() {
         // If the keyguard is animating away, we aren't really the keyguard anymore and should not
         // show the bouncer/lockscreen.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index a9aed2f..b5317fa 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -620,6 +620,51 @@
         configurationListenerArgumentCaptor.value.onUiModeChanged()
         verify(view).reloadColors()
     }
+    @Test
+    fun onOrientationChanged_landscapeKeyguardFlagDisabled_blockReinflate() {
+        featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
+
+        // Run onOrientationChanged
+        val configurationListenerArgumentCaptor =
+            ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
+        underTest.onViewAttached()
+        verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture())
+        clearInvocations(viewFlipperController)
+        configurationListenerArgumentCaptor.value.onOrientationChanged(
+            Configuration.ORIENTATION_LANDSCAPE
+        )
+        // Verify view is reinflated when flag is on
+        verify(viewFlipperController, never()).clearViews()
+        verify(viewFlipperController, never())
+            .asynchronouslyInflateView(
+                eq(SecurityMode.PIN),
+                any(),
+                onViewInflatedCallbackArgumentCaptor.capture()
+            )
+    }
+
+    @Test
+    fun onOrientationChanged_landscapeKeyguardFlagEnabled_doesReinflate() {
+        featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true)
+
+        // Run onOrientationChanged
+        val configurationListenerArgumentCaptor =
+            ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
+        underTest.onViewAttached()
+        verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture())
+        clearInvocations(viewFlipperController)
+        configurationListenerArgumentCaptor.value.onOrientationChanged(
+            Configuration.ORIENTATION_LANDSCAPE
+        )
+        // Verify view is reinflated when flag is on
+        verify(viewFlipperController).clearViews()
+        verify(viewFlipperController)
+            .asynchronouslyInflateView(
+                eq(SecurityMode.PIN),
+                any(),
+                onViewInflatedCallbackArgumentCaptor.capture()
+            )
+    }
 
     @Test
     fun hasDismissActions() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index a3f7fc5..e0ae0c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -131,14 +131,12 @@
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
+    fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_showsPrimaryBouncer() {
         keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
@@ -147,42 +145,48 @@
         whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        // action down: does NOT collapse the shade
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
-
-        // action up: collapses the shade
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
+    fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_doNothing() {
         keyguardInteractorWithDependencies.repository.setWakefulnessModel(asleepWakefulnessMode)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
+        verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
-    fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
+    fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_showsPrimaryBouncer() {
         keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
 
-        // action down: does NOT collapse the shade
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
+    }
 
-        // action up: collapses the shade
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+    @Test
+    fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
+        keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+        keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
+        keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
     }
 
     @Test
@@ -252,4 +256,42 @@
             .isFalse()
         verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
     }
+
+    private fun verifyActionUpCollapsesTheShade(keycode: Int) {
+        // action down: does NOT collapse the shade
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+
+        // action up: collapses the shade
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(shadeController).animateCollapseShadeForced()
+    }
+
+    private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
+        // action down: does NOT collapse the shade
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+
+        // action up: collapses the shade
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
+    }
+
+    private fun verifyActionsDoNothing(keycode: Int) {
+        // action down: does nothing
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+
+        // action up: doesNothing
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 1edeeff..dc506a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -18,6 +18,7 @@
 
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
+import android.view.KeyEvent
 import android.view.MotionEvent
 import android.view.ViewGroup
 import androidx.test.filters.SmallTest
@@ -38,6 +39,7 @@
 import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -118,6 +120,7 @@
     @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
     @Mock
     lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
+    @Mock lateinit var keyEventInteractor: KeyEventInteractor
     private val notificationExpansionRepository = NotificationExpansionRepository()
 
     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
@@ -188,7 +191,8 @@
                         CountDownTimerUtil(),
                         featureFlags
                     ),
-                    BouncerLogger(logcatLogBuffer("BouncerLog"))
+                    BouncerLogger(logcatLogBuffer("BouncerLog")),
+                    keyEventInteractor,
             )
         underTest.setupExpandedStatusBar()
 
@@ -345,6 +349,27 @@
             verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
         }
 
+    @Test
+    fun forwardsDispatchKeyEvent() {
+        val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
+        interactionEventHandler.dispatchKeyEvent(keyEvent)
+        verify(keyEventInteractor).dispatchKeyEvent(keyEvent)
+    }
+
+    @Test
+    fun forwardsDispatchKeyEventPreIme() {
+        val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
+        interactionEventHandler.dispatchKeyEventPreIme(keyEvent)
+        verify(keyEventInteractor).dispatchKeyEventPreIme(keyEvent)
+    }
+
+    @Test
+    fun forwardsInterceptMediaKey() {
+        val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
+        interactionEventHandler.interceptMediaKey(keyEvent)
+        verify(keyEventInteractor).interceptMediaKey(keyEvent)
+    }
+
     companion object {
         private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
         private const val VIEW_BOTTOM = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 829184c..66d48d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
@@ -198,7 +199,8 @@
                     CountDownTimerUtil(),
                     featureFlags
                 ),
-                BouncerLogger(logcatLogBuffer("BouncerLog"))
+                BouncerLogger(logcatLogBuffer("BouncerLog")),
+                Mockito.mock(KeyEventInteractor::class.java),
             )
 
         controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 21e4f5a..a6a2761 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -32,7 +32,6 @@
 import static org.junit.Assume.assumeNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
@@ -480,123 +479,45 @@
 
     @Test
     public void ifPortraitHalfOpen_drawVerticallyTop() {
-        DevicePostureController devicePostureController = mock(DevicePostureController.class);
-        when(devicePostureController.getDevicePosture())
-                .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
-
-        VolumeDialogImpl dialog = new VolumeDialogImpl(
-                getContext(),
-                mVolumeDialogController,
-                mAccessibilityMgr,
-                mDeviceProvisionedController,
-                mConfigurationController,
-                mMediaOutputDialogFactory,
-                mVolumePanelFactory,
-                mActivityStarter,
-                mInteractionJankMonitor,
-                false,
-                mCsdWarningDialogFactory,
-                devicePostureController,
-                mTestableLooper.getLooper(),
-                mDumpManager,
-                mFeatureFlags
-        );
-        dialog.init(0 , null);
-
-        verify(devicePostureController).addCallback(any());
-        dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
+        mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
         mTestableLooper.processAllMessages(); // let dismiss() finish
 
         setOrientation(Configuration.ORIENTATION_PORTRAIT);
 
         // Call show() to trigger layout updates before verifying position
-        dialog.show(SHOW_REASON_UNKNOWN);
+        mDialog.show(SHOW_REASON_UNKNOWN);
         mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
 
-        int gravity = dialog.getWindowGravity();
+        int gravity = mDialog.getWindowGravity();
         assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK);
-
-        cleanUp(dialog);
     }
 
     @Test
     public void ifPortraitAndOpen_drawCenterVertically() {
-        DevicePostureController devicePostureController = mock(DevicePostureController.class);
-        when(devicePostureController.getDevicePosture())
-                .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
-
-        VolumeDialogImpl dialog = new VolumeDialogImpl(
-                getContext(),
-                mVolumeDialogController,
-                mAccessibilityMgr,
-                mDeviceProvisionedController,
-                mConfigurationController,
-                mMediaOutputDialogFactory,
-                mVolumePanelFactory,
-                mActivityStarter,
-                mInteractionJankMonitor,
-                false,
-                mCsdWarningDialogFactory,
-                devicePostureController,
-                mTestableLooper.getLooper(),
-                mDumpManager,
-                mFeatureFlags
-        );
-        dialog.init(0, null);
-
-        verify(devicePostureController).addCallback(any());
-        dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED);
+        mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED);
         mTestableLooper.processAllMessages(); // let dismiss() finish
 
         setOrientation(Configuration.ORIENTATION_PORTRAIT);
 
-        dialog.show(SHOW_REASON_UNKNOWN);
+        mDialog.show(SHOW_REASON_UNKNOWN);
         mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
 
-        int gravity = dialog.getWindowGravity();
+        int gravity = mDialog.getWindowGravity();
         assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK);
-
-        cleanUp(dialog);
     }
 
     @Test
     public void ifLandscapeAndHalfOpen_drawCenterVertically() {
-        DevicePostureController devicePostureController = mock(DevicePostureController.class);
-        when(devicePostureController.getDevicePosture())
-                .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
-
-        VolumeDialogImpl dialog = new VolumeDialogImpl(
-                getContext(),
-                mVolumeDialogController,
-                mAccessibilityMgr,
-                mDeviceProvisionedController,
-                mConfigurationController,
-                mMediaOutputDialogFactory,
-                mVolumePanelFactory,
-                mActivityStarter,
-                mInteractionJankMonitor,
-                false,
-                mCsdWarningDialogFactory,
-                devicePostureController,
-                mTestableLooper.getLooper(),
-                mDumpManager,
-                mFeatureFlags
-        );
-        dialog.init(0, null);
-
-        verify(devicePostureController).addCallback(any());
-        dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
+        mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
         mTestableLooper.processAllMessages(); // let dismiss() finish
 
         setOrientation(Configuration.ORIENTATION_LANDSCAPE);
 
-        dialog.show(SHOW_REASON_UNKNOWN);
+        mDialog.show(SHOW_REASON_UNKNOWN);
         mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
 
-        int gravity = dialog.getWindowGravity();
+        int gravity = mDialog.getWindowGravity();
         assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK);
-
-        cleanUp(dialog);
     }
 
     @Test
@@ -607,31 +528,9 @@
 
     @Test
     public void dialogDestroy_removesPostureControllerCallback() {
-        VolumeDialogImpl dialog = new VolumeDialogImpl(
-                getContext(),
-                mVolumeDialogController,
-                mAccessibilityMgr,
-                mDeviceProvisionedController,
-                mConfigurationController,
-                mMediaOutputDialogFactory,
-                mVolumePanelFactory,
-                mActivityStarter,
-                mInteractionJankMonitor,
-                false,
-                mCsdWarningDialogFactory,
-                mPostureController,
-                mTestableLooper.getLooper(),
-                mDumpManager,
-                mFeatureFlags
-        );
-        dialog.init(0, null);
-
         verify(mPostureController, never()).removeCallback(any());
-        dialog.destroy();
-
+        mDialog.destroy();
         verify(mPostureController).removeCallback(any());
-
-        cleanUp(dialog);
     }
 
     private void setOrientation(int orientation) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9f9e2eb..533ecf1 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -391,7 +391,6 @@
         final boolean wasBtScoRequested = isBluetoothScoRequested();
         CommunicationRouteClient client;
 
-
         // Save previous client route in case of failure to start BT SCO audio
         AudioDeviceAttributes prevClientDevice = null;
         boolean prevPrivileged = false;
@@ -1043,7 +1042,7 @@
         synchronized (mBluetoothAudioStateLock) {
             mBluetoothScoOn = on;
             updateAudioHalBluetoothState();
-            postUpdateCommunicationRouteClient(eventSource);
+            postUpdateCommunicationRouteClient(isBluetoothScoRequested(), eventSource);
         }
     }
 
@@ -1395,8 +1394,10 @@
                 MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
     }
 
-    /*package*/ void postUpdateCommunicationRouteClient(String eventSource) {
-        sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, eventSource);
+    /*package*/ void postUpdateCommunicationRouteClient(
+            boolean wasBtScoRequested, String eventSource) {
+        sendILMsgNoDelay(MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE,
+                wasBtScoRequested ? 1 : 0, eventSource);
     }
 
     /*package*/ void postSetCommunicationDeviceForClient(CommunicationDeviceInfo info) {
@@ -1708,7 +1709,8 @@
                                             : AudioSystem.STREAM_DEFAULT);
                             if (btInfo.mProfile == BluetoothProfile.LE_AUDIO
                                     || btInfo.mProfile == BluetoothProfile.HEARING_AID) {
-                                onUpdateCommunicationRouteClient("setBluetoothActiveDevice");
+                                onUpdateCommunicationRouteClient(isBluetoothScoRequested(),
+                                        "setBluetoothActiveDevice");
                             }
                         }
                     }
@@ -1762,9 +1764,11 @@
                 case MSG_I_SET_MODE_OWNER:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
+                            boolean wasBtScoRequested = isBluetoothScoRequested();
                             mAudioModeOwner = (AudioModeInfo) msg.obj;
                             if (mAudioModeOwner.mMode != AudioSystem.MODE_RINGTONE) {
-                                onUpdateCommunicationRouteClient("setNewModeOwner");
+                                onUpdateCommunicationRouteClient(
+                                        wasBtScoRequested, "setNewModeOwner");
                             }
                         }
                     }
@@ -1787,10 +1791,10 @@
                     }
                     break;
 
-                case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT:
+                case MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
-                            onUpdateCommunicationRouteClient((String) msg.obj);
+                            onUpdateCommunicationRouteClient(msg.arg1 == 1, (String) msg.obj);
                         }
                     }
                     break;
@@ -1973,7 +1977,7 @@
     private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
 
     private static final int MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT = 42;
-    private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
+    private static final int MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
     private static final int MSG_I_SCO_AUDIO_STATE_CHANGED = 44;
 
     private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45;
@@ -2330,16 +2334,20 @@
      */
     // @GuardedBy("mSetModeLock")
     @GuardedBy("mDeviceStateLock")
-    private void onUpdateCommunicationRouteClient(String eventSource) {
-        updateCommunicationRoute(eventSource);
+    private void onUpdateCommunicationRouteClient(boolean wasBtScoRequested, String eventSource) {
         CommunicationRouteClient crc = topCommunicationRouteClient();
         if (AudioService.DEBUG_COMM_RTE) {
-            Log.v(TAG, "onUpdateCommunicationRouteClient, crc: "
-                    + crc + " eventSource: " + eventSource);
+            Log.v(TAG, "onUpdateCommunicationRouteClient, crc: " + crc
+                    + " wasBtScoRequested: " + wasBtScoRequested + " eventSource: " + eventSource);
         }
         if (crc != null) {
             setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
                     BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
+        } else {
+            if (!isBluetoothScoRequested() && wasBtScoRequested) {
+                mBtHelper.stopBluetoothSco(eventSource);
+            }
+            updateCommunicationRoute(eventSource);
         }
     }
 
@@ -2433,6 +2441,7 @@
             List<AudioRecordingConfiguration> recordConfigs) {
         synchronized (mSetModeLock) {
             synchronized (mDeviceStateLock) {
+                final boolean wasBtScoRequested = isBluetoothScoRequested();
                 boolean updateCommunicationRoute = false;
                 for (CommunicationRouteClient crc : mCommunicationRouteClients) {
                     boolean wasActive = crc.isActive();
@@ -2461,7 +2470,8 @@
                     }
                 }
                 if (updateCommunicationRoute) {
-                    postUpdateCommunicationRouteClient("updateCommunicationRouteClientsActivity");
+                    postUpdateCommunicationRouteClient(
+                            wasBtScoRequested, "updateCommunicationRouteClientsActivity");
                 }
             }
         }
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 3560797..a4d26d3 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -329,7 +329,7 @@
             default:
                 break;
         }
-        if(broadcast) {
+        if (broadcast) {
             broadcastScoConnectionState(scoAudioState);
             //FIXME: this is to maintain compatibility with deprecated intent
             // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
@@ -718,8 +718,10 @@
         checkScoAudioState();
         if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
             // Make sure that the state transitions to CONNECTING even if we cannot initiate
-            // the connection.
-            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+            // the connection except if already connected internally
+            if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
+                broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+            }
             switch (mScoAudioState) {
                 case SCO_STATE_INACTIVE:
                     mScoAudioMode = scoAudioMode;
@@ -775,7 +777,7 @@
                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
                     break;
                 case SCO_STATE_ACTIVE_INTERNAL:
-                    Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
+                    // Already in ACTIVE mode, simply return
                     break;
                 case SCO_STATE_ACTIVE_EXTERNAL:
                     /* Confirm SCO Audio connection to requesting app as it is already connected
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index d57dc47..8642fb8 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -16,6 +16,9 @@
 
 package com.android.server.display;
 
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Point;
@@ -132,12 +135,15 @@
     /**
      * Returns the default size of the surface associated with the display, or null if the surface
      * is not provided for layer mirroring by SurfaceFlinger. For non virtual displays, this will
-     * be the actual display device's size.
+     * be the actual display device's size, reflecting the current rotation.
      */
     @Nullable
     public Point getDisplaySurfaceDefaultSizeLocked() {
         DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked();
-        return new Point(displayDeviceInfo.width, displayDeviceInfo.height);
+        final boolean isRotated = mCurrentOrientation == ROTATION_90
+                || mCurrentOrientation == ROTATION_270;
+        return isRotated ? new Point(displayDeviceInfo.height, displayDeviceInfo.width)
+                : new Point(displayDeviceInfo.width, displayDeviceInfo.height);
     }
 
     /**
@@ -358,7 +364,7 @@
         }
 
         boolean isRotated = (mCurrentOrientation == Surface.ROTATION_90
-                || mCurrentOrientation == Surface.ROTATION_270);
+                || mCurrentOrientation == ROTATION_270);
         DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
         viewport.deviceWidth = isRotated ? info.height : info.width;
         viewport.deviceHeight = isRotated ? info.width : info.height;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index e5965ef..486cd28 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -2080,7 +2080,7 @@
 
     /** Loads the refresh rate profiles. */
     private void loadRefreshRateZoneProfiles(RefreshRateConfigs refreshRateConfigs) {
-        if (refreshRateConfigs == null) {
+        if (refreshRateConfigs == null || refreshRateConfigs.getRefreshRateZoneProfiles() == null) {
             return;
         }
         for (RefreshRateZone zone :
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
new file mode 100644
index 0000000..4fd8f26
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.display;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for the {@link DisplayDevice} class.
+ *
+ * Build/Install/Run:
+ * atest DisplayServicesTests:DisplayDeviceTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DisplayDeviceTest {
+    private final DisplayDeviceInfo mDisplayDeviceInfo = new DisplayDeviceInfo();
+    private static final int WIDTH = 500;
+    private static final int HEIGHT = 900;
+    private static final Point PORTRAIT_SIZE = new Point(WIDTH, HEIGHT);
+    private static final Point LANDSCAPE_SIZE = new Point(HEIGHT, WIDTH);
+
+    @Mock
+    private SurfaceControl.Transaction mMockTransaction;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mDisplayDeviceInfo.width = WIDTH;
+        mDisplayDeviceInfo.height = HEIGHT;
+        mDisplayDeviceInfo.rotation = ROTATION_0;
+    }
+
+    @Test
+    public void testGetDisplaySurfaceDefaultSizeLocked_notRotated() {
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
+    }
+
+    @Test
+    public void testGetDisplaySurfaceDefaultSizeLocked_rotation0() {
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        displayDevice.setProjectionLocked(mMockTransaction, ROTATION_0, new Rect(), new Rect());
+        assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
+    }
+
+    @Test
+    public void testGetDisplaySurfaceDefaultSizeLocked_rotation90() {
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        displayDevice.setProjectionLocked(mMockTransaction, ROTATION_90, new Rect(), new Rect());
+        assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
+    }
+
+    @Test
+    public void testGetDisplaySurfaceDefaultSizeLocked_rotation180() {
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        displayDevice.setProjectionLocked(mMockTransaction, ROTATION_180, new Rect(), new Rect());
+        assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
+    }
+
+    @Test
+    public void testGetDisplaySurfaceDefaultSizeLocked_rotation270() {
+        DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+        displayDevice.setProjectionLocked(mMockTransaction, ROTATION_270, new Rect(), new Rect());
+        assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
+    }
+
+    private static class FakeDisplayDevice extends DisplayDevice {
+        private final DisplayDeviceInfo mDisplayDeviceInfo;
+
+        FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo) {
+            super(null, null, "", InstrumentationRegistry.getInstrumentation().getContext());
+            mDisplayDeviceInfo = displayDeviceInfo;
+        }
+
+        @Override
+        public boolean hasStableUniqueId() {
+            return false;
+        }
+
+        @Override
+        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+            return mDisplayDeviceInfo;
+        }
+    }
+}