summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Graciela Wissen Putri <gracielawputri@google.com> 2023-03-07 17:26:06 +0000
committer Graciela Putri <gracielawputri@google.com> 2023-03-23 16:53:06 +0000
commit31b731c2de27b5ff2a9240c361a3e48114d5f59b (patch)
tree7dd071742a964483c90d05d0edd556e4623e2739
parent106aa99da2f1122c54f042a53288a75087c241b2 (diff)
Prevent orientation request loop in override
MUST_SLEEP Ignore any orientation requests if activity is not letterboxed for fixed orientation and aspect ratio and activity already requests orientation change more than twice. Logic is behind per-app OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED. Bug: 273509367 Test: atest WmTests:LetterboxUiControllerTest Change-Id: I72e8c9faf191c0ae819b409a3906da28389e72e6
-rw-r--r--core/java/android/content/pm/ActivityInfo.java14
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java98
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java66
3 files changed, 164 insertions, 14 deletions
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 226278cbe44d..319f1298a87f 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1035,6 +1035,20 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
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
* applied for a letterbox or SCM activity. The Display APIs will continue to provide
* DisplayArea bounds.
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 6bffc4c95e64..4f0f22732136 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -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_ENABLE_REFRESH_VIA_PAUSE;
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_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
@@ -125,6 +126,14 @@ final class LetterboxUiController {
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 LetterboxConfiguration mLetterboxConfiguration;
@@ -163,6 +172,8 @@ final class LetterboxUiController {
// Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION
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
private final boolean mIsOverrideEnableCompatFakeFocusEnabled;
@@ -187,12 +198,18 @@ final class LetterboxUiController {
private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
+ // Updated when ActivityRecord#setRequestedOrientation is called
+ private long mTimeMsLastSetOrientationRequest = 0;
+
@Configuration.Orientation
private int mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED;
// The app compat state for the opaque activity if any
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.
private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
@@ -288,6 +305,9 @@ final class LetterboxUiController {
mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled =
isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+ mIsOverrideEnableCompatIgnoreOrientationRequestWhenLoopDetectedEnabled =
+ isCompatChangeEnabled(
+ OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED);
mIsOverrideEnableCompatFakeFocusEnabled =
isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS);
@@ -354,35 +374,85 @@ final class LetterboxUiController {
* <li>Opt-in component property or per-app override are enabled
* <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.
+ * <li>Orientation request loop detected and is not letterboxed for fixed orientation
* </ul>
*/
boolean shouldIgnoreRequestedOrientation(@ScreenOrientation int requestedOrientation) {
- if (!shouldEnableWithOverrideAndProperty(
+ if (shouldEnableWithOverrideAndProperty(
/* gatingCondition */ mLetterboxConfiguration
::isPolicyForIgnoringRequestedOrientationEnabled,
mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled,
mBooleanPropertyIgnoreRequestedOrientation)) {
- return false;
+ if (mIsRelauchingAfterRequestedOrientationChanged) {
+ Slog.w(TAG, "Ignoring orientation update to "
+ + screenOrientationToString(requestedOrientation)
+ + " due to relaunching after setRequestedOrientation for "
+ + mActivityRecord);
+ return true;
+ }
+ DisplayContent displayContent = mActivityRecord.mDisplayContent;
+ if (displayContent == null) {
+ return false;
+ }
+ if (displayContent.mDisplayRotationCompatPolicy != null
+ && displayContent.mDisplayRotationCompatPolicy
+ .isTreatmentEnabledForActivity(mActivityRecord)) {
+ Slog.w(TAG, "Ignoring orientation update to "
+ + screenOrientationToString(requestedOrientation)
+ + " due to camera compat treatment for " + mActivityRecord);
+ return true;
+ }
}
- if (mIsRelauchingAfterRequestedOrientationChanged) {
+
+ if (shouldIgnoreOrientationRequestLoop()) {
Slog.w(TAG, "Ignoring orientation update to "
+ screenOrientationToString(requestedOrientation)
- + " due to relaunching after setRequestedOrientation for " + mActivityRecord);
+ + " as orientation request loop was detected for "
+ + mActivityRecord);
return true;
}
- DisplayContent displayContent = mActivityRecord.mDisplayContent;
- if (displayContent == null) {
+ 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;
}
- if (displayContent.mDisplayRotationCompatPolicy != null
- && displayContent.mDisplayRotationCompatPolicy
- .isTreatmentEnabledForActivity(mActivityRecord)) {
- Slog.w(TAG, "Ignoring orientation update to "
- + screenOrientationToString(requestedOrientation)
- + " due to camera compat treatment for " + mActivityRecord);
- return true;
+
+ 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;
}
- return false;
+ // Update time last called
+ mTimeMsLastSetOrientationRequest = currTimeMs;
+
+ return mSetOrientationRequestCounter >= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP
+ && !mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio();
+ }
+
+ @VisibleForTesting
+ int getSetOrientationRequestCounter() {
+ return mSetOrientationRequestCounter;
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 693f1ced78dd..23a3d520ea3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -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_ENABLE_REFRESH_VIA_PAUSE;
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_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
@@ -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.mock;
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.assertFalse;
@@ -185,6 +188,69 @@ public class LetterboxUiControllerTest extends WindowTestsBase {
}
@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
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() {
prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();