Merge "Prevent orientation request loop in override" into tm-qpr-dev

This commit is contained in:
Graciela Putri
2023-03-23 17:23:32 +00:00
committed by Android (Google) Code Review
3 changed files with 166 additions and 16 deletions

View File

@@ -1034,6 +1034,20 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION =
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

View File

@@ -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;
@@ -129,6 +130,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;
@@ -167,6 +176,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;
@@ -191,12 +202,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 = 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;
@@ -292,6 +309,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);
@@ -362,37 +382,87 @@ 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);
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);
+ " 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;
}
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
* because some game engines wait to get focus before drawing the content of the app which isn't

View File

@@ -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;
@@ -184,6 +187,69 @@ public class LetterboxUiControllerTest extends WindowTestsBase {
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
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() {