From 5052f015fc8bcc0b8ce88c9d9d136f46be4b8776 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 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:fe766df5e0908d6678ec01dec4ec7d1ab95bc840) Change-Id: I4fe6ef7716398047a757a5c761f316f6145d00b6 --- .../com/android/server/wm/ActivityRecord.java | 2 + .../android/server/wm/DisplayRotation.java | 22 ++++++++++ .../server/wm/ActivityRecordTests.java | 6 +++ .../server/wm/DisplayRotationTests.java | 41 +++++++++++++++++-- 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 8346e7c83190d..b5fde9e7593b6 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7917,6 +7917,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 628f4d3a85d6e..c8fde6b5b3558 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -97,6 +97,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; @@ -189,6 +191,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; @@ -894,6 +902,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(); @@ -1613,6 +1622,17 @@ public class DisplayRotation { } } + /** + * 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=" @@ -2012,9 +2032,11 @@ public class DisplayRotation { mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0); dispatchProposedRotation(rotation); 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 b8a21ec4c0305..341b331b74e00 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -589,12 +589,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 495f868b1b114..19a1eddb4da72 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -70,6 +70,7 @@ import android.view.IRotationWatcher; import android.view.Surface; import android.view.WindowManager; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.internal.util.test.FakeSettingsProvider; @@ -140,6 +141,8 @@ public class DisplayRotationTests { private DeviceStateController mDeviceStateController; private TestDisplayRotation mTarget; + @Nullable + private DisplayRotationImmersiveAppCompatPolicy mDisplayRotationImmersiveAppCompatPolicyMock; @BeforeClass public static void setUpOnce() { @@ -165,7 +168,7 @@ public class DisplayRotationTests { LocalServices.removeServiceForTest(StatusBarManagerInternal.class); mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class); LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal); - + mDisplayRotationImmersiveAppCompatPolicyMock = null; mBuilder = new DisplayRotationBuilder(); } @@ -577,6 +580,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 { @@ -1404,7 +1439,7 @@ public class DisplayRotationTests { } } - private static class TestDisplayRotation extends DisplayRotation { + private class TestDisplayRotation extends DisplayRotation { IntConsumer mProposedRotationCallback; TestDisplayRotation(DisplayContent dc, DisplayAddress address, DisplayPolicy policy, @@ -1417,7 +1452,7 @@ public class DisplayRotationTests { @Override DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy( WindowManagerService service, DisplayContent displayContent) { - return null; + return mDisplayRotationImmersiveAppCompatPolicyMock; } @Override