Merge "Prevent orientation request loop in override" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
84903febab
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user