From fe766df5e0908d6678ec01dec4ec7d1ab95bc840 Mon Sep 17 00:00:00 2001 From: Oleg Blinnikov Date: Mon, 3 Apr 2023 16:06:25 +0000 Subject: [PATCH] Added OnSetRequestedOrientation callback DisplayRotation disables Auto-Rotation for immersive applications. But during the rotation of the device, application may change the requested orientation by calling ActivityRecord.setRequestedOrientation This CL makes sure that onProposedRotationChanged gets reevaluated if previously user was shown with the rotation button, and then setRequestedOrientation being invoked by the application. Bug: 275810916 Test: atest DisplayRotationTests ActivityRecordTests Change-Id: I4fe6ef7716398047a757a5c761f316f6145d00b6 Merged-In: I4fe6ef7716398047a757a5c761f316f6145d00b6 --- .../com/android/server/wm/ActivityRecord.java | 2 + .../android/server/wm/DisplayRotation.java | 22 +++++++++++ .../server/wm/ActivityRecordTests.java | 6 +++ .../server/wm/DisplayRotationTests.java | 39 ++++++++++++++++++- 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index acadf4a03db52..363163eab1392 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7867,6 +7867,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( task.mTaskId, requestedOrientation); + + mDisplayContent.getDisplayRotation().onSetRequestedOrientation(); } /* diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 27e33b08d9361..96bb424201c6a 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -92,6 +92,8 @@ public class DisplayRotation { // config changes and unexpected jumps while folding the device to closed state. private static final int FOLDING_RECOMPUTE_CONFIG_DELAY_MS = 800; + private static final int ROTATION_UNDEFINED = -1; + private static class RotationAnimationPair { @AnimRes int mEnter; @@ -179,6 +181,12 @@ public class DisplayRotation { */ private int mShowRotationSuggestions; + /** + * The most recent {@link Surface.Rotation} choice shown to the user for confirmation, or + * {@link #ROTATION_UNDEFINED} + */ + private int mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED; + private static final int ALLOW_ALL_ROTATIONS_UNDEFINED = -1; private static final int ALLOW_ALL_ROTATIONS_DISABLED = 0; private static final int ALLOW_ALL_ROTATIONS_ENABLED = 1; @@ -856,6 +864,7 @@ public class DisplayRotation { @VisibleForTesting void setUserRotation(int userRotationMode, int userRotation) { + mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED; if (isDefaultDisplay) { // We'll be notified via settings listener, so we don't need to update internal values. final ContentResolver res = mContext.getContentResolver(); @@ -1545,6 +1554,17 @@ public class DisplayRotation { return shouldUpdateRotation; } + /** + * Called from {@link ActivityRecord#setRequestedOrientation(int)} + */ + void onSetRequestedOrientation() { + if (mCompatPolicyForImmersiveApps == null + || mRotationChoiceShownToUserForConfirmation == ROTATION_UNDEFINED) { + return; + } + mOrientationListener.onProposedRotationChanged(mRotationChoiceShownToUserForConfirmation); + } + void dump(String prefix, PrintWriter pw) { pw.println(prefix + "DisplayRotation"); pw.println(prefix + " mCurrentAppOrientation=" @@ -1786,9 +1806,11 @@ public class DisplayRotation { // Send interaction power boost to improve redraw performance. mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0); if (isRotationChoiceAllowed(rotation)) { + mRotationChoiceShownToUserForConfirmation = rotation; final boolean isValid = isValidRotationChoice(rotation); sendProposedRotationChangeToStatusBarInternal(rotation, isValid); } else { + mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED; mService.updateRotation(false /* alwaysSendConfiguration */, false /* forceRelayout */); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 2cc752cd1b1ad..c91822279e7c3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -588,12 +588,18 @@ public class ActivityRecordTests extends WindowTestsBase { throw new IllegalStateException("Orientation in new config should be either" + "landscape or portrait."); } + + final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation(); + spyOn(displayRotation); + activity.setRequestedOrientation(requestedOrientation); final ActivityConfigurationChangeItem expected = ActivityConfigurationChangeItem.obtain(newConfig); verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()), eq(activity.token), eq(expected)); + + verify(displayRotation).onSetRequestedOrientation(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 192274317173a..52bd10c6093cb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -67,6 +67,7 @@ import android.view.DisplayAddress; import android.view.Surface; import android.view.WindowManager; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.util.test.FakeSettingsProvider; @@ -126,6 +127,8 @@ public class DisplayRotationTests { private DeviceStateController mDeviceStateController; private DisplayRotation mTarget; + @Nullable + private DisplayRotationImmersiveAppCompatPolicy mDisplayRotationImmersiveAppCompatPolicyMock; @BeforeClass public static void setUpOnce() { @@ -149,7 +152,7 @@ public class DisplayRotationTests { LocalServices.removeServiceForTest(StatusBarManagerInternal.class); mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class); LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); - + mDisplayRotationImmersiveAppCompatPolicyMock = null; mBuilder = new DisplayRotationBuilder(); } @@ -557,6 +560,38 @@ public class DisplayRotationTests { verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true); } + @Test + public void testNotifiesChoiceWhenSensorUpdates_immersiveApp() throws Exception { + mDisplayRotationImmersiveAppCompatPolicyMock = mock( + DisplayRotationImmersiveAppCompatPolicy.class); + when(mDisplayRotationImmersiveAppCompatPolicyMock.isRotationLockEnforced( + Surface.ROTATION_90)).thenReturn(true); + + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + thawRotation(); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); + assertTrue(waitForUiHandler()); + + verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true); + + // An imaginary ActivityRecord.setRequestedOrientation call disables immersive mode: + when(mDisplayRotationImmersiveAppCompatPolicyMock.isRotationLockEnforced( + Surface.ROTATION_90)).thenReturn(false); + + // And then ActivityRecord.setRequestedOrientation calls onSetRequestedOrientation. + mTarget.onSetRequestedOrientation(); + + // onSetRequestedOrientation should lead to a second call to + // mOrientationListener.onProposedRotationChanged + // but now, instead of notifying mMockStatusBarManagerInternal, it calls updateRotation: + verify(sMockWm).updateRotation(false, false); + } + @Test public void testAllowAllRotations_allowsUpsideDownSuggestion() throws Exception { @@ -1158,7 +1193,7 @@ public class DisplayRotationTests { @Override DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy( WindowManagerService service, DisplayContent displayContent) { - return null; + return mDisplayRotationImmersiveAppCompatPolicyMock; } };