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 =
|
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
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -129,6 +130,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;
|
||||||
@@ -167,6 +176,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;
|
||||||
@@ -191,12 +202,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;
|
||||||
|
|
||||||
@@ -292,6 +309,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);
|
||||||
@@ -362,20 +382,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;
|
||||||
@@ -390,9 +410,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;
|
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
|
* 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
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -184,6 +187,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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user