Loading core/java/android/content/pm/ActivityInfo.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -1086,6 +1086,20 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION = public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION = 254631730L; // buganizer id 254631730L; // buganizer id /** * This change id enables compat policy that ignores app requested orientation in * response to an app calling {@link android.app.Activity#setRequestedOrientation} more * than twice in one second if an activity is not letterboxed for fixed orientation. * See com.android.server.wm.LetterboxUiController#shouldIgnoreRequestedOrientation * for details. * @hide */ @ChangeId @Overridable @Disabled public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = 273509367L; // buganizer id /** /** * This change id forces the packages it is applied to never have Display API sandboxing * This change id forces the packages it is applied to never have Display API sandboxing * applied for a letterbox or SCM activity. The Display APIs will continue to provide * applied for a letterbox or SCM activity. The Display APIs will continue to provide Loading services/core/java/com/android/server/wm/LetterboxUiController.java +84 −14 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FOR import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; Loading Loading @@ -130,6 +131,14 @@ final class LetterboxUiController { private static final float UNDEFINED_ASPECT_RATIO = 0f; private static final float UNDEFINED_ASPECT_RATIO = 0f; // Minimum value of mSetOrientationRequestCounter before qualifying as orientation request loop @VisibleForTesting static final int MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP = 2; // Used to determine reset of mSetOrientationRequestCounter if next app requested // orientation is after timeout value @VisibleForTesting static final int SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS = 1000; private final Point mTmpPoint = new Point(); private final Point mTmpPoint = new Point(); private final LetterboxConfiguration mLetterboxConfiguration; private final LetterboxConfiguration mLetterboxConfiguration; Loading Loading @@ -162,6 +171,8 @@ final class LetterboxUiController { // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION private final boolean mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled; private final boolean mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled; // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED private final boolean mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled; // Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS // Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS private final boolean mIsOverrideEnableCompatFakeFocusEnabled; private final boolean mIsOverrideEnableCompatFakeFocusEnabled; Loading @@ -186,12 +197,18 @@ final class LetterboxUiController { private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; // Updated when ActivityRecord#setRequestedOrientation is called private long mTimeMsLastSetOrientationRequest = 0; @Configuration.Orientation @Configuration.Orientation private int mInheritedOrientation = ORIENTATION_UNDEFINED; private int mInheritedOrientation = ORIENTATION_UNDEFINED; // The app compat state for the opaque activity if any // The app compat state for the opaque activity if any private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; // Counter for ActivityRecord#setRequestedOrientation private int mSetOrientationRequestCounter = 0; // The CompatDisplayInsets of the opaque activity beneath the translucent one. // The CompatDisplayInsets of the opaque activity beneath the translucent one. private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; Loading Loading @@ -284,6 +301,9 @@ final class LetterboxUiController { mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled = mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION); isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION); mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled = isCompatChangeEnabled( OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); mIsOverrideEnableCompatFakeFocusEnabled = mIsOverrideEnableCompatFakeFocusEnabled = isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS); isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS); Loading Loading @@ -354,20 +374,20 @@ final class LetterboxUiController { * <li>Opt-in component property or per-app override are enabled * <li>Opt-in component property or per-app override are enabled * <li>Activity is relaunched after {@link android.app.Activity#setRequestedOrientation} * <li>Activity is relaunched after {@link android.app.Activity#setRequestedOrientation} * call from an app or camera compat force rotation treatment is active for the activity. * call from an app or camera compat force rotation treatment is active for the activity. * <li>Orientation request loop detected and is not letterboxed for fixed orientation * </ul> * </ul> */ */ boolean shouldIgnoreRequestedOrientation(@ScreenOrientation int requestedOrientation) { boolean shouldIgnoreRequestedOrientation(@ScreenOrientation int requestedOrientation) { if (!shouldEnableWithOverrideAndProperty( if (shouldEnableWithOverrideAndProperty( /* gatingCondition */ mLetterboxConfiguration /* gatingCondition */ mLetterboxConfiguration ::isPolicyForIgnoringRequestedOrientationEnabled, ::isPolicyForIgnoringRequestedOrientationEnabled, mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled, mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled, mBooleanPropertyIgnoreRequestedOrientation)) { mBooleanPropertyIgnoreRequestedOrientation)) { return false; } if (mIsRelauchingAfterRequestedOrientationChanged) { if (mIsRelauchingAfterRequestedOrientationChanged) { Slog.w(TAG, "Ignoring orientation update to " Slog.w(TAG, "Ignoring orientation update to " + screenOrientationToString(requestedOrientation) + screenOrientationToString(requestedOrientation) + " due to relaunching after setRequestedOrientation for " + mActivityRecord); + " due to relaunching after setRequestedOrientation for " + mActivityRecord); return true; return true; } } DisplayContent displayContent = mActivityRecord.mDisplayContent; DisplayContent displayContent = mActivityRecord.mDisplayContent; Loading @@ -382,9 +402,59 @@ final class LetterboxUiController { + " due to camera compat treatment for " + mActivityRecord); + " due to camera compat treatment for " + mActivityRecord); return true; return true; } } } if (shouldIgnoreOrientationRequestLoop()) { Slog.w(TAG, "Ignoring orientation update to " + screenOrientationToString(requestedOrientation) + " as orientation request loop was detected for " + mActivityRecord); return true; } return false; } /** * Whether an app is calling {@link android.app.Activity#setRequestedOrientation} * in a loop and orientation request should be ignored. * * <p>This should only be called once in response to * {@link android.app.Activity#setRequestedOrientation}. See * {@link #shouldIgnoreRequestedOrientation} for more details. * * <p>This treatment is enabled when the following conditions are met: * <ul> * <li>Per-app override is enabled * <li>App has requested orientation more than 2 times within 1-second * timer and activity is not letterboxed for fixed orientation * </ul> */ @VisibleForTesting boolean shouldIgnoreOrientationRequestLoop() { if (!mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled) { return false; return false; } } final long currTimeMs = System.currentTimeMillis(); if (currTimeMs - mTimeMsLastSetOrientationRequest < SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS) { mSetOrientationRequestCounter += 1; } else { // Resets app setOrientationRequest counter if timed out mSetOrientationRequestCounter = 0; } // Update time last called mTimeMsLastSetOrientationRequest = currTimeMs; return mSetOrientationRequestCounter >= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP && !mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio(); } @VisibleForTesting int getSetOrientationRequestCounter() { return mSetOrientationRequestCounter; } /** /** * Whether sending compat fake focus for split screen resumed activities is enabled. Needed * Whether sending compat fake focus for split screen resumed activities is enabled. Needed * because some game engines wait to get focus before drawing the content of the app which isn't * because some game engines wait to get focus before drawing the content of the app which isn't Loading services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +66 −0 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FOR import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; Loading @@ -46,6 +47,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.LetterboxUiController.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; import static com.android.server.wm.LetterboxUiController.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse; Loading Loading @@ -185,6 +188,69 @@ public class LetterboxUiControllerTest extends WindowTestsBase { assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); } } @Test public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() { doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); // Request 3 times to simulate orientation request loop for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ 0); } } @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() { doReturn(true).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); // Request 3 times to simulate orientation request loop for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ i); } } @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() { doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); // No orientation request loop assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ 0); } @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse() throws InterruptedException { doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); for (int i = MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i > 0; i--) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ 0); Thread.sleep(SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS); } } @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_returnsTrue() { doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ i); } assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ true, /* expectedCount */ MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP); } private void assertShouldIgnoreOrientationRequestLoop(boolean shouldIgnore, int expectedCount) { if (shouldIgnore) { assertTrue(mController.shouldIgnoreOrientationRequestLoop()); } else { assertFalse(mController.shouldIgnoreOrientationRequestLoop()); } assertEquals(expectedCount, mController.getSetOrientationRequestCounter()); } @Test @Test @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH}) @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH}) public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() { public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() { Loading Loading
core/java/android/content/pm/ActivityInfo.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -1086,6 +1086,20 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION = public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION = 254631730L; // buganizer id 254631730L; // buganizer id /** * This change id enables compat policy that ignores app requested orientation in * response to an app calling {@link android.app.Activity#setRequestedOrientation} more * than twice in one second if an activity is not letterboxed for fixed orientation. * See com.android.server.wm.LetterboxUiController#shouldIgnoreRequestedOrientation * for details. * @hide */ @ChangeId @Overridable @Disabled public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = 273509367L; // buganizer id /** /** * This change id forces the packages it is applied to never have Display API sandboxing * This change id forces the packages it is applied to never have Display API sandboxing * applied for a letterbox or SCM activity. The Display APIs will continue to provide * applied for a letterbox or SCM activity. The Display APIs will continue to provide Loading
services/core/java/com/android/server/wm/LetterboxUiController.java +84 −14 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FOR import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; Loading Loading @@ -130,6 +131,14 @@ final class LetterboxUiController { private static final float UNDEFINED_ASPECT_RATIO = 0f; private static final float UNDEFINED_ASPECT_RATIO = 0f; // Minimum value of mSetOrientationRequestCounter before qualifying as orientation request loop @VisibleForTesting static final int MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP = 2; // Used to determine reset of mSetOrientationRequestCounter if next app requested // orientation is after timeout value @VisibleForTesting static final int SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS = 1000; private final Point mTmpPoint = new Point(); private final Point mTmpPoint = new Point(); private final LetterboxConfiguration mLetterboxConfiguration; private final LetterboxConfiguration mLetterboxConfiguration; Loading Loading @@ -162,6 +171,8 @@ final class LetterboxUiController { // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION private final boolean mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled; private final boolean mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled; // Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED private final boolean mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled; // Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS // Corresponds to OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS private final boolean mIsOverrideEnableCompatFakeFocusEnabled; private final boolean mIsOverrideEnableCompatFakeFocusEnabled; Loading @@ -186,12 +197,18 @@ final class LetterboxUiController { private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; // Updated when ActivityRecord#setRequestedOrientation is called private long mTimeMsLastSetOrientationRequest = 0; @Configuration.Orientation @Configuration.Orientation private int mInheritedOrientation = ORIENTATION_UNDEFINED; private int mInheritedOrientation = ORIENTATION_UNDEFINED; // The app compat state for the opaque activity if any // The app compat state for the opaque activity if any private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; // Counter for ActivityRecord#setRequestedOrientation private int mSetOrientationRequestCounter = 0; // The CompatDisplayInsets of the opaque activity beneath the translucent one. // The CompatDisplayInsets of the opaque activity beneath the translucent one. private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; Loading Loading @@ -284,6 +301,9 @@ final class LetterboxUiController { mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled = mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION); isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION); mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled = isCompatChangeEnabled( OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); mIsOverrideEnableCompatFakeFocusEnabled = mIsOverrideEnableCompatFakeFocusEnabled = isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS); isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS); Loading Loading @@ -354,20 +374,20 @@ final class LetterboxUiController { * <li>Opt-in component property or per-app override are enabled * <li>Opt-in component property or per-app override are enabled * <li>Activity is relaunched after {@link android.app.Activity#setRequestedOrientation} * <li>Activity is relaunched after {@link android.app.Activity#setRequestedOrientation} * call from an app or camera compat force rotation treatment is active for the activity. * call from an app or camera compat force rotation treatment is active for the activity. * <li>Orientation request loop detected and is not letterboxed for fixed orientation * </ul> * </ul> */ */ boolean shouldIgnoreRequestedOrientation(@ScreenOrientation int requestedOrientation) { boolean shouldIgnoreRequestedOrientation(@ScreenOrientation int requestedOrientation) { if (!shouldEnableWithOverrideAndProperty( if (shouldEnableWithOverrideAndProperty( /* gatingCondition */ mLetterboxConfiguration /* gatingCondition */ mLetterboxConfiguration ::isPolicyForIgnoringRequestedOrientationEnabled, ::isPolicyForIgnoringRequestedOrientationEnabled, mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled, mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled, mBooleanPropertyIgnoreRequestedOrientation)) { mBooleanPropertyIgnoreRequestedOrientation)) { return false; } if (mIsRelauchingAfterRequestedOrientationChanged) { if (mIsRelauchingAfterRequestedOrientationChanged) { Slog.w(TAG, "Ignoring orientation update to " Slog.w(TAG, "Ignoring orientation update to " + screenOrientationToString(requestedOrientation) + screenOrientationToString(requestedOrientation) + " due to relaunching after setRequestedOrientation for " + mActivityRecord); + " due to relaunching after setRequestedOrientation for " + mActivityRecord); return true; return true; } } DisplayContent displayContent = mActivityRecord.mDisplayContent; DisplayContent displayContent = mActivityRecord.mDisplayContent; Loading @@ -382,9 +402,59 @@ final class LetterboxUiController { + " due to camera compat treatment for " + mActivityRecord); + " due to camera compat treatment for " + mActivityRecord); return true; return true; } } } if (shouldIgnoreOrientationRequestLoop()) { Slog.w(TAG, "Ignoring orientation update to " + screenOrientationToString(requestedOrientation) + " as orientation request loop was detected for " + mActivityRecord); return true; } return false; } /** * Whether an app is calling {@link android.app.Activity#setRequestedOrientation} * in a loop and orientation request should be ignored. * * <p>This should only be called once in response to * {@link android.app.Activity#setRequestedOrientation}. See * {@link #shouldIgnoreRequestedOrientation} for more details. * * <p>This treatment is enabled when the following conditions are met: * <ul> * <li>Per-app override is enabled * <li>App has requested orientation more than 2 times within 1-second * timer and activity is not letterboxed for fixed orientation * </ul> */ @VisibleForTesting boolean shouldIgnoreOrientationRequestLoop() { if (!mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled) { return false; return false; } } final long currTimeMs = System.currentTimeMillis(); if (currTimeMs - mTimeMsLastSetOrientationRequest < SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS) { mSetOrientationRequestCounter += 1; } else { // Resets app setOrientationRequest counter if timed out mSetOrientationRequestCounter = 0; } // Update time last called mTimeMsLastSetOrientationRequest = currTimeMs; return mSetOrientationRequestCounter >= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP && !mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio(); } @VisibleForTesting int getSetOrientationRequestCounter() { return mSetOrientationRequestCounter; } /** /** * Whether sending compat fake focus for split screen resumed activities is enabled. Needed * Whether sending compat fake focus for split screen resumed activities is enabled. Needed * because some game engines wait to get focus before drawing the content of the app which isn't * because some game engines wait to get focus before drawing the content of the app which isn't Loading
services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +66 −0 Original line number Original line Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FOR import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; Loading @@ -46,6 +47,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.LetterboxUiController.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; import static com.android.server.wm.LetterboxUiController.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse; Loading Loading @@ -185,6 +188,69 @@ public class LetterboxUiControllerTest extends WindowTestsBase { assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); } } @Test public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() { doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); // Request 3 times to simulate orientation request loop for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ 0); } } @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() { doReturn(true).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); // Request 3 times to simulate orientation request loop for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ i); } } @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() { doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); // No orientation request loop assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ 0); } @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse() throws InterruptedException { doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); for (int i = MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i > 0; i--) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ 0); Thread.sleep(SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS); } } @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) public void testShouldIgnoreOrientationRequestLoop_returnsTrue() { doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio(); for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false, /* expectedCount */ i); } assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ true, /* expectedCount */ MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP); } private void assertShouldIgnoreOrientationRequestLoop(boolean shouldIgnore, int expectedCount) { if (shouldIgnore) { assertTrue(mController.shouldIgnoreOrientationRequestLoop()); } else { assertFalse(mController.shouldIgnoreOrientationRequestLoop()); } assertEquals(expectedCount, mController.getSetOrientationRequestCounter()); } @Test @Test @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH}) @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH}) public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() { public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() { Loading