From 0e3de559ca3d349c8890bc7c20928f288688c1fa Mon Sep 17 00:00:00 2001 From: Massimo Carli Date: Wed, 19 Jun 2024 17:03:47 +0000 Subject: [11/n] Rename AppCompat Capability suffix to Overrides Rename *Capability classes to *Overrides for a better description of theit purpose. Flag: EXEMPT refactor Bug: 345156325 Test: atest WmTests:AppCompatOrientationPolicyTest Test: atest WmTests:AppCompatOrientationOverridesTest Test: atest WmTests:LetterboxUiControllerTest Test: atest WmTests:SizeCompatTests Test: atest WmTests:TransparentPolicyTest Change-Id: I31c7e1c12b756cab6c4095aa9e84e57a638bb9f3 --- .../java/com/android/server/wm/ActivityRecord.java | 6 +- .../com/android/server/wm/AppCompatCapability.java | 416 --------------------- .../com/android/server/wm/AppCompatController.java | 10 +- .../server/wm/AppCompatOrientationCapability.java | 257 ------------- .../server/wm/AppCompatOrientationOverrides.java | 262 +++++++++++++ .../server/wm/AppCompatOrientationPolicy.java | 16 +- .../com/android/server/wm/AppCompatOverrides.java | 416 +++++++++++++++++++++ .../android/server/wm/LetterboxUiController.java | 44 +-- .../wm/AppCompatOrientationCapabilityTest.java | 329 ---------------- .../wm/AppCompatOrientationOverridesTest.java | 329 ++++++++++++++++ 10 files changed, 1045 insertions(+), 1040 deletions(-) delete mode 100644 services/core/java/com/android/server/wm/AppCompatCapability.java delete mode 100644 services/core/java/com/android/server/wm/AppCompatOrientationCapability.java create mode 100644 services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java create mode 100644 services/core/java/com/android/server/wm/AppCompatOverrides.java delete mode 100644 services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationCapabilityTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 21155bb7e29e..d28125c258f0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -8183,8 +8183,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) { - if (mAppCompatController.getAppCompatCapability() - .getAppCompatOrientationCapability() + if (mAppCompatController.getAppCompatOverrides() + .getAppCompatOrientationOverrides() .shouldIgnoreRequestedOrientation(requestedOrientation)) { return; } @@ -10821,7 +10821,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A proto.write(SHOULD_OVERRIDE_MIN_ASPECT_RATIO, mLetterboxUiController.shouldOverrideMinAspectRatio()); proto.write(SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP, - mAppCompatController.getAppCompatCapability().getAppCompatOrientationCapability() + mAppCompatController.getAppCompatOverrides().getAppCompatOrientationOverrides() .shouldIgnoreOrientationRequestLoop()); proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP, mLetterboxUiController.shouldOverrideForceResizeApp()); diff --git a/services/core/java/com/android/server/wm/AppCompatCapability.java b/services/core/java/com/android/server/wm/AppCompatCapability.java deleted file mode 100644 index d4379e486ea1..000000000000 --- a/services/core/java/com/android/server/wm/AppCompatCapability.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2024 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.FORCE_NON_RESIZE_APP; -import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP; -import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER; -import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION; -import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT; -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_MIN_ASPECT_RATIO; -import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA; -import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; -import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION; -import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION; -import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN; -import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION; -import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH; -import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE; -import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; - -import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; - -import android.annotation.NonNull; -import android.content.pm.PackageManager; - -import com.android.server.wm.utils.OptPropFactory; -import com.android.window.flags.Flags; - -import java.util.function.BooleanSupplier; - -/** - * Encapsulate logic related to operations guarded by an app override. - */ -public class AppCompatCapability { - - private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatCapability" : TAG_ATM; - - @NonNull - private final LetterboxConfiguration mLetterboxConfiguration; - - @NonNull - private final ActivityRecord mActivityRecord; - - // Corresponds to OVERRIDE_ANY_ORIENTATION_TO_USER - private final boolean mIsSystemOverrideToFullscreenEnabled; - // Corresponds to OVERRIDE_RESPECT_REQUESTED_ORIENTATION - private final boolean mIsOverrideRespectRequestedOrientationEnabled; - // Corresponds to OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA - private final boolean mIsOverrideOrientationOnlyForCameraEnabled; - - @NonNull - private final OptPropFactory.OptProp mFakeFocusOptProp; - @NonNull - private final OptPropFactory.OptProp mCameraCompatAllowForceRotationOptProp; - @NonNull - private final OptPropFactory.OptProp mCameraCompatAllowRefreshOptProp; - @NonNull - private final OptPropFactory.OptProp mCameraCompatEnableRefreshViaPauseOptProp; - @NonNull - private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp; - @NonNull - private final OptPropFactory.OptProp mAllowDisplayOrientationOverrideOptProp; - @NonNull - private final OptPropFactory.OptProp mAllowMinAspectRatioOverrideOptProp; - @NonNull - private final OptPropFactory.OptProp mAllowForceResizeOverrideOptProp; - @NonNull - private final OptPropFactory.OptProp mAllowUserAspectRatioOverrideOptProp; - @NonNull - private final OptPropFactory.OptProp mAllowUserAspectRatioFullscreenOverrideOptProp; - - private final AppCompatOrientationCapability mAppCompatOrientationCapability; - - AppCompatCapability(@NonNull WindowManagerService wmService, - @NonNull ActivityRecord activityRecord, - @NonNull LetterboxConfiguration letterboxConfiguration) { - mLetterboxConfiguration = letterboxConfiguration; - mActivityRecord = activityRecord; - final PackageManager packageManager = wmService.mContext.getPackageManager(); - - final OptPropFactory optPropBuilder = new OptPropFactory(packageManager, - activityRecord.packageName); - - mAppCompatOrientationCapability = - new AppCompatOrientationCapability(optPropBuilder, mLetterboxConfiguration, - mActivityRecord); - - mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, - mLetterboxConfiguration::isCompatFakeFocusEnabled); - - final BooleanSupplier isCameraCompatTreatmentEnabled = asLazy( - mLetterboxConfiguration::isCameraCompatTreatmentEnabled); - mCameraCompatAllowForceRotationOptProp = optPropBuilder.create( - PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, - isCameraCompatTreatmentEnabled); - mCameraCompatAllowRefreshOptProp = optPropBuilder.create( - PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, - isCameraCompatTreatmentEnabled); - mCameraCompatEnableRefreshViaPauseOptProp = optPropBuilder.create( - PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE, - isCameraCompatTreatmentEnabled); - - mAllowOrientationOverrideOptProp = optPropBuilder.create( - PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE); - - mAllowDisplayOrientationOverrideOptProp = optPropBuilder.create( - PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE, - () -> mActivityRecord.mDisplayContent != null - && mActivityRecord.getTask() != null - && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest() - && !mActivityRecord.getTask().inMultiWindowMode() - && mActivityRecord.mDisplayContent.getNaturalOrientation() - == ORIENTATION_LANDSCAPE - ); - - mAllowMinAspectRatioOverrideOptProp = optPropBuilder.create( - PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE); - mAllowForceResizeOverrideOptProp = optPropBuilder.create( - PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES); - mAllowUserAspectRatioOverrideOptProp = optPropBuilder.create( - PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, - mLetterboxConfiguration::isUserAppAspectRatioSettingsEnabled); - mAllowUserAspectRatioFullscreenOverrideOptProp = optPropBuilder.create( - PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, - mLetterboxConfiguration::isUserAppAspectRatioFullscreenEnabled); - - mIsSystemOverrideToFullscreenEnabled = - isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER); - mIsOverrideRespectRequestedOrientationEnabled = - isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION); - mIsOverrideOrientationOnlyForCameraEnabled = - isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA); - } - - /** - * @return {@code true} if the App Compat Camera Policy is active for the current activity. - */ - boolean isCameraCompatTreatmentActive() { - final DisplayContent displayContent = mActivityRecord.mDisplayContent; - if (displayContent == null) { - return false; - } - return displayContent.mDisplayRotationCompatPolicy != null - && displayContent.mDisplayRotationCompatPolicy - .isTreatmentEnabledForActivity(mActivityRecord); - } - - @NonNull - AppCompatOrientationCapability getAppCompatOrientationCapability() { - return mAppCompatOrientationCapability; - } - - /** - * 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 - * guaranteed by default in multi-window modes. - * - *

This treatment is enabled when the following conditions are met: - *

- */ - boolean shouldSendFakeFocus() { - return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty( - isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS)); - } - - /** - * Whether activity is eligible for camera compat force rotation treatment. See {@link - * DisplayRotationCompatPolicy} for context. - * - *

This treatment is enabled when the following conditions are met: - *

- */ - boolean shouldForceRotateForCameraCompat() { - return mCameraCompatAllowForceRotationOptProp.shouldEnableWithOptOutOverrideAndProperty( - isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION)); - } - - /** - * Whether activity is eligible for activity "refresh" after camera compat force rotation - * treatment. See {@link DisplayRotationCompatPolicy} for context. - * - *

This treatment is enabled when the following conditions are met: - *

- */ - boolean shouldRefreshActivityForCameraCompat() { - return mCameraCompatAllowRefreshOptProp.shouldEnableWithOptOutOverrideAndProperty( - isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH)); - } - - /** - * Whether activity should be "refreshed" after the camera compat force rotation treatment - * using the "resumed -> paused -> resumed" cycle rather than the "resumed -> ... -> stopped - * -> ... -> resumed" cycle. See {@link DisplayRotationCompatPolicy} for context. - * - *

This treatment is enabled when the following conditions are met: - *

- */ - boolean shouldRefreshActivityViaPauseForCameraCompat() { - return mCameraCompatEnableRefreshViaPauseOptProp.shouldEnableWithOverrideAndProperty( - isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE)); - } - - boolean isSystemOverrideToFullscreenEnabled(int userAspectRatio) { - return mIsSystemOverrideToFullscreenEnabled - && !mAllowOrientationOverrideOptProp.isFalse() - && (userAspectRatio == USER_MIN_ASPECT_RATIO_UNSET - || userAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN); - } - - boolean isAllowOrientationOverrideOptOut() { - return mAllowOrientationOverrideOptProp.isFalse(); - } - - /** - * Whether we should apply the min aspect ratio per-app override. When this override is applied - * the min aspect ratio given in the app's manifest will be overridden to the largest enabled - * aspect ratio treatment unless the app's manifest value is higher. The treatment will also - * apply if no value is provided in the manifest. - * - *

This method returns {@code true} when the following conditions are met: - *

- */ - boolean shouldOverrideMinAspectRatio() { - return mAllowMinAspectRatioOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty( - isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO)); - } - - boolean isOverrideRespectRequestedOrientationEnabled() { - return mIsOverrideRespectRequestedOrientationEnabled; - } - - boolean isOverrideOrientationOnlyForCameraEnabled() { - return mIsOverrideOrientationOnlyForCameraEnabled; - } - - /** - * Whether activity is eligible for camera compatibility free-form treatment. - * - *

The treatment is applied to a fixed-orientation camera activity in free-form windowing - * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and - * provides changes to the camera and display orientation signals to match those expected on a - * portrait device in that orientation (for example, on a standard phone). - * - *

The treatment is enabled when the following conditions are met: - *

- */ - boolean shouldApplyFreeformTreatmentForCameraCompat() { - return Flags.cameraCompatForFreeform() && !isCompatChangeEnabled( - OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT); - } - - - /** - * Whether we should apply the min aspect ratio per-app override only when an app is connected - * to the camera. - * When this override is applied the min aspect ratio given in the app's manifest will be - * overridden to the largest enabled aspect ratio treatment unless the app's manifest value - * is higher. The treatment will also apply if no value is provided in the manifest. - * - *

This method returns {@code true} when the following conditions are met: - *

- */ - boolean shouldOverrideMinAspectRatioForCamera() { - return mActivityRecord.isCameraActive() - && mAllowMinAspectRatioOverrideOptProp - .shouldEnableWithOptInOverrideAndOptOutProperty( - isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA)); - } - - /** - * Whether should fix display orientation to landscape natural orientation when a task is - * fullscreen and the display is ignoring orientation requests. - * - *

This treatment is enabled when the following conditions are met: - *

- */ - boolean shouldUseDisplayLandscapeNaturalOrientation() { - return mAllowDisplayOrientationOverrideOptProp - .shouldEnableWithOptInOverrideAndOptOutProperty( - isCompatChangeEnabled(OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION)); - } - - /** - * Whether we should apply the force resize per-app override. When this override is applied it - * forces the packages it is applied to to be resizable. It won't change whether the app can be - * put into multi-windowing mode, but allow the app to resize without going into size-compat - * mode when the window container resizes, such as display size change or screen rotation. - * - *

This method returns {@code true} when the following conditions are met: - *

- */ - boolean shouldOverrideForceResizeApp() { - return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty( - isCompatChangeEnabled(FORCE_RESIZE_APP)); - } - - /** - * Whether we should enable users to resize the current app. - */ - boolean shouldEnableUserAspectRatioSettings() { - // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has - // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null, - // the current app doesn't opt-out so the first part of the predicate is true. - return !mAllowUserAspectRatioOverrideOptProp.isFalse() - && mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled() - && mActivityRecord.mDisplayContent != null - && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest(); - } - - boolean isUserFullscreenOverrideEnabled() { - if (mAllowUserAspectRatioOverrideOptProp.isFalse() - || mAllowUserAspectRatioFullscreenOverrideOptProp.isFalse() - || !mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()) { - return false; - } - return true; - } - - /** - * Whether we should apply the force non resize per-app override. When this override is applied - * it forces the packages it is applied to to be non-resizable. - * - *

This method returns {@code true} when the following conditions are met: - *

- */ - boolean shouldOverrideForceNonResizeApp() { - return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty( - isCompatChangeEnabled(FORCE_NON_RESIZE_APP)); - } - - private boolean isCompatChangeEnabled(long overrideChangeId) { - return mActivityRecord.info.isChangeEnabled(overrideChangeId); - } - - @NonNull - static BooleanSupplier asLazy(@NonNull BooleanSupplier supplier) { - return new BooleanSupplier() { - private boolean mRead; - private boolean mValue; - - @Override - public boolean getAsBoolean() { - if (!mRead) { - mRead = true; - mValue = supplier.getAsBoolean(); - } - return mValue; - } - }; - } -} diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java index 3ff0fcefd747..4b0d739af64a 100644 --- a/services/core/java/com/android/server/wm/AppCompatController.java +++ b/services/core/java/com/android/server/wm/AppCompatController.java @@ -27,18 +27,18 @@ class AppCompatController { @NonNull private final AppCompatOrientationPolicy mOrientationPolicy; @NonNull - private final AppCompatCapability mAppCompatCapability; + private final AppCompatOverrides mAppCompatOverrides; AppCompatController(@NonNull WindowManagerService wmService, @NonNull ActivityRecord activityRecord) { mTransparentPolicy = new TransparentPolicy(activityRecord, wmService.mLetterboxConfiguration); - mAppCompatCapability = new AppCompatCapability(wmService, activityRecord, + mAppCompatOverrides = new AppCompatOverrides(wmService, activityRecord, wmService.mLetterboxConfiguration); // TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with aspectRatio. final LetterboxUiController tmpController = activityRecord.mLetterboxUiController; mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, - mAppCompatCapability, tmpController::shouldApplyUserFullscreenOverride, + mAppCompatOverrides, tmpController::shouldApplyUserFullscreenOverride, tmpController::shouldApplyUserMinAspectRatioOverride, tmpController::isSystemOverrideToFullscreenEnabled); } @@ -54,7 +54,7 @@ class AppCompatController { } @NonNull - AppCompatCapability getAppCompatCapability() { - return mAppCompatCapability; + AppCompatOverrides getAppCompatOverrides() { + return mAppCompatOverrides; } } diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationCapability.java b/services/core/java/com/android/server/wm/AppCompatOrientationCapability.java deleted file mode 100644 index 10f3e833f78a..000000000000 --- a/services/core/java/com/android/server/wm/AppCompatOrientationCapability.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2024 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_ANY_ORIENTATION; -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_UNDEFINED_ORIENTATION_TO_NOSENSOR; -import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT; -import static android.content.pm.ActivityInfo.screenOrientationToString; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; -import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION; - -import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.AppCompatCapability.asLazy; - -import android.annotation.NonNull; -import android.content.pm.ActivityInfo; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.wm.utils.OptPropFactory; - -import java.util.function.BooleanSupplier; -import java.util.function.LongSupplier; - -class AppCompatOrientationCapability { - - private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatCapability" : TAG_ATM; - - @NonNull - private final ActivityRecord mActivityRecord; - - @NonNull - private final OptPropFactory.OptProp mIgnoreRequestedOrientationOptProp; - @NonNull - private final OptPropFactory.OptProp mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp; - - @NonNull - final OrientationCapabilityState mOrientationCapabilityState; - - AppCompatOrientationCapability(@NonNull OptPropFactory optPropBuilder, - @NonNull LetterboxConfiguration letterboxConfiguration, - @NonNull ActivityRecord activityRecord) { - mActivityRecord = activityRecord; - mOrientationCapabilityState = new OrientationCapabilityState(mActivityRecord, - System::currentTimeMillis); - final BooleanSupplier isPolicyForIgnoringRequestedOrientationEnabled = asLazy( - letterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled); - mIgnoreRequestedOrientationOptProp = optPropBuilder.create( - PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION, - isPolicyForIgnoringRequestedOrientationEnabled); - mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp = optPropBuilder.create( - PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED, - isPolicyForIgnoringRequestedOrientationEnabled); - } - - /** - * Whether should ignore app requested orientation in response to an app - * calling {@link android.app.Activity#setRequestedOrientation}. - * - *

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: - *

    - *
  1. App sets default orientation to "unspecified" at runtime - *
  2. App requests to "portrait" after checking some condition (e.g. display rotation). - *
  3. (2) leads to fullscreen -> letterboxed bounds change and activity relaunch because - * app can't handle the corresponding config changes. - *
  4. Loop goes back to (1) - *
- * - *

This treatment is enabled when the following conditions are met: - *

- */ - boolean shouldIgnoreRequestedOrientation( - @ActivityInfo.ScreenOrientation int requestedOrientation) { - if (mIgnoreRequestedOrientationOptProp.shouldEnableWithOverrideAndProperty( - isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION))) { - if (mOrientationCapabilityState.mIsRelaunchingAfterRequestedOrientationChanged) { - Slog.w(TAG, "Ignoring orientation update to " - + screenOrientationToString(requestedOrientation) - + " due to relaunching after setRequestedOrientation for " - + mActivityRecord); - return true; - } - if (isCameraCompatTreatmentActive()) { - Slog.w(TAG, "Ignoring orientation update to " - + screenOrientationToString(requestedOrientation) - + " due to camera compat treatment for " + mActivityRecord); - 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; - } - - /** - * Whether an app is calling {@link android.app.Activity#setRequestedOrientation} - * in a loop and orientation request should be ignored. - * - *

This should only be called once in response to - * {@link android.app.Activity#setRequestedOrientation}. See - * {@link #shouldIgnoreRequestedOrientation} for more details. - * - *

This treatment is enabled when the following conditions are met: - *

- */ - boolean shouldIgnoreOrientationRequestLoop() { - final boolean loopDetectionEnabled = isCompatChangeEnabled( - OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); - if (!mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp - .shouldEnableWithOptInOverrideAndOptOutProperty(loopDetectionEnabled)) { - return false; - } - mOrientationCapabilityState.updateOrientationRequestLoopState(); - - return mOrientationCapabilityState.shouldIgnoreRequestInLoop() - && !mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio(); - } - - /** - * Sets whether an activity is relaunching after the app has called {@link - * android.app.Activity#setRequestedOrientation}. - */ - void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) { - mOrientationCapabilityState - .mIsRelaunchingAfterRequestedOrientationChanged = isRelaunching; - } - - boolean getIsRelaunchingAfterRequestedOrientationChanged() { - return mOrientationCapabilityState.mIsRelaunchingAfterRequestedOrientationChanged; - } - - @VisibleForTesting - int getSetOrientationRequestCounter() { - return mOrientationCapabilityState.mSetOrientationRequestCounter; - } - - private boolean isCompatChangeEnabled(long overrideChangeId) { - return mActivityRecord.info.isChangeEnabled(overrideChangeId); - } - - /** - * @return {@code true} if the App Compat Camera Policy is active for the current activity. - */ - // TODO(b/346253439): Remove after defining dependency with Camera capabilities. - private boolean isCameraCompatTreatmentActive() { - DisplayContent displayContent = mActivityRecord.mDisplayContent; - if (displayContent == null) { - return false; - } - return displayContent.mDisplayRotationCompatPolicy != null - && displayContent.mDisplayRotationCompatPolicy - .isTreatmentEnabledForActivity(mActivityRecord); - } - - static class OrientationCapabilityState { - // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR - final boolean mIsOverrideToNosensorOrientationEnabled; - // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT - final boolean mIsOverrideToPortraitOrientationEnabled; - // Corresponds to OVERRIDE_ANY_ORIENTATION - final boolean mIsOverrideAnyOrientationEnabled; - // Corresponds to OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE - final boolean mIsOverrideToReverseLandscapeOrientationEnabled; - - private boolean mIsRelaunchingAfterRequestedOrientationChanged; - - // 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; - // Minimum value of mSetOrientationRequestCounter before qualifying as orientation request - // loop - @VisibleForTesting - static final int MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP = 2; - // Updated when ActivityRecord#setRequestedOrientation is called - private long mTimeMsLastSetOrientationRequest = 0; - // Counter for ActivityRecord#setRequestedOrientation - private int mSetOrientationRequestCounter = 0; - @VisibleForTesting - LongSupplier mCurrentTimeMillisSupplier; - - OrientationCapabilityState(@NonNull ActivityRecord activityRecord, - @NonNull LongSupplier currentTimeMillisSupplier) { - mCurrentTimeMillisSupplier = currentTimeMillisSupplier; - mIsOverrideToNosensorOrientationEnabled = - activityRecord.info.isChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR); - mIsOverrideToPortraitOrientationEnabled = - activityRecord.info.isChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT); - mIsOverrideAnyOrientationEnabled = - activityRecord.info.isChangeEnabled(OVERRIDE_ANY_ORIENTATION); - mIsOverrideToReverseLandscapeOrientationEnabled = activityRecord.info - .isChangeEnabled(OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE); - } - - /** - * @return {@code true} if we should start ignoring orientation in a orientation request - * loop. - */ - boolean shouldIgnoreRequestInLoop() { - return mSetOrientationRequestCounter >= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; - } - - /** - * Updates the orientation request counter using a specific timeout. - */ - void updateOrientationRequestLoopState() { - final long currTimeMs = mCurrentTimeMillisSupplier.getAsLong(); - final long elapsedTime = currTimeMs - mTimeMsLastSetOrientationRequest; - if (elapsedTime < SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS) { - mSetOrientationRequestCounter++; - } else { - mSetOrientationRequestCounter = 0; - } - mTimeMsLastSetOrientationRequest = currTimeMs; - } - } -} diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java new file mode 100644 index 000000000000..446a75caeb82 --- /dev/null +++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2024 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_ANY_ORIENTATION; +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_UNDEFINED_ORIENTATION_TO_NOSENSOR; +import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT; +import static android.content.pm.ActivityInfo.screenOrientationToString; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; +import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION; + +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.AppCompatOverrides.asLazy; + +import android.annotation.NonNull; +import android.content.pm.ActivityInfo; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wm.utils.OptPropFactory; + +import java.util.function.BooleanSupplier; +import java.util.function.LongSupplier; + +/** + * Encapsulates all the configurations and overrides about orientation used by + * {@link AppCompatOrientationPolicy}. + */ +class AppCompatOrientationOverrides { + + private static final String TAG = TAG_WITH_CLASS_NAME + ? "AppCompatOrientationOverrides" : TAG_ATM; + + @NonNull + private final ActivityRecord mActivityRecord; + + @NonNull + private final OptPropFactory.OptProp mIgnoreRequestedOrientationOptProp; + @NonNull + private final OptPropFactory.OptProp mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp; + + @NonNull + final OrientationOverridesState mOrientationOverridesState; + + AppCompatOrientationOverrides(@NonNull OptPropFactory optPropBuilder, + @NonNull LetterboxConfiguration letterboxConfiguration, + @NonNull ActivityRecord activityRecord) { + mActivityRecord = activityRecord; + mOrientationOverridesState = new OrientationOverridesState(mActivityRecord, + System::currentTimeMillis); + final BooleanSupplier isPolicyForIgnoringRequestedOrientationEnabled = asLazy( + letterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled); + mIgnoreRequestedOrientationOptProp = optPropBuilder.create( + PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION, + isPolicyForIgnoringRequestedOrientationEnabled); + mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp = optPropBuilder.create( + PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED, + isPolicyForIgnoringRequestedOrientationEnabled); + } + + /** + * Whether should ignore app requested orientation in response to an app + * calling {@link android.app.Activity#setRequestedOrientation}. + * + *

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: + *

    + *
  1. App sets default orientation to "unspecified" at runtime + *
  2. App requests to "portrait" after checking some condition (e.g. display rotation). + *
  3. (2) leads to fullscreen -> letterboxed bounds change and activity relaunch because + * app can't handle the corresponding config changes. + *
  4. Loop goes back to (1) + *
+ * + *

This treatment is enabled when the following conditions are met: + *

+ */ + boolean shouldIgnoreRequestedOrientation( + @ActivityInfo.ScreenOrientation int requestedOrientation) { + if (mIgnoreRequestedOrientationOptProp.shouldEnableWithOverrideAndProperty( + isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION))) { + if (mOrientationOverridesState.mIsRelaunchingAfterRequestedOrientationChanged) { + Slog.w(TAG, "Ignoring orientation update to " + + screenOrientationToString(requestedOrientation) + + " due to relaunching after setRequestedOrientation for " + + mActivityRecord); + return true; + } + if (isCameraCompatTreatmentActive()) { + Slog.w(TAG, "Ignoring orientation update to " + + screenOrientationToString(requestedOrientation) + + " due to camera compat treatment for " + mActivityRecord); + 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; + } + + /** + * Whether an app is calling {@link android.app.Activity#setRequestedOrientation} + * in a loop and orientation request should be ignored. + * + *

This should only be called once in response to + * {@link android.app.Activity#setRequestedOrientation}. See + * {@link #shouldIgnoreRequestedOrientation} for more details. + * + *

This treatment is enabled when the following conditions are met: + *

+ */ + boolean shouldIgnoreOrientationRequestLoop() { + final boolean loopDetectionEnabled = isCompatChangeEnabled( + OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); + if (!mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp + .shouldEnableWithOptInOverrideAndOptOutProperty(loopDetectionEnabled)) { + return false; + } + mOrientationOverridesState.updateOrientationRequestLoopState(); + + return mOrientationOverridesState.shouldIgnoreRequestInLoop() + && !mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio(); + } + + /** + * Sets whether an activity is relaunching after the app has called {@link + * android.app.Activity#setRequestedOrientation}. + */ + void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) { + mOrientationOverridesState + .mIsRelaunchingAfterRequestedOrientationChanged = isRelaunching; + } + + boolean getIsRelaunchingAfterRequestedOrientationChanged() { + return mOrientationOverridesState.mIsRelaunchingAfterRequestedOrientationChanged; + } + + @VisibleForTesting + int getSetOrientationRequestCounter() { + return mOrientationOverridesState.mSetOrientationRequestCounter; + } + + private boolean isCompatChangeEnabled(long overrideChangeId) { + return mActivityRecord.info.isChangeEnabled(overrideChangeId); + } + + /** + * @return {@code true} if the App Compat Camera Policy is active for the current activity. + */ + // TODO(b/346253439): Remove after defining dependency with Camera capabilities. + private boolean isCameraCompatTreatmentActive() { + DisplayContent displayContent = mActivityRecord.mDisplayContent; + if (displayContent == null) { + return false; + } + return displayContent.mDisplayRotationCompatPolicy != null + && displayContent.mDisplayRotationCompatPolicy + .isTreatmentEnabledForActivity(mActivityRecord); + } + + static class OrientationOverridesState { + // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR + final boolean mIsOverrideToNosensorOrientationEnabled; + // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT + final boolean mIsOverrideToPortraitOrientationEnabled; + // Corresponds to OVERRIDE_ANY_ORIENTATION + final boolean mIsOverrideAnyOrientationEnabled; + // Corresponds to OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE + final boolean mIsOverrideToReverseLandscapeOrientationEnabled; + + private boolean mIsRelaunchingAfterRequestedOrientationChanged; + + // 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; + // Minimum value of mSetOrientationRequestCounter before qualifying as orientation request + // loop + @VisibleForTesting + static final int MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP = 2; + // Updated when ActivityRecord#setRequestedOrientation is called + private long mTimeMsLastSetOrientationRequest = 0; + // Counter for ActivityRecord#setRequestedOrientation + private int mSetOrientationRequestCounter = 0; + @VisibleForTesting + LongSupplier mCurrentTimeMillisSupplier; + + OrientationOverridesState(@NonNull ActivityRecord activityRecord, + @NonNull LongSupplier currentTimeMillisSupplier) { + mCurrentTimeMillisSupplier = currentTimeMillisSupplier; + mIsOverrideToNosensorOrientationEnabled = + activityRecord.info.isChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR); + mIsOverrideToPortraitOrientationEnabled = + activityRecord.info.isChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT); + mIsOverrideAnyOrientationEnabled = + activityRecord.info.isChangeEnabled(OVERRIDE_ANY_ORIENTATION); + mIsOverrideToReverseLandscapeOrientationEnabled = activityRecord.info + .isChangeEnabled(OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE); + } + + /** + * @return {@code true} if we should start ignoring orientation in a orientation request + * loop. + */ + boolean shouldIgnoreRequestInLoop() { + return mSetOrientationRequestCounter >= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; + } + + /** + * Updates the orientation request counter using a specific timeout. + */ + void updateOrientationRequestLoopState() { + final long currTimeMs = mCurrentTimeMillisSupplier.getAsLong(); + final long elapsedTime = currTimeMs - mTimeMsLastSetOrientationRequest; + if (elapsedTime < SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS) { + mSetOrientationRequestCounter++; + } else { + mSetOrientationRequestCounter = 0; + } + mTimeMsLastSetOrientationRequest = currTimeMs; + } + } +} diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java index c505ff9de825..8e9a9e99d4e1 100644 --- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java +++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java @@ -45,7 +45,7 @@ class AppCompatOrientationPolicy { private final ActivityRecord mActivityRecord; @NonNull - private final AppCompatCapability mAppCompatCapability; + private final AppCompatOverrides mAppCompatOverrides; @NonNull private final BooleanSupplier mShouldApplyUserFullscreenOverride; @@ -56,12 +56,12 @@ class AppCompatOrientationPolicy { // TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with spectRatio component AppCompatOrientationPolicy(@NonNull ActivityRecord activityRecord, - @NonNull AppCompatCapability appCompatCapability, + @NonNull AppCompatOverrides appCompatOverrides, @NonNull BooleanSupplier shouldApplyUserFullscreenOverride, @NonNull BooleanSupplier shouldApplyUserMinAspectRatioOverride, @NonNull BooleanSupplier isSystemOverrideToFullscreenEnabled) { mActivityRecord = activityRecord; - mAppCompatCapability = appCompatCapability; + mAppCompatOverrides = appCompatOverrides; mShouldApplyUserFullscreenOverride = shouldApplyUserFullscreenOverride; mShouldApplyUserMinAspectRatioOverride = shouldApplyUserMinAspectRatioOverride; mIsSystemOverrideToFullscreenEnabled = isSystemOverrideToFullscreenEnabled; @@ -99,11 +99,11 @@ class AppCompatOrientationPolicy { return SCREEN_ORIENTATION_PORTRAIT; } - if (mAppCompatCapability.isAllowOrientationOverrideOptOut()) { + if (mAppCompatOverrides.isAllowOrientationOverrideOptOut()) { return candidate; } - if (displayContent != null && mAppCompatCapability + if (displayContent != null && mAppCompatOverrides .isOverrideOrientationOnlyForCameraEnabled() && (displayContent.mDisplayRotationCompatPolicy == null || !displayContent.mDisplayRotationCompatPolicy @@ -127,9 +127,9 @@ class AppCompatOrientationPolicy { return SCREEN_ORIENTATION_USER; } - final AppCompatOrientationCapability.OrientationCapabilityState capabilityState = - mAppCompatCapability.getAppCompatOrientationCapability() - .mOrientationCapabilityState; + final AppCompatOrientationOverrides.OrientationOverridesState capabilityState = + mAppCompatOverrides.getAppCompatOrientationOverrides() + .mOrientationOverridesState; if (capabilityState.mIsOverrideToReverseLandscapeOrientationEnabled && (isFixedOrientationLandscape(candidate) diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java new file mode 100644 index 000000000000..9e739653c738 --- /dev/null +++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2024 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.FORCE_NON_RESIZE_APP; +import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP; +import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER; +import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION; +import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT; +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_MIN_ASPECT_RATIO; +import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA; +import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; +import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION; +import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION; +import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH; +import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE; +import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; + +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.annotation.NonNull; +import android.content.pm.PackageManager; + +import com.android.server.wm.utils.OptPropFactory; +import com.android.window.flags.Flags; + +import java.util.function.BooleanSupplier; + +/** + * Encapsulate logic related to operations guarded by an app override. + */ +public class AppCompatOverrides { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatOverrides" : TAG_ATM; + + @NonNull + private final LetterboxConfiguration mLetterboxConfiguration; + + @NonNull + private final ActivityRecord mActivityRecord; + + // Corresponds to OVERRIDE_ANY_ORIENTATION_TO_USER + private final boolean mIsSystemOverrideToFullscreenEnabled; + // Corresponds to OVERRIDE_RESPECT_REQUESTED_ORIENTATION + private final boolean mIsOverrideRespectRequestedOrientationEnabled; + // Corresponds to OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA + private final boolean mIsOverrideOrientationOnlyForCameraEnabled; + + @NonNull + private final OptPropFactory.OptProp mFakeFocusOptProp; + @NonNull + private final OptPropFactory.OptProp mCameraCompatAllowForceRotationOptProp; + @NonNull + private final OptPropFactory.OptProp mCameraCompatAllowRefreshOptProp; + @NonNull + private final OptPropFactory.OptProp mCameraCompatEnableRefreshViaPauseOptProp; + @NonNull + private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp; + @NonNull + private final OptPropFactory.OptProp mAllowDisplayOrientationOverrideOptProp; + @NonNull + private final OptPropFactory.OptProp mAllowMinAspectRatioOverrideOptProp; + @NonNull + private final OptPropFactory.OptProp mAllowForceResizeOverrideOptProp; + @NonNull + private final OptPropFactory.OptProp mAllowUserAspectRatioOverrideOptProp; + @NonNull + private final OptPropFactory.OptProp mAllowUserAspectRatioFullscreenOverrideOptProp; + + private final AppCompatOrientationOverrides mAppCompatOrientationOverrides; + + AppCompatOverrides(@NonNull WindowManagerService wmService, + @NonNull ActivityRecord activityRecord, + @NonNull LetterboxConfiguration letterboxConfiguration) { + mLetterboxConfiguration = letterboxConfiguration; + mActivityRecord = activityRecord; + final PackageManager packageManager = wmService.mContext.getPackageManager(); + + final OptPropFactory optPropBuilder = new OptPropFactory(packageManager, + activityRecord.packageName); + + mAppCompatOrientationOverrides = + new AppCompatOrientationOverrides(optPropBuilder, mLetterboxConfiguration, + mActivityRecord); + + mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, + mLetterboxConfiguration::isCompatFakeFocusEnabled); + + final BooleanSupplier isCameraCompatTreatmentEnabled = asLazy( + mLetterboxConfiguration::isCameraCompatTreatmentEnabled); + mCameraCompatAllowForceRotationOptProp = optPropBuilder.create( + PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, + isCameraCompatTreatmentEnabled); + mCameraCompatAllowRefreshOptProp = optPropBuilder.create( + PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, + isCameraCompatTreatmentEnabled); + mCameraCompatEnableRefreshViaPauseOptProp = optPropBuilder.create( + PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE, + isCameraCompatTreatmentEnabled); + + mAllowOrientationOverrideOptProp = optPropBuilder.create( + PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE); + + mAllowDisplayOrientationOverrideOptProp = optPropBuilder.create( + PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE, + () -> mActivityRecord.mDisplayContent != null + && mActivityRecord.getTask() != null + && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest() + && !mActivityRecord.getTask().inMultiWindowMode() + && mActivityRecord.mDisplayContent.getNaturalOrientation() + == ORIENTATION_LANDSCAPE + ); + + mAllowMinAspectRatioOverrideOptProp = optPropBuilder.create( + PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE); + mAllowForceResizeOverrideOptProp = optPropBuilder.create( + PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES); + mAllowUserAspectRatioOverrideOptProp = optPropBuilder.create( + PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, + mLetterboxConfiguration::isUserAppAspectRatioSettingsEnabled); + mAllowUserAspectRatioFullscreenOverrideOptProp = optPropBuilder.create( + PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, + mLetterboxConfiguration::isUserAppAspectRatioFullscreenEnabled); + + mIsSystemOverrideToFullscreenEnabled = + isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER); + mIsOverrideRespectRequestedOrientationEnabled = + isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION); + mIsOverrideOrientationOnlyForCameraEnabled = + isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA); + } + + /** + * @return {@code true} if the App Compat Camera Policy is active for the current activity. + */ + boolean isCameraCompatTreatmentActive() { + final DisplayContent displayContent = mActivityRecord.mDisplayContent; + if (displayContent == null) { + return false; + } + return displayContent.mDisplayRotationCompatPolicy != null + && displayContent.mDisplayRotationCompatPolicy + .isTreatmentEnabledForActivity(mActivityRecord); + } + + @NonNull + AppCompatOrientationOverrides getAppCompatOrientationOverrides() { + return mAppCompatOrientationOverrides; + } + + /** + * 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 + * guaranteed by default in multi-window modes. + * + *

This treatment is enabled when the following conditions are met: + *

+ */ + boolean shouldSendFakeFocus() { + return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty( + isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS)); + } + + /** + * Whether activity is eligible for camera compat force rotation treatment. See {@link + * DisplayRotationCompatPolicy} for context. + * + *

This treatment is enabled when the following conditions are met: + *

+ */ + boolean shouldForceRotateForCameraCompat() { + return mCameraCompatAllowForceRotationOptProp.shouldEnableWithOptOutOverrideAndProperty( + isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION)); + } + + /** + * Whether activity is eligible for activity "refresh" after camera compat force rotation + * treatment. See {@link DisplayRotationCompatPolicy} for context. + * + *

This treatment is enabled when the following conditions are met: + *

+ */ + boolean shouldRefreshActivityForCameraCompat() { + return mCameraCompatAllowRefreshOptProp.shouldEnableWithOptOutOverrideAndProperty( + isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH)); + } + + /** + * Whether activity should be "refreshed" after the camera compat force rotation treatment + * using the "resumed -> paused -> resumed" cycle rather than the "resumed -> ... -> stopped + * -> ... -> resumed" cycle. See {@link DisplayRotationCompatPolicy} for context. + * + *

This treatment is enabled when the following conditions are met: + *

+ */ + boolean shouldRefreshActivityViaPauseForCameraCompat() { + return mCameraCompatEnableRefreshViaPauseOptProp.shouldEnableWithOverrideAndProperty( + isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE)); + } + + boolean isSystemOverrideToFullscreenEnabled(int userAspectRatio) { + return mIsSystemOverrideToFullscreenEnabled + && !mAllowOrientationOverrideOptProp.isFalse() + && (userAspectRatio == USER_MIN_ASPECT_RATIO_UNSET + || userAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN); + } + + boolean isAllowOrientationOverrideOptOut() { + return mAllowOrientationOverrideOptProp.isFalse(); + } + + /** + * Whether we should apply the min aspect ratio per-app override. When this override is applied + * the min aspect ratio given in the app's manifest will be overridden to the largest enabled + * aspect ratio treatment unless the app's manifest value is higher. The treatment will also + * apply if no value is provided in the manifest. + * + *

This method returns {@code true} when the following conditions are met: + *

+ */ + boolean shouldOverrideMinAspectRatio() { + return mAllowMinAspectRatioOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty( + isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO)); + } + + boolean isOverrideRespectRequestedOrientationEnabled() { + return mIsOverrideRespectRequestedOrientationEnabled; + } + + boolean isOverrideOrientationOnlyForCameraEnabled() { + return mIsOverrideOrientationOnlyForCameraEnabled; + } + + /** + * Whether activity is eligible for camera compatibility free-form treatment. + * + *

The treatment is applied to a fixed-orientation camera activity in free-form windowing + * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and + * provides changes to the camera and display orientation signals to match those expected on a + * portrait device in that orientation (for example, on a standard phone). + * + *

The treatment is enabled when the following conditions are met: + *

+ */ + boolean shouldApplyFreeformTreatmentForCameraCompat() { + return Flags.cameraCompatForFreeform() && !isCompatChangeEnabled( + OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT); + } + + + /** + * Whether we should apply the min aspect ratio per-app override only when an app is connected + * to the camera. + * When this override is applied the min aspect ratio given in the app's manifest will be + * overridden to the largest enabled aspect ratio treatment unless the app's manifest value + * is higher. The treatment will also apply if no value is provided in the manifest. + * + *

This method returns {@code true} when the following conditions are met: + *

+ */ + boolean shouldOverrideMinAspectRatioForCamera() { + return mActivityRecord.isCameraActive() + && mAllowMinAspectRatioOverrideOptProp + .shouldEnableWithOptInOverrideAndOptOutProperty( + isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA)); + } + + /** + * Whether should fix display orientation to landscape natural orientation when a task is + * fullscreen and the display is ignoring orientation requests. + * + *

This treatment is enabled when the following conditions are met: + *

+ */ + boolean shouldUseDisplayLandscapeNaturalOrientation() { + return mAllowDisplayOrientationOverrideOptProp + .shouldEnableWithOptInOverrideAndOptOutProperty( + isCompatChangeEnabled(OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION)); + } + + /** + * Whether we should apply the force resize per-app override. When this override is applied it + * forces the packages it is applied to to be resizable. It won't change whether the app can be + * put into multi-windowing mode, but allow the app to resize without going into size-compat + * mode when the window container resizes, such as display size change or screen rotation. + * + *

This method returns {@code true} when the following conditions are met: + *

+ */ + boolean shouldOverrideForceResizeApp() { + return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty( + isCompatChangeEnabled(FORCE_RESIZE_APP)); + } + + /** + * Whether we should enable users to resize the current app. + */ + boolean shouldEnableUserAspectRatioSettings() { + // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has + // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null, + // the current app doesn't opt-out so the first part of the predicate is true. + return !mAllowUserAspectRatioOverrideOptProp.isFalse() + && mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled() + && mActivityRecord.mDisplayContent != null + && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest(); + } + + boolean isUserFullscreenOverrideEnabled() { + if (mAllowUserAspectRatioOverrideOptProp.isFalse() + || mAllowUserAspectRatioFullscreenOverrideOptProp.isFalse() + || !mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()) { + return false; + } + return true; + } + + /** + * Whether we should apply the force non resize per-app override. When this override is applied + * it forces the packages it is applied to to be non-resizable. + * + *

This method returns {@code true} when the following conditions are met: + *

+ */ + boolean shouldOverrideForceNonResizeApp() { + return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty( + isCompatChangeEnabled(FORCE_NON_RESIZE_APP)); + } + + private boolean isCompatChangeEnabled(long overrideChangeId) { + return mActivityRecord.info.isChangeEnabled(overrideChangeId); + } + + @NonNull + static BooleanSupplier asLazy(@NonNull BooleanSupplier supplier) { + return new BooleanSupplier() { + private boolean mRead; + private boolean mValue; + + @Override + public boolean getAsBoolean() { + if (!mRead) { + mRead = true; + mValue = supplier.getAsBoolean(); + } + return mValue; + } + }; + } +} diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index e0d2035118ad..b17364bb14b5 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -153,7 +153,7 @@ final class LetterboxUiController { @VisibleForTesting int getSetOrientationRequestCounter() { - return getAppCompatCapability().getAppCompatOrientationCapability() + return getAppCompatOverrides().getAppCompatOrientationOverrides() .getSetOrientationRequestCounter(); } @@ -170,7 +170,7 @@ final class LetterboxUiController { * */ boolean shouldSendFakeFocus() { - return getAppCompatCapability().shouldSendFakeFocus(); + return getAppCompatOverrides().shouldSendFakeFocus(); } /** @@ -186,7 +186,7 @@ final class LetterboxUiController { * */ boolean shouldOverrideMinAspectRatio() { - return getAppCompatCapability().shouldOverrideMinAspectRatio(); + return getAppCompatOverrides().shouldOverrideMinAspectRatio(); } /** @@ -203,7 +203,7 @@ final class LetterboxUiController { * */ boolean shouldOverrideMinAspectRatioForCamera() { - return getAppCompatCapability().shouldOverrideMinAspectRatioForCamera(); + return getAppCompatOverrides().shouldOverrideMinAspectRatioForCamera(); } /** @@ -219,7 +219,7 @@ final class LetterboxUiController { * */ boolean shouldOverrideForceResizeApp() { - return getAppCompatCapability().shouldOverrideForceResizeApp(); + return getAppCompatOverrides().shouldOverrideForceResizeApp(); } /** @@ -233,7 +233,7 @@ final class LetterboxUiController { * */ boolean shouldOverrideForceNonResizeApp() { - return getAppCompatCapability().shouldOverrideForceNonResizeApp(); + return getAppCompatOverrides().shouldOverrideForceNonResizeApp(); } /** @@ -241,7 +241,7 @@ final class LetterboxUiController { * android.app.Activity#setRequestedOrientation}. */ void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) { - getAppCompatCapability().getAppCompatOrientationCapability() + getAppCompatOverrides().getAppCompatOrientationOverrides() .setRelaunchingAfterRequestedOrientationChanged(isRelaunching); } @@ -257,7 +257,7 @@ final class LetterboxUiController { } boolean isOverrideRespectRequestedOrientationEnabled() { - return getAppCompatCapability().isOverrideRespectRequestedOrientationEnabled(); + return getAppCompatOverrides().isOverrideRespectRequestedOrientationEnabled(); } /** @@ -274,11 +274,11 @@ final class LetterboxUiController { * */ boolean shouldUseDisplayLandscapeNaturalOrientation() { - return getAppCompatCapability().shouldUseDisplayLandscapeNaturalOrientation(); + return getAppCompatOverrides().shouldUseDisplayLandscapeNaturalOrientation(); } boolean isOverrideOrientationOnlyForCameraEnabled() { - return getAppCompatCapability().isOverrideOrientationOnlyForCameraEnabled(); + return getAppCompatOverrides().isOverrideOrientationOnlyForCameraEnabled(); } /** @@ -293,7 +293,7 @@ final class LetterboxUiController { * */ boolean shouldRefreshActivityForCameraCompat() { - return getAppCompatCapability().shouldRefreshActivityForCameraCompat(); + return getAppCompatOverrides().shouldRefreshActivityForCameraCompat(); } /** @@ -311,7 +311,7 @@ final class LetterboxUiController { * */ boolean shouldRefreshActivityViaPauseForCameraCompat() { - return getAppCompatCapability().shouldRefreshActivityViaPauseForCameraCompat(); + return getAppCompatOverrides().shouldRefreshActivityViaPauseForCameraCompat(); } /** @@ -326,7 +326,7 @@ final class LetterboxUiController { * */ boolean shouldForceRotateForCameraCompat() { - return getAppCompatCapability().shouldForceRotateForCameraCompat(); + return getAppCompatOverrides().shouldForceRotateForCameraCompat(); } /** @@ -344,7 +344,7 @@ final class LetterboxUiController { * */ boolean shouldApplyFreeformTreatmentForCameraCompat() { - return getAppCompatCapability().shouldApplyFreeformTreatmentForCameraCompat(); + return getAppCompatOverrides().shouldApplyFreeformTreatmentForCameraCompat(); } @FreeformCameraCompatMode @@ -609,7 +609,7 @@ final class LetterboxUiController { // Don't resize to split screen size when in book mode if letterbox position is centered return (isBookMode && isNotCenteredHorizontally || isTabletopMode && isLandscape) || isCameraCompatSplitScreenAspectRatioAllowed() - && getAppCompatCapability().isCameraCompatTreatmentActive(); + && getAppCompatOverrides().isCameraCompatTreatmentActive(); } private float getDefaultMinAspectRatioForUnresizableApps() { @@ -711,7 +711,7 @@ final class LetterboxUiController { * Whether we should enable users to resize the current app. */ boolean shouldEnableUserAspectRatioSettings() { - return getAppCompatCapability().shouldEnableUserAspectRatioSettings(); + return getAppCompatOverrides().shouldEnableUserAspectRatioSettings(); } /** @@ -741,11 +741,11 @@ final class LetterboxUiController { } boolean isUserFullscreenOverrideEnabled() { - return getAppCompatCapability().isUserFullscreenOverrideEnabled(); + return getAppCompatOverrides().isUserFullscreenOverrideEnabled(); } boolean isSystemOverrideToFullscreenEnabled() { - return getAppCompatCapability().isSystemOverrideToFullscreenEnabled(mUserAspectRatio); + return getAppCompatOverrides().isSystemOverrideToFullscreenEnabled(mUserAspectRatio); } boolean hasFullscreenOverride() { @@ -941,8 +941,8 @@ final class LetterboxUiController { } // TODO(b/346264992): Remove after AppCompatController refactoring - private AppCompatCapability getAppCompatCapability() { - return mActivityRecord.mAppCompatController.getAppCompatCapability(); + private AppCompatOverrides getAppCompatOverrides() { + return mActivityRecord.mAppCompatController.getAppCompatOverrides(); } /** @@ -983,7 +983,7 @@ final class LetterboxUiController { @VisibleForTesting boolean shouldShowLetterboxUi(WindowState mainWindow) { - if (getAppCompatCapability().getAppCompatOrientationCapability() + if (getAppCompatOverrides().getAppCompatOrientationOverrides() .getIsRelaunchingAfterRequestedOrientationChanged()) { return mLastShouldShowLetterboxUi; } @@ -1144,7 +1144,7 @@ final class LetterboxUiController { } boolean getIsRelaunchingAfterRequestedOrientationChanged() { - return getAppCompatCapability().getAppCompatOrientationCapability() + return getAppCompatOverrides().getAppCompatOrientationOverrides() .getIsRelaunchingAfterRequestedOrientationChanged(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationCapabilityTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationCapabilityTest.java deleted file mode 100644 index 55e66a1b3556..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationCapabilityTest.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2024 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_CAMERA_COMPAT_DISABLE_REFRESH; -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.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; -import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.server.wm.AppCompatOrientationCapability.OrientationCapabilityState.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; -import static com.android.server.wm.AppCompatOrientationCapability.OrientationCapabilityState.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.compat.testing.PlatformCompatChangeRule; -import android.content.res.Configuration; -import android.platform.test.annotations.Presubmit; - -import androidx.annotation.NonNull; - -import com.android.server.wm.utils.CurrentTimeMillisSupplierFake; - -import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.junit.runner.RunWith; - -import java.util.function.Consumer; -import java.util.function.IntConsumer; - -/** - * Test class for {@link AppCompatOrientationCapability}. - *

- * Build/Install/Run: - * atest WmTests:AppCompatOrientationCapabilityTest - */ -@Presubmit -@RunWith(WindowTestRunner.class) -public class AppCompatOrientationCapabilityTest extends WindowTestsBase { - - @Rule - public TestRule compatChangeRule = new PlatformCompatChangeRule(); - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION}) - public void testShouldIgnoreRequestedOrientation_activityRelaunching_returnsTrue() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.activity().createActivityWithComponent(); - robot.prepareRelaunchingAfterRequestedOrientationChanged(true); - - robot.checkShouldIgnoreRequestedOrientation(/* expected */ true, - /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); - }); - } - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION}) - public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() { - runTestScenario((robot) -> { - robot.applyOnConf((c) -> { - c.enableCameraCompatTreatment(true); - c.enableCameraCompatTreatmentAtBuildTime(true); - c.enablePolicyForIgnoringRequestedOrientation(true); - }); - robot.applyOnActivity((a) -> { - a.createActivityWithComponentInNewTask(); - a.enableTreatmentForTopActivity(true); - }); - robot.prepareRelaunchingAfterRequestedOrientationChanged(false); - - robot.checkShouldIgnoreRequestedOrientation(/* expected */ true, - /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); - }); - } - - @Test - public void testShouldIgnoreRequestedOrientation_overrideDisabled_returnsFalse() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - - robot.activity().createActivityWithComponent(); - robot.prepareRelaunchingAfterRequestedOrientationChanged(true); - - robot.checkShouldIgnoreRequestedOrientation(/* expected */ false, - /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); - }); - } - - @Test - public void testShouldIgnoreRequestedOrientation_propertyIsTrue_returnsTrue() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.prop().enable(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION); - - robot.activity().createActivityWithComponent(); - robot.prepareRelaunchingAfterRequestedOrientationChanged(true); - - robot.checkShouldIgnoreRequestedOrientation(/* expected */ true, - /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); - }); - } - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION}) - public void testShouldIgnoreRequestedOrientation_propertyIsFalseAndOverride_returnsFalse() - throws Exception { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.prop().disable(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION); - - robot.activity().createActivityWithComponent(); - robot.prepareRelaunchingAfterRequestedOrientationChanged(true); - - robot.checkShouldIgnoreRequestedOrientation(/* expected */ false, - /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); - }); - } - - @Test - public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.applyOnActivity((a) -> { - a.createActivityWithComponent(); - a.setLetterboxedForFixedOrientationAndAspectRatio(false); - }); - robot.checkRequestLoopExtended((i) -> { - robot.checkShouldNotIgnoreOrientationLoop(); - robot.checkExpectedLoopCount(/* expectedCount */ 0); - }); - }); - } - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) - public void testShouldIgnoreOrientationRequestLoop_propertyIsFalseAndOverride_returnsFalse() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.prop().disable( - PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); - robot.applyOnActivity((a) -> { - a.createActivityWithComponent(); - a.setLetterboxedForFixedOrientationAndAspectRatio(false); - }); - robot.checkRequestLoopExtended((i) -> { - robot.checkShouldNotIgnoreOrientationLoop(); - robot.checkExpectedLoopCount(/* expectedCount */ 0); - }); - }); - } - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) - public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.applyOnActivity((a) -> { - a.createActivityWithComponent(); - a.setLetterboxedForFixedOrientationAndAspectRatio(true); - }); - robot.checkRequestLoopExtended((i) -> { - robot.checkShouldNotIgnoreOrientationLoop(); - robot.checkExpectedLoopCount(/* expectedCount */ i); - }); - }); - } - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) - public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.applyOnActivity((a) -> { - a.createActivityWithComponent(); - a.setLetterboxedForFixedOrientationAndAspectRatio(false); - }); - - robot.checkShouldNotIgnoreOrientationLoop(); - robot.checkExpectedLoopCount(/* expectedCount */ 0); - }); - } - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) - public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.applyOnActivity((a) -> { - a.createActivityWithComponent(); - a.setLetterboxedForFixedOrientationAndAspectRatio(false); - }); - - robot.prepareMockedTime(); - robot.checkRequestLoopExtended((i) -> { - robot.checkShouldNotIgnoreOrientationLoop(); - robot.checkExpectedLoopCount(/* expectedCount */ 0); - robot.delay(); - }); - }); - } - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) - public void testShouldIgnoreOrientationRequestLoop_returnsTrue() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.applyOnActivity((a) -> { - a.createActivityWithComponent(); - a.setLetterboxedForFixedOrientationAndAspectRatio(false); - }); - - robot.checkRequestLoop((i) -> { - robot.checkShouldNotIgnoreOrientationLoop(); - robot.checkExpectedLoopCount(/* expectedCount */ i); - }); - robot.checkShouldIgnoreOrientationLoop(); - robot.checkExpectedLoopCount(/* expectedCount */ MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP); - }); - } - - @Test - @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH}) - public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() { - runTestScenario((robot) -> { - robot.conf().enablePolicyForIgnoringRequestedOrientation(true); - robot.applyOnActivity((a) -> { - a.createActivityWithComponent(); - a.setLetterboxedForFixedOrientationAndAspectRatio(false); - }); - - robot.checkShouldIgnoreRequestedOrientation(/* expected */ false, - /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); - }); - } - - /** - * Runs a test scenario providing a Robot. - */ - void runTestScenario(@NonNull Consumer consumer) { - spyOn(mWm.mLetterboxConfiguration); - final OrientationCapabilityRobotTest robot = - new OrientationCapabilityRobotTest(mWm, mAtm, mSupervisor); - consumer.accept(robot); - } - - private static class OrientationCapabilityRobotTest extends AppCompatRobotBase { - - @NonNull - private final CurrentTimeMillisSupplierFake mTestCurrentTimeMillisSupplier; - - OrientationCapabilityRobotTest(@NonNull WindowManagerService wm, - @NonNull ActivityTaskManagerService atm, - @NonNull ActivityTaskSupervisor supervisor) { - super(wm, atm, supervisor); - mTestCurrentTimeMillisSupplier = new CurrentTimeMillisSupplierFake(); - } - - void prepareRelaunchingAfterRequestedOrientationChanged(boolean enabled) { - getTopOrientationCapability().setRelaunchingAfterRequestedOrientationChanged(enabled); - } - - // Useful to reduce timeout during tests - void prepareMockedTime() { - getTopOrientationCapability().mOrientationCapabilityState.mCurrentTimeMillisSupplier = - mTestCurrentTimeMillisSupplier; - } - - void delay() { - mTestCurrentTimeMillisSupplier.delay(SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS); - } - - void checkShouldIgnoreRequestedOrientation(boolean expected, - @Configuration.Orientation int requestedOrientation) { - assertEquals(expected, getTopOrientationCapability() - .shouldIgnoreRequestedOrientation(requestedOrientation)); - } - - void checkExpectedLoopCount(int expectedCount) { - assertEquals(expectedCount, getTopOrientationCapability() - .getSetOrientationRequestCounter()); - } - - void checkShouldNotIgnoreOrientationLoop() { - assertFalse(getTopOrientationCapability().shouldIgnoreOrientationRequestLoop()); - } - - void checkShouldIgnoreOrientationLoop() { - assertTrue(getTopOrientationCapability().shouldIgnoreOrientationRequestLoop()); - } - - void checkRequestLoop(IntConsumer consumer) { - for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { - consumer.accept(i); - } - } - - void checkRequestLoopExtended(IntConsumer consumer) { - for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { - consumer.accept(i); - } - } - - private AppCompatOrientationCapability getTopOrientationCapability() { - return activity().top().mAppCompatController.getAppCompatCapability() - .getAppCompatOrientationCapability(); - } - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java new file mode 100644 index 000000000000..1720b64f558b --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2024 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_CAMERA_COMPAT_DISABLE_REFRESH; +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.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED; +import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; +import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.compat.testing.PlatformCompatChangeRule; +import android.content.res.Configuration; +import android.platform.test.annotations.Presubmit; + +import androidx.annotation.NonNull; + +import com.android.server.wm.utils.CurrentTimeMillisSupplierFake; + +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +import java.util.function.Consumer; +import java.util.function.IntConsumer; + +/** + * Test class for {@link AppCompatOrientationOverrides}. + *

+ * Build/Install/Run: + * atest WmTests:AppCompatOrientationOverridesTest + */ +@Presubmit +@RunWith(WindowTestRunner.class) +public class AppCompatOrientationOverridesTest extends WindowTestsBase { + + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION}) + public void testShouldIgnoreRequestedOrientation_activityRelaunching_returnsTrue() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.activity().createActivityWithComponent(); + robot.prepareRelaunchingAfterRequestedOrientationChanged(true); + + robot.checkShouldIgnoreRequestedOrientation(/* expected */ true, + /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION}) + public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() { + runTestScenario((robot) -> { + robot.applyOnConf((c) -> { + c.enableCameraCompatTreatment(true); + c.enableCameraCompatTreatmentAtBuildTime(true); + c.enablePolicyForIgnoringRequestedOrientation(true); + }); + robot.applyOnActivity((a) -> { + a.createActivityWithComponentInNewTask(); + a.enableTreatmentForTopActivity(true); + }); + robot.prepareRelaunchingAfterRequestedOrientationChanged(false); + + robot.checkShouldIgnoreRequestedOrientation(/* expected */ true, + /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); + }); + } + + @Test + public void testShouldIgnoreRequestedOrientation_overrideDisabled_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + + robot.activity().createActivityWithComponent(); + robot.prepareRelaunchingAfterRequestedOrientationChanged(true); + + robot.checkShouldIgnoreRequestedOrientation(/* expected */ false, + /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); + }); + } + + @Test + public void testShouldIgnoreRequestedOrientation_propertyIsTrue_returnsTrue() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.prop().enable(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION); + + robot.activity().createActivityWithComponent(); + robot.prepareRelaunchingAfterRequestedOrientationChanged(true); + + robot.checkShouldIgnoreRequestedOrientation(/* expected */ true, + /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION}) + public void testShouldIgnoreRequestedOrientation_propertyIsFalseAndOverride_returnsFalse() + throws Exception { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.prop().disable(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION); + + robot.activity().createActivityWithComponent(); + robot.prepareRelaunchingAfterRequestedOrientationChanged(true); + + robot.checkShouldIgnoreRequestedOrientation(/* expected */ false, + /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); + }); + } + + @Test + public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setLetterboxedForFixedOrientationAndAspectRatio(false); + }); + robot.checkRequestLoopExtended((i) -> { + robot.checkShouldNotIgnoreOrientationLoop(); + robot.checkExpectedLoopCount(/* expectedCount */ 0); + }); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) + public void testShouldIgnoreOrientationRequestLoop_propertyIsFalseAndOverride_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.prop().disable( + PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setLetterboxedForFixedOrientationAndAspectRatio(false); + }); + robot.checkRequestLoopExtended((i) -> { + robot.checkShouldNotIgnoreOrientationLoop(); + robot.checkExpectedLoopCount(/* expectedCount */ 0); + }); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) + public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setLetterboxedForFixedOrientationAndAspectRatio(true); + }); + robot.checkRequestLoopExtended((i) -> { + robot.checkShouldNotIgnoreOrientationLoop(); + robot.checkExpectedLoopCount(/* expectedCount */ i); + }); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) + public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setLetterboxedForFixedOrientationAndAspectRatio(false); + }); + + robot.checkShouldNotIgnoreOrientationLoop(); + robot.checkExpectedLoopCount(/* expectedCount */ 0); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) + public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setLetterboxedForFixedOrientationAndAspectRatio(false); + }); + + robot.prepareMockedTime(); + robot.checkRequestLoopExtended((i) -> { + robot.checkShouldNotIgnoreOrientationLoop(); + robot.checkExpectedLoopCount(/* expectedCount */ 0); + robot.delay(); + }); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED}) + public void testShouldIgnoreOrientationRequestLoop_returnsTrue() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setLetterboxedForFixedOrientationAndAspectRatio(false); + }); + + robot.checkRequestLoop((i) -> { + robot.checkShouldNotIgnoreOrientationLoop(); + robot.checkExpectedLoopCount(/* expectedCount */ i); + }); + robot.checkShouldIgnoreOrientationLoop(); + robot.checkExpectedLoopCount(/* expectedCount */ MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH}) + public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enablePolicyForIgnoringRequestedOrientation(true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setLetterboxedForFixedOrientationAndAspectRatio(false); + }); + + robot.checkShouldIgnoreRequestedOrientation(/* expected */ false, + /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED); + }); + } + + /** + * Runs a test scenario providing a Robot. + */ + void runTestScenario(@NonNull Consumer consumer) { + spyOn(mWm.mLetterboxConfiguration); + final OrientationOverridesRobotTest robot = + new OrientationOverridesRobotTest(mWm, mAtm, mSupervisor); + consumer.accept(robot); + } + + private static class OrientationOverridesRobotTest extends AppCompatRobotBase { + + @NonNull + private final CurrentTimeMillisSupplierFake mTestCurrentTimeMillisSupplier; + + OrientationOverridesRobotTest(@NonNull WindowManagerService wm, + @NonNull ActivityTaskManagerService atm, + @NonNull ActivityTaskSupervisor supervisor) { + super(wm, atm, supervisor); + mTestCurrentTimeMillisSupplier = new CurrentTimeMillisSupplierFake(); + } + + void prepareRelaunchingAfterRequestedOrientationChanged(boolean enabled) { + getTopOrientationOverrides().setRelaunchingAfterRequestedOrientationChanged(enabled); + } + + // Useful to reduce timeout during tests + void prepareMockedTime() { + getTopOrientationOverrides().mOrientationOverridesState.mCurrentTimeMillisSupplier = + mTestCurrentTimeMillisSupplier; + } + + void delay() { + mTestCurrentTimeMillisSupplier.delay(SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS); + } + + void checkShouldIgnoreRequestedOrientation(boolean expected, + @Configuration.Orientation int requestedOrientation) { + assertEquals(expected, getTopOrientationOverrides() + .shouldIgnoreRequestedOrientation(requestedOrientation)); + } + + void checkExpectedLoopCount(int expectedCount) { + assertEquals(expectedCount, getTopOrientationOverrides() + .getSetOrientationRequestCounter()); + } + + void checkShouldNotIgnoreOrientationLoop() { + assertFalse(getTopOrientationOverrides().shouldIgnoreOrientationRequestLoop()); + } + + void checkShouldIgnoreOrientationLoop() { + assertTrue(getTopOrientationOverrides().shouldIgnoreOrientationRequestLoop()); + } + + void checkRequestLoop(IntConsumer consumer) { + for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { + consumer.accept(i); + } + } + + void checkRequestLoopExtended(IntConsumer consumer) { + for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) { + consumer.accept(i); + } + } + + private AppCompatOrientationOverrides getTopOrientationOverrides() { + return activity().top().mAppCompatController.getAppCompatOverrides() + .getAppCompatOrientationOverrides(); + } + } +} -- cgit v1.2.3-59-g8ed1b