Merge "Per-app compat treatment for setRequestedOrientation loops." into tm-qpr-dev am: 300cd48c7a
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20838889
Change-Id: I3b81dbcedda594171769125275bdd1a55ff41313
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 06399b9..9e5e8de 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1022,6 +1022,19 @@
public @interface SizeChangesSupportMode {}
/**
+ * This change id enables compat policy that ignores app requested orientation in
+ * response to an app calling {@link android.app.Activity#setRequestedOrientation}. See
+ * com.android.server.wm.LetterboxUiController#shouldIgnoreRequestedOrientation for
+ * details.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION =
+ 254631730L; // 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/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 2f77901..ed9cb00 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -814,6 +814,45 @@
}
/**
+ * Activity level {@link android.content.pm.PackageManager.Property PackageManager
+ * .Property} for an app to inform the system that the activity can be opted-in or opted-out
+ * from the compatibility treatment that avoids {@link
+ * android.app.Activity#setRequestedOrientation} loops. The loop can be trigerred by
+ * ignoreRequestedOrientation display setting enabled on the device or by the landscape natural
+ * orientation of the device.
+ *
+ * <p>The treatment is disabled by default but device manufacturers can enable the treatment
+ * using their discretion to improve display compatibility.
+ *
+ * <p>With this property set to {@code true}, the system could ignore {@link
+ * android.app.Activity#setRequestedOrientation} call from an app if one of the following
+ * conditions are true:
+ * <ul>
+ * <li>Activity is relaunching due to the previous {@link
+ * android.app.Activity#setRequestedOrientation} call.
+ * <li>Camera compatibility force rotation treatment is active for the package.
+ * </ul>
+ *
+ * <p>Setting this property to {@code false} informs the system that the activity must be
+ * opted-out from the compatibility treatment even if the device manufacturer has opted the app
+ * into the treatment.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * <activity>
+ * <property
+ * android:name="android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION"
+ * android:value="true|false"/>
+ * </activity>
+ * </pre>
+ *
+ * @hide
+ */
+ // TODO(b/263984287): Make this public API.
+ String PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION =
+ "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION";
+
+ /**
* @hide
*/
public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4215774..483e908 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5275,6 +5275,11 @@
If given value is outside of this range, the option 0 (top) is assummed. -->
<integer name="config_letterboxDefaultPositionForTabletopModeReachability">0</integer>
+ <!-- Whether should ignore app requested orientation in response to an app
+ calling Activity#setRequestedOrientation. See
+ LetterboxUiController#shouldIgnoreRequestedOrientation for details. -->
+ <bool name="config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled">false</bool>
+
<!-- Whether displaying letterbox education is enabled for letterboxed fullscreen apps. -->
<bool name="config_letterboxIsEducationEnabled">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c57ef10..c24b629 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4481,6 +4481,7 @@
<java-symbol type="integer" name="config_letterboxDefaultPositionForVerticalReachability" />
<java-symbol type="integer" name="config_letterboxDefaultPositionForBookModeReachability" />
<java-symbol type="integer" name="config_letterboxDefaultPositionForTabletopModeReachability" />
+ <java-symbol type="bool" name="config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled" />
<java-symbol type="bool" name="config_letterboxIsEducationEnabled" />
<java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" />
<java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" />
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c712167..9215cab 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1456,8 +1456,7 @@
updatePictureInPictureMode(null, false);
} else {
mLastReportedMultiWindowMode = inMultiWindowMode;
- ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
- false /* ignoreVisibility */);
+ ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS);
}
}
}
@@ -3988,6 +3987,7 @@
}
void finishRelaunching() {
+ mLetterboxUiController.setRelauchingAfterRequestedOrientationChanged(false);
mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this);
if (mPendingRelaunchCount > 0) {
@@ -7745,13 +7745,17 @@
}
void setRequestedOrientation(int requestedOrientation) {
+ if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
+ return;
+ }
setOrientation(requestedOrientation, this);
// Push the new configuration to the requested app in case where it's not pushed, e.g. when
// the request is handled at task level with letterbox.
if (!getMergedOverrideConfiguration().equals(
mLastReportedConfiguration.getMergedConfiguration())) {
- ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */,
+ false /* ignoreVisibility */, true /* isRequestedOrientationChanged */);
}
mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
@@ -9052,7 +9056,13 @@
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
return ensureActivityConfiguration(globalChanges, preserveWindow,
- false /* ignoreVisibility */);
+ false /* ignoreVisibility */, false /* isRequestedOrientationChanged */);
+ }
+
+ boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
+ boolean ignoreVisibility) {
+ return ensureActivityConfiguration(globalChanges, preserveWindow, ignoreVisibility,
+ false /* isRequestedOrientationChanged */);
}
/**
@@ -9066,11 +9076,13 @@
* (stopped state). This is useful for the case where we know the
* activity will be visible soon and we want to ensure its configuration
* before we make it visible.
+ * @param isRequestedOrientationChanged whether this is triggered in response to an app calling
+ * {@link android.app.Activity#setRequestedOrientation}.
* @return False if the activity was relaunched and true if it wasn't relaunched because we
* can't or the app handles the specific configuration that is changing.
*/
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
- boolean ignoreVisibility) {
+ boolean ignoreVisibility, boolean isRequestedOrientationChanged) {
final Task rootTask = getRootTask();
if (rootTask.mConfigWillChange) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
@@ -9194,6 +9206,9 @@
} else {
mRelaunchReason = RELAUNCH_REASON_NONE;
}
+ if (isRequestedOrientationChanged) {
+ mLetterboxUiController.setRelauchingAfterRequestedOrientationChanged(true);
+ }
if (mState == PAUSING) {
// A little annoying: we are waiting for this activity to finish pausing. Let's not
// do anything now, but just flag that it needs to be restarted when done pausing.
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 7266d21..ba0413d 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -287,7 +287,7 @@
* <li>The activity has fixed orientation but not "locked" or "nosensor" one.
* </ul>
*/
- private boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity) {
+ boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity) {
return activity != null && !activity.inMultiWindowMode()
&& activity.getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED
// "locked" and "nosensor" values are often used by camera apps that can't
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 9b84233..6427326 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -222,6 +222,11 @@
// See RefreshCallbackItem for context.
private boolean mIsCameraCompatRefreshCycleThroughStopEnabled = true;
+ // Whether should ignore app requested orientation in response to an app
+ // calling Activity#setRequestedOrientation. See
+ // LetterboxUiController#shouldIgnoreRequestedOrientation for details.
+ private final boolean mIsPolicyForIgnoringRequestedOrientationEnabled;
+
LetterboxConfiguration(Context systemUiContext) {
this(systemUiContext, new LetterboxConfigurationPersister(systemUiContext,
() -> readLetterboxHorizontalReachabilityPositionFromConfig(systemUiContext,
@@ -274,10 +279,13 @@
R.bool.config_letterboxIsEnabledForTranslucentActivities);
mIsCameraCompatTreatmentEnabled = mContext.getResources().getBoolean(
R.bool.config_isWindowManagerCameraCompatTreatmentEnabled);
+ mIsCompatFakeFocusEnabled = mContext.getResources().getBoolean(
+ R.bool.config_isCompatFakeFocusEnabled);
+ mIsPolicyForIgnoringRequestedOrientationEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled);
+
mLetterboxConfigurationPersister = letterboxConfigurationPersister;
mLetterboxConfigurationPersister.start();
- mIsCompatFakeFocusEnabled = mContext.getResources()
- .getBoolean(R.bool.config_isCompatFakeFocusEnabled);
}
/**
@@ -1034,6 +1042,15 @@
mIsCompatFakeFocusEnabled = enabled;
}
+ /**
+ * Whether should ignore app requested orientation in response to an app calling
+ * {@link android.app.Activity#setRequestedOrientation}. See {@link
+ * LetterboxUiController#shouldIgnoreRequestedOrientation} for details.
+ */
+ boolean isPolicyForIgnoringRequestedOrientationEnabled() {
+ return mIsPolicyForIgnoringRequestedOrientationEnabled;
+ }
+
/** Whether camera compatibility treatment is enabled. */
boolean isCameraCompatTreatmentEnabled(boolean checkDeviceConfig) {
return mIsCameraCompatTreatmentEnabled
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index fd7e082..0c8a645 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -17,10 +17,13 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.screenOrientationToString;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
@@ -55,6 +58,8 @@
import android.annotation.Nullable;
import android.app.ActivityManager.TaskDescription;
+import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
@@ -74,6 +79,7 @@
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import java.io.PrintWriter;
+import java.util.function.BooleanSupplier;
/** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
// TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
@@ -131,12 +137,37 @@
// DisplayRotationCompatPolicy.
private boolean mIsRefreshAfterRotationRequested;
+ @Nullable
+ private final Boolean mBooleanPropertyIgnoreRequestedOrientation;
+
+ private boolean mIsRelauchingAfterRequestedOrientationChanged;
+
LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
mLetterboxConfiguration = wmService.mLetterboxConfiguration;
// Given activityRecord may not be fully constructed since LetterboxUiController
// is created in its constructor. It shouldn't be used in this constructor but it's safe
// to use it after since controller is only used in ActivityRecord.
mActivityRecord = activityRecord;
+
+ PackageManager packageManager = wmService.mContext.getPackageManager();
+ mBooleanPropertyIgnoreRequestedOrientation =
+ readComponentProperty(packageManager, mActivityRecord.packageName,
+ mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled,
+ PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+ }
+
+ @Nullable
+ private static Boolean readComponentProperty(PackageManager packageManager, String packageName,
+ BooleanSupplier gatingCondition, String propertyName) {
+ if (!gatingCondition.getAsBoolean()) {
+ return null;
+ }
+ try {
+ return packageManager.getProperty(propertyName, packageName).getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ // No such property name.
+ }
+ return null;
}
/** Cleans up {@link Letterbox} if it exists.*/
@@ -154,6 +185,72 @@
}
/**
+ * Whether should ignore app requested orientation in response to an app
+ * calling {@link android.app.Activity#setRequestedOrientation}.
+ *
+ * <p>This is needed to avoid getting into {@link android.app.Activity#setRequestedOrientation}
+ * loop when {@link DisplayContent#getIgnoreOrientationRequest} is enabled or device has
+ * landscape natural orientation which app developers don't expect. For example, the loop can
+ * look like this:
+ * <ol>
+ * <li>App sets default orientation to "unspecified" at runtime
+ * <li>App requests to "portrait" after checking some condition (e.g. display rotation).
+ * <li>(2) leads to fullscreen -> letterboxed bounds change and activity relaunch because
+ * app can't handle the corresponding config changes.
+ * <li>Loop goes back to (1)
+ * </ol>
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Flag gating the treatment is enabled
+ * <li>Opt-out component property isn't enabled
+ * <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.
+ * </ul>
+ */
+ boolean shouldIgnoreRequestedOrientation(@ScreenOrientation int requestedOrientation) {
+ if (!mLetterboxConfiguration.isPolicyForIgnoringRequestedOrientationEnabled()) {
+ return false;
+ }
+ if (Boolean.FALSE.equals(mBooleanPropertyIgnoreRequestedOrientation)) {
+ return false;
+ }
+ if (!Boolean.TRUE.equals(mBooleanPropertyIgnoreRequestedOrientation)
+ && !mActivityRecord.info.isChangeEnabled(
+ OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION)) {
+ 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;
+ }
+ return false;
+ }
+
+ /**
+ * Sets whether an activity is relaunching after the app has called {@link
+ * android.app.Activity#setRequestedOrientation}.
+ */
+ void setRelauchingAfterRequestedOrientationChanged(boolean isRelaunching) {
+ mIsRelauchingAfterRequestedOrientationChanged = isRelaunching;
+ }
+
+ /**
* Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}
* following the camera compat force rotation in {@link DisplayRotationCompatPolicy}.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
new file mode 100644
index 0000000..6d778afe
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+
+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.spyOn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+ /**
+ * Test class for {@link LetterboxUiControllerTest}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:LetterboxUiControllerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class LetterboxUiControllerTest extends WindowTestsBase {
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ private ActivityRecord mActivity;
+ private DisplayContent mDisplayContent;
+ private LetterboxUiController mController;
+ private LetterboxConfiguration mLetterboxConfiguration;
+
+ @Before
+ public void setUp() throws Exception {
+ mActivity = setUpActivityWithComponent();
+
+ mLetterboxConfiguration = mWm.mLetterboxConfiguration;
+ spyOn(mLetterboxConfiguration);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_activityRelaunching_returnsTrue() {
+ prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
+
+ assertTrue(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() {
+ doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean());
+
+ // Recreate DisplayContent with DisplayRotationCompatPolicy
+ mActivity = setUpActivityWithComponent();
+ mController = new LetterboxUiController(mWm, mActivity);
+ prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
+ mController.setRelauchingAfterRequestedOrientationChanged(false);
+
+ spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
+ doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
+ .isTreatmentEnabledForActivity(eq(mActivity));
+
+ assertTrue(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
+ }
+
+ @Test
+ public void testShouldIgnoreRequestedOrientation_overrideDisabled_returnsFalse() {
+ prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
+
+ assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
+ }
+
+ @Test
+ public void testShouldIgnoreRequestedOrientation_propertyIsTrue_returnsTrue()
+ throws Exception {
+ doReturn(true).when(mLetterboxConfiguration)
+ .isPolicyForIgnoringRequestedOrientationEnabled();
+ mockThatProperty(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION, /* value */ true);
+ mController = new LetterboxUiController(mWm, mActivity);
+ prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
+
+ assertTrue(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_propertyIsFalseAndOverride_returnsFalse()
+ throws Exception {
+ doReturn(true).when(mLetterboxConfiguration)
+ .isPolicyForIgnoringRequestedOrientationEnabled();
+ mockThatProperty(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION, /* value */ false);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+ prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
+
+ assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() {
+ prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
+ doReturn(false).when(mLetterboxConfiguration)
+ .isPolicyForIgnoringRequestedOrientationEnabled();
+
+ assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
+ }
+
+ private void mockThatProperty(String propertyName, boolean value) throws Exception {
+ Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
+ /* className */ "");
+ PackageManager pm = mWm.mContext.getPackageManager();
+ spyOn(pm);
+ doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
+ }
+
+ private void prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch() {
+ doReturn(true).when(mLetterboxConfiguration)
+ .isPolicyForIgnoringRequestedOrientationEnabled();
+ mController.setRelauchingAfterRequestedOrientationChanged(true);
+ }
+
+ private ActivityRecord setUpActivityWithComponent() {
+ mDisplayContent = new TestDisplayContent
+ .Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
+ Task task = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setOnTop(true)
+ .setTask(task)
+ // Set the component to be that of the test class in order to enable compat changes
+ .setComponent(ComponentName.createRelative(mContext,
+ com.android.server.wm.LetterboxUiControllerTest.class.getName()))
+ .build();
+ return activity;
+ }
+}